Why is this quicksort routine not working? - c

I'm trying to use this to sort randomly generated numbers, so I print the random numbers, apply the quicksort and then print the sorted list, but it just gives me the numbers in the same order again.
void quicksort(double x[], int first, int last)
{
int i=first;
int j=last;
if((j-i)<2)
{return;}
else
{
double pivot;
pivot=x[(i+j)/2];
if(i<j)
{
if(x[i]<pivot)
{ ++i; printf("%f\n", i); }
else if(x[j]>pivot)
{ --j; }
else if(x[i]>=pivot && x[j]<=pivot)
{
swap( x[i], x[j] );
++i;
--j;
}
}
quicksort(x, first, i);
quicksort(x, i+1, last);
}
}

You're not showing swap().
If that's a function, there's no way it can succeed since it only gets the values of x[i] and x[i], not any addresses.
If it's a macro, it might work, but of course it's hard to tell without seeing it.

There are a few issues with your implementation. The one that's causing your current problem is most likely the swap function, which if you tested it, I'm guessing probably wouldn't do any swapping. I suggest you replace it with something like this:
void
swap(double a[], int i, in j)
{
double t = a[i];
a[i] = a[j]; a[j] = t;
}
But then after that, there will be other issues in your implementation, because you are not partitioning the list properly.
Here's a reference implementation (I'm taking the pivot to be the first element in the range, which is non-optimal, but not less optimal than picking the middle element).
void
qsort(double a[], int lo, int hi)
{
int i = lo, j = hi - 1;
int pivot = a[i++];
if(i+1 > j) return;
while(i<j)
{
while(i < hi && a[i] < pivot) i++;
while(j > lo && a[j] > pivot) j--;
if(i<j) swap(a, i, j);
}
swap(a, lo, j);
qsort(a, lo, j);
qsort(a, j+1, hi);
}

Related

C language: Why my code get infinite loop and how to use recursion to solve Merge Sort problem?

There are two problems I encounter in this code.
The first problem is the infinite loop, which happened from 77~79. When I change this test data and use merge method instead, I will get infinite loop and the R1 will be change to 4198739.But if I use mergeSort method, the problem won't show up as expected. Shown as below:
int M = 2;
int R1 = 4;
int arr[] = {3,9,8,20};
merge(arr, L, M, R1);
Second problem happened from 57 to 69 lines.In this mergeSort method, I tried to divide the unorder array into sub-array. However, there is no any effect on the original array.
Here is my code.
Merge Sort in C
void mergeSort(int arr[], int L, int R)
{
if(L<R) {
return ;
} else {
int M = (L+R) / 2;
mergeSort(arr, L, M);
mergeSort(arr, M+1, R);
merge(arr, L, M+1 ,R);
}
}
There are several mistakes in the logic of the code. I will address it the correct code.
A few pointers first.
The logic deals with array indices, don't confuse with array limits.
I have used sizeof operator to get the upper limit of the array.
Use if-else whenever possible.
The full code cannot be properly added here, so - https://pastebin.com/ixickcQA
Main mistake was here:
for(i=M;i<=R;i++) //1. <= as R is also an index
{
right[i-M] = arr[i];
}
here is the code linked by the (other) answer:
#include <stdio.h>
void merge(int arr[], int L, int M, int R)
{
int LEFT_SIZE = M-L;
int RIGHT_SIZE = R-M+1;
int left[LEFT_SIZE];
int right[RIGHT_SIZE];
int i,j,k;
for(i=L;i<M;i++)
{
left[i-L] = arr[i];
}
for(i=M;i<=R;i++) //1. <= as R is also an index
{
right[i-M] = arr[i];
}
i=0;
j=0;
k=L;
while(i<LEFT_SIZE && j<RIGHT_SIZE)
{
if(left[i] <= right[j]) //2. <= so that duplicates are not ignored
{
arr[k] = left[i];
i++;
}
else //3. use else to keep up the computing speed
{
arr[k] = right[j];
j++;
}
k++;
}
while(i<LEFT_SIZE)
{
arr[k] = left[i];
i++;
k++;
}
while(j<RIGHT_SIZE)
{
arr[k] = right[j];
j++;
k++;
}
}
void mergeSort(int arr[], int L, int R)
{
if(L<R) //4. Better
{
int M = (L+R) / 2;
mergeSort(arr, L, M);
mergeSort(arr, M+1, R);
merge(arr, L, M+1 ,R);
}
}
int main() {
int arr[] = {3,9,20,8,21,22};
mergeSort(arr, 0, (sizeof(arr)/sizeof(int))-1); //5. Use size-1 as we are dealing with indices
int i;
printf("Final\n");
for(i=0; i<(sizeof(arr)/sizeof(int)); i++)
{
printf("i=%d, R=%d,arr[i]=%d\n",i,(sizeof(arr)/sizeof(int)), arr[i]);
}
}
so inserting the code into an answer is no problem
In the mergeSort replace this:
if(L<R)
By this:
if(L>=R)
If L<R is present then it will always be satisfied because the index L will always be less than than R (at starting). And this is the reason that you will get infinite loop.
Your code contains a logical error on your if-statement. Try this:
void mergesort(int a[], int l, int r) {
int m;
if (l < r) {
// same as (l+r) / 2 but for avoiding overflow
m = l + (r - l) / 2;
// recur both parts
mergesort(a, l, m);
mergesort(a, m + 1, r);
merge(a, l, m, r); // merging parts
}
}

C - using to pointer to function yield no result

I am creating a program which uses a pointer to function to allow the user to select from 2 options to perform on an array of integers: reverse or randomise. However, the program yield no output when executed. Where did I go wrong?
void reverseArray(int arraySize, int a[]);
void randomiseArray(int arraySize, int a[]);
void printArray(int arraySize, int a[], void (*arrayFunction)(int arraySize, int a[]));
int main ()
{
srand(time(NULL));
int selection;
int myArray[] = {2,4,6,8,10,12,14,16,18,20};
printf("Enter 0 for reverse, 1 for randomise.\n");
scanf("%d",&selection);
if (selection == 0)
printArray(sizeof(myArray)/sizeof(myArray[0]),myArray,reverseArray);
else if (selection == 1)
printArray(sizeof(myArray)/sizeof(myArray[0]),myArray,randomiseArray);
else
printf("Please make a valid selection\n");
return 0;
}
void reverseArray(int arraySize, int a[])
{
int i,j=arraySize,swap=0;
for (i=0;i<arraySize/2;i++)
{
swap = a[i];
a[i] = a[j];
a[j] = swap;
--j;
}
}
void randomiseArray(int arraySize, int a[])
{
int i,j,swap;
for (i=0;i<arraySize;i++)
{
j = rand()%(i+1);
if (j!=i)
{
swap = a[i];
a[i] = a[j];
a[i] = swap;
}
}
}
void printArray(int arraySize, int a[], void (*arrayFunction)(int arraySize, int a[]))
{
arrayFunction(arraySize, a);
for (int i = 0; i < arraySize; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
The user selects when function they want to perform by typing either 0 for reverse or 1 for randomise. Based on the decision, a printArray function is called with the respective choice.
reverseArray is just a generic swap function, and randomiseArray uses a Fisher-Yates shuffling algorithm.
I am assuming that the source of my error lies in the printArray fucntion, yet I cannot correct it.
Two errors
in reverseArray, j should start at arraysize - 1 or you will be writing one element past the end of the array
void reverseArray(int arraySize, int a[])
{
int i, j = arraySize - 1, swap = 0;
for (i = 0; i<arraySize / 2; i++)
{
swap = a[i];
a[i] = a[j];
a[j] = swap;
--j;
}
}
And in randomizearray(), you assign to a[i] twice, instead of using a[j] for the final assignment.
void randomiseArray(int arraySize, int a[])
{
int i, j, swap;
for (i = 0; i<arraySize; i++)
{
j = rand() % (i + 1);
printf("%d %d\n", i, j);
if (j != i)
{
swap = a[i];
a[i] = a[j];
a[j] = swap;
}
}
}
The first error causes undefined behavior when reversing, the second causes the array to stay the same. Other than that, the function pointers are setup fine and work correctly.
In reverseArray() your index j is out of range, it should he j=arraySize-1
Other than that it works for me.
You are accessing outside the bounds of your array, causing UB and in this case "no output":
In reverseArray, you must initialize j=arraySize-1. Without the -1 you go out of bounds.

Quicksort implementation in C using first element as pivot

I am a complete beginner to stackoverflow and this is my first post. Please forgive if this is not the correct place to post these kinds of queries. I have written code for the Quicksort algorithm, based on the algorithm given in the Algorithms course in Coursera(It is not for any assignments though).
Basically, there are two functions Quicksort which is called recursively and partition() function that returns the index of the pivot. I select the pivot as the first element of the array every time. I checked the partition() function and it works fine but the array is not sorted even after I call the Quicksort() function.
Any help is appreciated. Thanks.
#include <stdio.h>
void swap(int *p, int i, int j)
{
int temp = *(p+i);
*(p+i) = *(p+j);
*(p+j) = temp;
}
int partition(int *q, int l, int r)
{
int i = l+1, j;
int p = l;
int len = r-l +1;
for (j = l+1; j < len; j++)
{
/*printf("%d \n", j);*/
if ( *(q+j) < *(q+p) )
{
swap(q, i, j);
i += 1;
}
}
swap(q, l, i-1);
/*printf("%d", i-1);*/
return (i-1);
}
void quicksort(int *ptr, int low, int high)
{
if (low < high)
{
int p = partition(ptr, low, high);
printf("%d\n", p);
quicksort(ptr, low, p);
quicksort(ptr, p+1, high);
}
}
int main(){
int i;
int a[] = {3, 8, 2, 5, 1, 4, 7, 6};
int len = sizeof(a)/sizeof(a[0]);
for ( i = 0; i < len; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
int *ptr = a;
quicksort(ptr, 0, len-1);
for (i = 0; i < sizeof(a)/sizeof(a[0]); ++i)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
2 corrections.
Small one: Change 3rd line inside if block in QuickSort function
from
quicksort(ptr, low, p);
to
quicksort(ptr, low, p-1);
This will improve performance.
Main error:
Your partition function is wrong. Specifically the loop where j runs from l+1 to r-l+1, because, r-l+1 can be less than l+1
I'll write the partition function for you if you want (post a comment if you face any problem with that) though I'd advice you to do it yourself.
EDIT:
A possible partition function:
int partition(int *q, int l, int r){
int i,j;
int p = *(q + l);
for(i = l + 1, j = r; ;){
while(*(q + i) <= p)
i++;
while(*(q + j) >= p)
j--;
if(i >= j)
break;
swap(q, i, j);
}
return i;
}
Changes noted in comments.
int partition(int *q, int l, int r)
{
int i = l+1, j;
int p = l;
/* fix: int len = r-l+1; is not used */
for (j = l+1; j <= r; j++) /* fix: j <= r */
{
if ( *(q+j) <= *(q+p) ) /* fix: <= */
{
swap(q, i, j);
i += 1;
}
}
swap(q, l, i-1);
return (i-1);
}
void quicksort(int *ptr, int low, int high)
{
if (low < high)
{
int p = partition(ptr, low, high);
quicksort(ptr, low, p-1); /* optimization: p-1 */
quicksort(ptr, p+1, high);
}
}
If interested, Hoare partition scheme is faster. If you switch to this, don't forget to change the two quicksort calls to quicksort(lo, p) and quicksort(p+1, hi) ). You might want to change the Hoare pivot to pivot = A[(lo+hi)/2], which will avoid worst case issue with sorted or reverse sorted array.
http://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme

Writing merge sort in C without pointers

I am trying to write code for homework in C that will take 10 integers from user input into an array and sort it using a recursive merge sort. We have not gone over pointers yet so I wanted to avoid using that in my code (many online examples use pointers).
Here is my code:
/* This code will take input for 10 integers given by the user
into an array, sort them with a recursive merge function
and print the updated array in ascending order. */
#include <stdio.h>
#define ARRSIZE 10
void merge_sort (int arr[], int temp[], int left, int right);
void merge (int arr[], int temp[], int left, int mid, int right);
int main (void){
int arr[ARRSIZE], temp[ARRSIZE], left, right, i;
printf("Enter 10 integers for an array:");
for(i=0;i<ARRSIZE;i++){
printf("\narray value %d:", i+1);
scanf("%d", &arr[i]);
}
left = 0;
right = ARRSIZE-1;
merge_sort(arr, temp, left, right);
printf("\nHere is your updated array:");
printf("\n{");
for(i=0;i<ARRSIZE;i++){
printf("%d,", arr[i]);
}
printf("}");
return 0;
}
void merge_sort (int arr[], int temp[], int left, int right){
if(left<right){
int mid = (right-left)/2;
merge_sort(arr, temp, left, mid);
merge_sort(arr, temp, mid+1, right);
merge(arr, temp, left, mid, right);
}
}
void merge (int arr[], int temp[], int left, int mid, int right){
int i, j, tempi = 0;
for(i=left, j=mid+1; i<=mid && j<=right ;){
// mid+1 is the start of the right array
if(arr[i]<arr[j] && i<=mid){
temp[tempi] = arr[i];
tempi++;
i++;
}
else if(arr[i]>arr[j] && j<=right){
temp[tempi] = arr[j];
tempi++;
j++;
}
}
for(i=0,j=right; i<=j; i++){
arr[i] = temp[i];
}
}
I keep getting a segmentation fault when I run this in my linux shell. Any suggestions?
Well, I've found one subtle bug already: int mid = (right-left)/2; should be int mid = (left+right)/2;
Also, what flows I've found:
You should use tempi = left for simplicity. Simply copy incoming part of array to corresponding part of temporary array.
In your merge cycle, you can place incrementing tempi inside for definition:
for( ...; ...; ++tempi)
Inside that loop you check boundaries AFTER you have read values from that place. This is very bad. VERY. Although, You haven't encounter any problems here just because you are checking boundaries inside for definition :) simply remove them:
for (i = left, j = 1 + mid; i <= mid && j <= right; ++tempi)
{
if (arr[i] < arr[j]) temp[tempi] = arr[i++];
else /* arr[j] <= arr[i] */ temp[tempi] = arr[j++];
}
Cause this loop exits when either subarray has reached end, you have to copy rest of items from another subarray to temp[]:
if (i > mid) i = j; /* if we need to copy right subarray */
for (; tempi <= right; ++tempi, ++i) temp[tempi] = arr[i];
So, your copying back from temporary array will look like
for (i = left; i <= right; ++i) arr[i] = temp[i];

QuickSort doesn't work for large inputs

Can anyone spot a problem with my quick sort implementation below? It seems to fail on arrays with more than 10 or so elements.
void swap(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void generateRandom(int arr[], int size)
{
srand(time(NULL));
int i;
for (i = 0; i < size; i++)
{
arr[i] = rand() % 100;
}
}
int partition(int arr[], int start, int end)
{
int i = start, j = end;
int pivot = arr[start];
for (;;)
{
for (; arr[i] < pivot; i++);
for (; arr[j] > pivot; j--);
if (i < j)
{
swap(&arr[i], &arr[j]);
}
else
{
return j;
}
}
}
void quickSort(int arr[], int start, int end)
{
int part;
if (start < end)
{
part = partition(arr, start, end);
quickSort(arr, start, part);
quickSort(arr, part + 1, end);
}
}
int main()
{
generateRandom(arr, 100);
for (i = 0; i < 100; i++)
{
printf("%d ", arr[i]);
}
printf("\n\n");
quickSort(arr, 0, 99);
for (i = 0; i < 100; i++)
{
printf("%d ", arr[i]);
}
printf("\n\n");
return 0;
}
First, you code doesn't compile. When I made the corrections to make it compile (adding stdio.h, and definitions for arr and i in main) it infinite looped, which it will do if the partition starts and ends with the pivot. You need to increment and decrement before the comparisons rather than after. You can do that by starting with i = start-1 and j = end+1 and changing your inner loops to increment or decrement first, or you can leave them as is and just do an i++ and j-- after the swap -- I did that and the sort works.
Note that your pivot choice is poor for already sorted arrays; you really should be picking the median of 3 or even 9 values.
P.S. Other desirable optimizations: 1) Switch to an insertion sort for small partitions -- the optimal cutoff point is machine-dependent. Another approach is to not sort partitions below a certain size, and then do an insertion sort on the whole array after quicksort is done. It's also possible to use heapsort instead of insertion sort if done carefully; google introsort. 2) quicksort does two recursive calls; eliminate the second one by setting start = part + 1 and looping. 3) Avoid the possibility of stack overflow by quicksorting the larger partition first. 4) Eliminate the first recursive call by using an explicit stack. 5) Inline swap() and partition(). 6) Don't bother with any of that and just call the qsort library routine. :-)
I had the same problem,
but I changed my while loop to do..while and it worked.
This is my new code now.
int partition(int a[], int lo, int hi) {
int v = a[lo], i = lo, j = hi;
do {
do {
i++;
} while(a[i] < v) ;
do {
j--;
}while(a[j] > v) ;
if(i < j) interchange(&a[i], &a[j]);
}while(i < j);
interchange(&a[lo], &a[j]);
return j;
}

Resources