Quicksort sorting issues - c

This might not be a conventional of doing quicksort.my first try at it.the numbers are not sorted in the way they should be.I have tried to sort a random list of numbers.However i am unable to identify the logical errors even after a strict checking.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int n;
int *expivot;
int *arr;
void quicksort();
void display();
int check();
main()
{
int i;
printf("to continue press 'a' always\n");
while(getch()=='a')
{
printf("Enter the length of list\n");
scanf("%d",&n);
time_t start,end;
double t;
start=clock();
arr=(int *)calloc(n,sizeof(int));
expivot=(int *)calloc(n,sizeof(int));
srand(time(NULL));
for(i=0;i<n;i++)
arr[i]=rand()%RAND_MAX + 1;
printf("\nelements inputted are:");
display();
quicksort();
end=clock();
t=(double)(end-start)/CLOCKS_PER_SEC;
printf("\n\nelements sorted are:");
display();
printf("\ntime take is %.15lf",t);
free(arr);
free(expivot);
}
}
void quicksort()
{
int low,high,temp;
int pivot=rand()%n;//generate random pivot
int store=pivot;
/*location of pivot might change due to swapping,so using store to store pivot location so as to add this to expivot list after running quickort once*/
int flag=1;
if(expivot[pivot]==1) // checks if it's an already used pivot
flag=0;
if(flag==1) //if the pivot is unused
{
low=pivot;
high=pivot;
while(low>0 && expivot[low]==0)
low--;
if(expivot[low]==1)//i
low++;
/*decrements low to a location where a value has been set permanently and then increase by 1,if nothing is set then decrements low to zero*/
/*increments high to a location where a value has been set permanently and then decrease by 1,if nothing is set then increments high to last index*/
while(high<n-1 && expivot[high]==0)
high++;
if(expivot[high]==1)
high--;
while(low<high)
{
if(arr[low]>=arr[pivot] && arr[high]<=arr[pivot])//checks swap possibilty
{
if(low==pivot) //if pivot is to be swapped store new location of pivot
store=high;
else if(high==pivot)
store=low;
temp=arr[low];
arr[low]=arr[high];
arr[high]=temp;
low++;
high--;
}
else
{
if(arr[low]<arr[pivot])
low++;
else if(arr[high]>arr[pivot])
high--;
}
}
expivot[store]=1;
/*final location of pivot,stores info that this location has a permanent value now
and cannot be used as a pivot*/
}
if(check()==1)
quicksort();
}
int check() //checks if there are any unused pivots
{
int i;
for(i=0;i<n;i++)
{
if(expivot[i]==0)
return 1;
}
return 0;
}
void display()
{
int i;
for(i=0;i<n;i++)
printf("%d ",arr[i]);
}

Your method is:
Randomly select a pivot from whole array;
From the pivot, spread a range to both direction, this range will be partitioned by the pivot;
All pivot will be cached in another array (item 5);
The range, mentioned in item 2 above, should apread as large as it can do, but: 1) should not beyond the range of whole array; 2) should not contain another pivot, if it does, stop and shrink one unit;
Partition the range by the pivot it spread from, then cache this pivot;
If all unit in the array has been selected as pivot, sorting is done. If not, repeat as above, over and over again.
There are three problems in your code:
1- "checd()"function should be:
int check() //checks if there are any unused pivots
{
int flag = 0;
int i;
for(i=0;i<n;i++)
{
if(expivot[i]==0)
flag = 1;
}
return flag;
}
You should check all member, see if they ALL satisfy your condition, but not one of them satisfy your condition.
2- While shrink the range, make sure the pivot is between "high" and "low" (equal is well). Keep tracking the index and value of the pivot. The while loop should be:
//"store" is not needed, change it to "pivot", even after this code block.
while(low<high)
{
if(arr[low]>=arr[pivot] && arr[high]<=arr[pivot])//checks swap possibilty
{
if(low==pivot) //if pivot is to be swapped store new location of pivot
pivot=high;
else if(high==pivot)
pivot=low;
temp=arr[low];
arr[low]=arr[high];
arr[high]=temp;
/////////////////////
if (low < pivot)
low++;
if (high > pivot)
high--;
/////////////////////
}
else
{
if(arr[low]<arr[pivot])
low++;
else if(arr[high]>arr[pivot])
high--;
}
}
3- Finally, once you get memory from calloc or malloc, check if it's NULL.
==================================
Additionally, you should make sure all unit in the array can be selected, because the random number in computer is mostly pseudo random number. So, maybe for a certain length, a particular unit cannot be chosen forever.

Quicksort is a Divide and Conquer algorithm. You cannot perform it without using stacks or recursion.
Edit: The function does use recursion (oops!). But this isn't quicksort. If your are changing the method of a standard sorting algorithm, then it is no more that algorithm.

Related

C Lang: Quick sorting 5 elements but doesn't sort 10 elements

I am confused on why code only sorts 5 elements but not 10 elements. This method was described to me during my office visit with my professor. So I followed his instructions when I coded this. Can someone please help me fix this problem? My code works but it doesn't work on a bigger array. I know it is too late for someone to help before its due but I just want to understand what I had done in this code.
Thank You
/* Homework3.c
Qucksort arrays
Jared DaRocha
3/1/2020
*/
#include <stdio.h>// preprocessor
int partition(int arr[], int first, int end);// function declaration
void quickSort(int arr[], int first, int end);// function declaration
int main(void)// beginning of main
{
//Data
int array[5];// creates an array of 5 elements
int size = sizeof(array) / sizeof(array[0]); // calculates size of the array
// prompt user to input data to input int the array
printf("Enter 5 numbers to add in the array");
// prompts user to input 10 numbers
for(int i =0;i<size; i++)
{
scanf("%d", &array[i]);// stores user-defined data into the array
}
// call the function to sort the array
quickSort(array, 0, size -1);
// prints array
for (int i = 0; i < size; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
int partition(int arr[], int low, int high)// Function declaration
{
//Data
int partition = arr[low]; // partition number
// infinite for loop
for (;;)
{
// decrement high while partition is smaller than element of high
while(partition <arr[high])
{
high--;
}
// if partition is greater than element of high, swap high with low element since low element is the pivot
if(partition > arr[high]){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
low++;
}
// increment low while partition is greater than element of low
while(partition > arr[low])
{
low++;
}
// swap low and next element in the right if partition is less than element of low
if(partition < arr[low])
{
int temp = arr[low];
arr[low] = arr[low+1];
arr[low+1] = temp;
}
// if low is same as high then exit loop
if(high == low)
{
break;
}
}
return low;
}
void quickSort(int arr[], int first, int end) // function definition
{
if (first < end)
{
int partitionIndex = partition(arr, first, end);
quickSort(arr, first, (partitionIndex - 1));
quickSort(arr, (partitionIndex+1 ), end);
}
}
`
If you check the algorithm quicksort, you'll find how the array is divided into two slices that don't overlap, but also don't leave a single element hung around. You do this, just between both slices, you leave a single element. You divide the array in two slices and one single array element that doesn't participate in the recursive calls to the other slices.... and that's incorrect. What you know is that all elements of the inferior slice are less than the token, and the rest are greater than or equal than the token... but the point of division can be any element of the array subpart that corresponds.
By the way, the code or partition is completely incorrect. You first select one element that will act as a token, you can select it as any element of the array or as the average value of all of them, it doesn't matter... you are not going to put it in the array. then search from the end elements that are under the array, and search from the beginning elements that are over the token, once you have one such pair, switch them, but you exchange the found elements with the token, which not only doesn't do what is expected, but introduces copies of the token (and change its value) thing that will trash your array.
You are not searching for an array element... you are searching for a place (between to adjacent elements) where to divide your array, and the subarrays will be from beg, to pos-1, and from pos to end, (or from beg to pos, then from pos + 1 to end) but don't ignore the central element, because you know it belongs to an array, but it's not necessary the token.

Bubble Function sorting in C on an array of integers

I have coded out a Bubble Function that is supposed to Bubble sort an array of user input integers, but for some reason, my array is only working with arrays with size 6... otherwise the array outputs a zero for the largest number. Please run this code and help me identify the problem.
#include <stdio.h>
//prototype
void bubble(int array[], int size);
int main(void)
{
int n,i,j,size,temp,counter = 0;
//FOR SOME REASON, ONLY INPUT SIZE 6 WORKS PERFECTLY.
int k;
printf("How many numbers to sort?\n");
scanf("%d",&size);
int array[size];
for(i=0;i<size;i++)
{
printf("Enter number %d\n",i+1);
scanf("%d",&k);
array[i] = k;
}
printf("Array before sorting:\n");
for(i=0;i<size;i++)
{
printf("%d ",array[i]);
}
bubble(array,size);
return 0;
}
// use this if you want to test print an array
// for(i=0;i<size;i++)
// {
// printf("%d",array[i]);
// }
void bubble(int array[], int size)
{
int i, j, temp;
for(j=0;j<size;j++)
{
printf("\nIteration# %d\n",j+1);
for(i=0;i<size;i++)
{
if(array[i] > array[i+1])
{
temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
}
printf("%4d",array[i]);
}
}
}
// void select(int array[], int size)
// {
// int i, j, temp;
// min = array[0];
// for(j=0;j<size;j++)
// {
// if(array[j] < min)
// {
// array[j] = temp;
// min = array[j];
// }
// }
// }
Your inner-loop top-end conditional break is size, but within the loop you reference array[i+1], which means you're referring to array[size]. Since C arrays are zero-base indexed, the only allowable indexing is from 0...(size-1). Your code breaches that by one item repeatedly.
Changing the top-end of the inner loop to size-1 will work in your case. but there is arguably a better alternative that alleviates you from remembering the minus-1 in the first place. It involves modifying size as you sort to control the top-end of your inner loop directly. It also eliminates one local variable that you no longer need).
void bubble(int array[], int size)
{
while (size-- > 0)
{
for(int i=0; i<size; ++i)
{
if(array[i] > array[i+1])
{
int temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
}
}
}
}
Often called a "goes-down-to" expression (because it looks like a long arrow pointing at a limiting value), the outer loop has been changed to become while (size-- > 0). This takes the current value of size to a temporary, decrements size, and compares the temporary against > 0 (more or less). The result is size now reflects the top limit of your inner loop that you want. Each enumeration of the outer loop will shrink the next inner loop pass by one. The point of bubble sort is that, once an element has been "bubbled up" to its proper position, you need not visit that element ever again, something your code is not taking advantage of.
Bonus Optimization
Finally, you can optimize this further and give your bubblesort the one and only redeeming quality the algorithm can offer: O(n) in best case where the sequence is already sorted. You do this by doing "swap-detection". If you ever pass over the inner loop without making a single swap, it makes no sense to perform anymore sorting. The sequence is sorted and you're done. It's a near-freebie addition to the original algorithm above, and looks like this:
void bubble(int array[], int size)
{
int swapped = 1;
while (swapped && size-- > 0)
{
swapped = 0;
for(int i=0; i<size; ++i)
{
if(array[i] > array[i+1])
{
int temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
swapped = 1;
}
}
}
}
Given an already sorted sequence of a ten, a hundred, or a hundred-thousand elements, this will finish after only one pass. Worth noting: even one element out of position on one extreme end will make this optimization irrelevant. I.e. if any element that belongs near the beginning is originally near the end, it will take up to size iterations to bring it home, and with that the optimization becomes moot. In short, this sequence
1 3 4 5... 9998 9999 2
will completely foil the optimization above. There are techniques to combat this as well including a two pass inner loop enumeration, where you ascend to bubble up larger values, then reverse direction to descend, bubbling down smaller values. but at this point you're better off using a finer algorithm like quicksort or heapsort. The discussion of that, and indeed the latter half of this post, is beyond the scope of your question.
i<size in combination with i+1 will go past the bounds of the array.
You should replace this:
for(i=0;i<size;i++)
with this:
for(i=0;i<size-1;i++)

Rotating array (Larray hackerrank)

I am not able to get the logic behind the solution to the problem . I will be very thankful if someone can explain me the working of it.
Solution:
#include <bits/stdc++.h>
using namespace std;
const int N=1509;
int n;
int a[N];
void input(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
}
void sol(){
int K=1;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
K^=(a[i]>a[j]);
if (K) printf("YES\n");
else printf("NO\n");
}
int main() {
int test;
scanf("%d",&test);
while (test--){
input();
sol();
}
return 0;
}
I am not able to get how after xoring each permutation, value of 'k' in the end is determining the answer(ie whether it can be arranged in sorting order) ?
When you rotate a block you change the number of inversions by +/- 2 or 0 (work it out on paper, if you don't trust me). So if the number of inversions in the array is odd you will not be able make it sorted with the given operation. You'll end up with the array almost sorted with all but 2 elements in place (1 inversion) and you can't fix that with the given operation.
What the code does is check if the number of inversions is odd by xoring with itself every time it sees an inversion. You can get the same result if you count the inversions up and check inversions % 2 == 0.

Segment Tree to compute frequencies

Is there any way to use a Segment Tree structure to compute the frequency of a given value in an array?
Suppose there is an array A of size N, and every element A[i] of the array contains the value 0, 1 or 2. I want to perform the following operations:
Compute the amount of zeroes in any range [a,b] of the array
Increment (mod 3) every element in in any range [a,b] of the array
Example: If A = [0,1,0,2,0]:
Query[2,4] must return 1 , since there is one 0 in the range [2,4]
Increment[2,4] updates A to [0,2,1,0,0]
This looks really similar to the Range Sum Query problem, which can be solved using Segment Trees (in this case using Lazy Propagation because of range updates), but i had no success adapting my seg tree code to this problem, because if i store the values in the tree like in a normal RSQ, any parent node which contains the value "3" (for example) wouldn't mean nothing, since with this information i can't extract how much zeroes are present in this range.
Thanks in advance!
--
EDIT:
Segment Trees are binary tree structures that store intervals related to an array in its nodes. The leaf nodes store the actual array cells, and each parent node stores a function f(node->left, node->right) of its children. Segment Trees are commonly used to perform Range Sum Queries, in which we want to compute the sum of all elements in a range [a,b] of the array. In this case, the function computed by the parent nodes is the sum of the value in its children nodes. We want to use segtrees to solve the Range Sum Query problem because it allows to solve it in O(log n) (we only need to descend the tree until we find the nodes that are completely covered by our range query), much better than the naive O(n) algorithm.
Since actual array values are stored in the leaves (level L), let the nodes at level L - 1 store how many zeros they contain (which will be a value in the range [0, 2]). Other than that, everything is the same, the rest of the nodes will compute f(node->left, node->right) as node->left + node->right and the count of zeros will be propagated to the root.
After incrementing a range, if that range contained no zeros than nothing needs to be done. If however that range had zeros, then all those zeros will now be ones and the function value of current node (call it F) now becomes just zero. That change in the value now needs to be propagated upwards to the root, each time subtracting F from the function values.
This question can be easily solved using Square root decomposition
First create the new prefix sum array modulo each prefix sum by 3.
Divide the whole array into sqrt(n) blocks . Each block will have counts of number of 0's,1's and 2's. Also create one temporary array which will contain the sum to be added to the elements of the block
Here is the implementation in c++:
#include <bits/stdc++.h>
using namespace std;
#define si(a) scanf("%d",&a)
#define sll(a) scanf("%lld",&a)
#define sl(a) scanf("%ld",&a)
#define pi(a) printf("%d\n",a)
#define pl(a) printf("%ld\n",a)
#define pll(a) printf("%lld\n",a)
#define sc(a) scanf("%c",&a)
#define pc(a) printf("%c",a)
#define ll long long
#define mod 1000000007
#define w while
#define pb push_back
#define mp make_pair
#define f first
#define s second
#define INF INT_MAX
#define fr(i,a,b) for(int i=a;i<=b;i++)
///////////////////////////////////////////////////////////////
struct block
{
int one;
int two;
int zero;
block()
{
one=two=zero=0;
}
};
ll a[100005],a1[100005];
ll sum[400];
int main()
{
int n,m;
cin>>n>>m;
string s;
cin>>s;
int N=(int)(sqrt(n));
struct block b[N+10];
for(int i=0;i<n;i++)
{
a[i]=s[i]-'0';
a[i]%=3;
a1[i]=a[i];
}
for(int i=1;i<n;i++)
a[i]=(a[i]+a[i-1])%3;
for(int i=0;i<n;i++)
{
if(a[i]==0)
b[i/N].zero++;
else if(a[i]==1)
b[i/N].one++;
else
b[i/N].two++;
}
w(m--)
{
int type;
si(type);
if(type==1)
{
int ind,x;
si(ind);
si(x);
x%=3;
ind--;
int diff=(x-a1[ind]+3)%3;
if(diff==1)
{
int st=ind/N;
int end=(n-1)/N;
int kl=(st+1)*N;
int hj=min(n,kl);
for(int i=st*N;i<hj;i++)
{
a[i]=(a[i]+sum[st])%3;
}
sum[st]=0;
for(int i=ind;i<hj;i++)
{
if(a[i]==0)
b[st].zero--;
else if(a[i]==1)
b[st].one--;
else
b[st].two--;
a[i]=(a[i]+diff)%3;
if(a[i]==0)
b[st].zero++;
else if(a[i]==1)
b[st].one++;
else
b[st].two++;
}
for(int i=st+1;i<=end;i++)
{
int yu=b[i].zero;
b[i].zero=b[i].two;
b[i].two=b[i].one;
b[i].one=yu;
sum[i]=(sum[i]+diff)%3;
}
}
else if(diff==2)
{
int st=ind/N;
int end=(n-1)/N;
int kl=(st+1)*N;
int hj=min(n,kl);
for(int i=st*N;i<hj;i++)
{
a[i]=(a[i]+sum[st])%3;
}
sum[st]=0;
for(int i=ind;i<hj;i++)
{
if(a[i]==0)
b[st].zero--;
else if(a[i]==1)
b[st].one--;
else
b[st].two--;
a[i]=(a[i]+diff)%3;
if(a[i]==0)
b[st].zero++;
else if(a[i]==1)
b[st].one++;
else
b[st].two++;
}
for(int i=st+1;i<=end;i++)
{
int yu=b[i].zero;
b[i].zero=b[i].one;
b[i].one=b[i].two;
b[i].two=yu;
sum[i]=(sum[i]+diff)%3;
}
}
a1[ind]=x%3;
}
else
{
int l,r;
ll x=0,y=0,z=0;
si(l);
si(r);
l--;
r--;
int st=l/N;
int end=r/N;
if(st==end)
{
for(int i=l;i<=r;i++)
{
ll op=(a[i]+sum[i/N])%3;
if(op==0)
x++;
else if(op==1)
y++;
else
z++;
}
}
else
{
for(int i=l;i<(st+1)*N;i++)
{
ll op=(a[i]+sum[i/N])%3;
if(op==0)
x++;
else if(op==1)
y++;
else
z++;
}
for(int i=end*N;i<=r;i++)
{
ll op=(a[i]+sum[i/N])%3;
if(op==0)
x++;
else if(op==1)
y++;
else
z++;
}
for(int i=st+1;i<=end-1;i++)
{
x+=b[i].zero;
y+=b[i].one;
z+=b[i].two;
}
}
ll temp=0;
if(l!=0)
{
temp=(a[l-1]+sum[(l-1)/N])%3;
}
ll ans=(x*(x-1))/2;
ans+=((y*(y-1))/2);
ans+=((z*(z-1))/2);
if(temp==0)
ans+=x;
else if(temp==1)
ans+=y;
else
ans+=z;
pll(ans);
}
}
return 0;
}

Mergesort, weird behavior

Here's what I'm trying to do.
There are 3 arrays, cost[] node1[] and node2[].
These entires correspond to edges of a graph with node1[i],node2[i] and cost[i] specifying that there is an edge going from vertex node1[i] to node2[i] with an edge weight of cost[i].
I'm trying to sort these edges with respect to their weights, i.e sort the cost[] array using merge-sort. However whenever I'm, changing an entry in the cost[] array I also want to change the corresponding entries in the node1 and node2 array since even the nodes of the graph have to be modified. Ie if node1[]=1,2,3 and node2[]=2,3,1 cost[]={7 4 8} then after sorting the cost array the node1 and node2 should look like node1[]=2,1,3 node2[]=3,2,1. and cost[]=4,7,8
Here's my code.
#include<stdio.h>
#include<stdlib.h>
int merge_sort(int arr[],int low,int high,int node1[],int node2[])
{
int mid;
if(low<high) {
mid=(low+high)/2;
// Divide and Conquer
merge_sort(arr,low,mid,node1,node2);
merge_sort(arr,mid+1,high,node1,node2);
// Combine
merge(arr,low,mid,high,node1,node2);
}
return 0;
}
int merge(int arr[],int l,int m,int h,int node1[],int node2[])
{
int arr1[80000],arr2[80000]; // Two temporary arrays to
int arr3[70000],arr4[70000];
int arr5[70000],arr6[70000];
int n1,n2,i,j,k;
n1=m-l+1;
n2=h-m;
for(i=0; i<n1; i++)
{
arr1[i]=arr[l+i];
arr3[i]=node1[l+i];
arr5[i]=node2[l+i];
}
for(j=0; j<n2; j++)
{
arr2[j]=arr[m+j+1];
arr4[i]=node1[m+j+1];
arr6[i]=node2[m+j+1];
}
arr1[i]=99999; // To mark the end of each temporary array
arr2[j]=99999;
arr3[i]=99999;
arr4[j]=99999;
arr5[i]=99999;
arr6[j]=99999;
i=0;
j=0;
for(k=l; k<=h; k++) { //process of combining two sorted arrays
if(arr1[i]<=arr2[j])
{
arr[k]=arr1[i++];
//node1[k]=arr3[i++]; COMMENTED LINES!!!!!!!!!!!
//node2[k]=arr5[i++];
}
else
{
arr[k]=arr2[j++];
//node1[k]=arr4[j++]; COMMENTED LINES!!!!!!!!~!
//node2[k]=arr6[j++];
}
}
return(0);
}
int main(void)
{
int i,j,n,vert1,vert2,weight;
scanf("%d",&n);
int adjmat[n+1][n+1],cluster[n+1][n+1];
int *cost,*node1,*node2;
node1=malloc(sizeof(int)*1000000);
node2=malloc(sizeof(int)*1000000);
cost=malloc(sizeof(int)*1000000);
for(i=0;i<n+1;i++)
for(j=0;j<n+1;j++)
{
adjmat[i][j]=0;
cluster[i][j]=0;
}
for(i=1;i<n+1;i++)
cluster[i][0]=i;
for(i=1;i<(n+1)*(n+1);i++)
{
scanf("%d %d %d",&vert1,&vert2,&weight);
node1[i]=vert1;
node2[i]=vert2;
cost[i]=weight;
if(node1[i]==node1[i-1] && node2[i]==node2[i-1] && cost[i]==cost[i-1])
break;
// printf("%d %d %d\n",node1[i],node2[i],cost[i]);
adjmat[vert1][vert2]=weight;
adjmat[vert2][vert1]=weight;
}
printf("\n%d\n",i);
merge_sort(cost,1,124751,node1,node2);
for(j=1;j<i;j++)
printf("%d %d %d\n",node1[j],node2[j],cost[j]);
return(0);
}
Whenever I comment the lines in the merge function the code manages to sort the cost array. However whenever I un comment these lines somehow everything gets equated to 0. i.e all entires of the node1 node2 and cost arrays are 0. Could anyone tell me why this is happening? Thanks!
You probably have forgotten to take care of the side effect of the i++ operation. There is no need at all at that place to work with side effects, don't do that.

Resources