merge sort understanding recursion - c

I want to understand how does it work like I have this thing.
void merge_sort(int *a, int first, int last)
{
int middle;
if (first < last)
{
middle = (first + last) / 2;
merge_sort(a, first, middle); // 1
merge_sort(a, middle + 1, last); // 2
merge(a, first, middle, last); // 3
}
}
how does it work like if have an array of 3,4,2,1,5 it will first cut it in half from <--middle side and only then it will go to line 2-merge_sort(a, middle+1, last); and it will restart first and last until the condition is made and then it will go to line 3 like does all that stuff happen one after another or does it all happen at the same time all the 3 lines?

Yes, it sorts the first half, then it sorts the 2nd half and then it merges the two sorted parts together. This happens sequentially. The two recursive merge_sort() calls could happen in parallel (as they are non-overlapping) but it doesn't in this implementation.

Related

Merge sort function in C

I have the following piece of code which represents a merge sort function
/* perform merge sort */
void merge_sort(int arr[], int left, int right) {
if (left < right) {
int middle = left + (right - left) / 2;
merge_sort(arr, left, middle);
merge_sort(arr, middle + 1, right);
merge(arr, left, middle, right);
}
}
What is the use of this merge_sort(arr, middle + 1, right);?
The basic idea of the merge sort algorithm is to divide the sequence to be sorted in half, sort each half, and then merge them together. This is frequently done recursively, as in this case. As a result, there must be two recursive calls, one for each half of the sequence.
If the merge_sort(arr, middle+1, right); call were not present, the algorithm would not be complete and the sort would not work.
As an experiment, you could try writing an example program using the merge sort function you posted. Try it with and without the line in question and see what results you get.

What is the purpose of swap(v, left, (left + right)/2) in K&R's qsort() implementation?

in K&R second edition, section 5.11, page 107:
void qsort(void *v[], int left, int right, int (*comp)(void *, void *))
{
int i, last;
void swap(void *v[], int, int);
if (left >= right)
return;
swap(v, left, (left + right)/2);
last = left;
for (i = left+1; i <= right; i++)
if ((*comp)(v[i], v[left]) < 0) /* Here's the function call */
swap(v, ++last, i);
swap(v, left, last);
qsort(v, left, last-1, comp);
qsort(v, last+1, right, comp);
}
However, I am confused about the "swap(v, left, (left + right)/2);". I think it is useless... What's the purpose of this sentence?
If the sentence is absent, there is no problem if the array V[ ] is random data.
However, if V[ ] is sorted data, no swap is encountered,
and the variable last is not changed.
Thus, last qsort() is equivalent to qsort(v, left+1, right, comp).
It means that only one element decreased in recursive call.
When comparison number of times in the function is n,
comparison needs n + (n-1) + (n-2) + ... + 1 = n(n+1)/2 times to complete.
It takes a long time if n is big.
Furthermore, if V[ ] is very large, stack overflow error may be encountered.
The statement exists to prevent them.
In addition, (left + right) / 2 should be left + (right - left) / 2
to prevent overflow error.
This swap is meant to pick the pivot element and place it at the leftmost position. Then, the variable last is used to count how many elements were greater than the pivot hance belong to the right partition. We don't actually compute the number, though - but only a position of the leftmost element of the right partition. After that, we can safely place the pivot back to where it belongs (exactly to the left of the right partition) - this is what the second swap does - and form the left partition at no cost since we know for sure that all elements that are not the pivot or right partition must belong to the left one.
Still, I strongly recommend to to simply take a piece of paper, write some random integer array and try to walk through the code line by line to see how the partitioning process is performed.

Quick sort programmed in C

I am reading ANSI C by K&R. I came across the qsort program. I want a little help. Suppose I have 9 elements with index 0->8. Please read the comments to see if I am understanding it correct or not. Thanks a lot for you efforts
void qsort(int v[] , int left, int right)
{
int i, j, last;
void swap(int v[], int i, int j);
if(left >= right) /*if the array has only one element return it*/
return;
swap(v,left, (left+right)/2); /* now, left=(left+right)/2= 0+8/2= 4 we have 4 as left*/
last= left; /* putting left = last of the first partition group i.e. last=4*/
for(i=left+1; i<=right,i++) /* now looping from 4+1=5 to 8 with increment of 1*/
if(v[i] < v[left]) /*if value at 5th is less than value at 4th */
swap(v, ++last, i);
I have problem in this last swap step. As my values suggest swap ++4 i.e. to i i.e. 4+1= 5 (swapping 5 position with 5?). How can I understand this? There must be a swapping between 4 and 5, not 5 and 5 is it?
code continues
swap(v,left, last);
qsort(v,left,last-1);
qsort(v,last+1,right);
}
Firstly, you have a small misconception about the swap function. Let's say the prototype of the function is -
swap(int array[], int i, int j)
The swap function swaps the numbers at location array[i] and array[j]. So, the swap function swaps the elements in the array. So, the line -
swap(v, left, (left + right) / 2);
Means that, the middle element in the array is swapped with the leftmost element. Clearly, the quicksort is taking the middle element as the pivot. This swap has no effect on the local variables or parameters. According to your data input example, the value of 'left' = 0, and the value of right = '8', even after the swapping. This is where you got confused. The elements of array are swapped, not the values of variables. So, now, the line -
last = left;
makes, 'last' point to the location of the pivot ('left'), so here the value of 'last' = 0 not 4. So, the loop,
for(i = left + 1; i <= right; i++)
Runs from i = 1 to 8. BTW, you forgot the semicolon..! Then, the line,
if(v[i] < v[left])
checks if the current element ('v[i]') is less than the pivot ('v[left]') or not. Then, accordingly swaps the lesser elements as in the line,
swap(v, ++last, i);
from the location (last + 1) till where ever it increments to. So, the elements to the left of 'last' are less than pivot and the elements to the right are greater. I think you are missing another line, where we bring the pivot back to the middle which was at the location 'v[left]' during the execution of the algorithm. Then, the recursive calls play their roles. If you are looking for help with quicksort, this is a good place to start !
I hope my answer has helped you, if it did, let me know..! ☺

Why is this code for implementing mergesort not producing correct result?

Here in I'm trying to implement mergesort on array of 10 elements. On providing the input a[10]={9,8,7,6,5,4,3,2,1,0} , I obtain the output as {0,0,1,1,0, 4,4,4,3,2} while the expected output is {0,1,2,3,4,5,6,7,8,9}. I am calling the mergesort in main with l=0,h=9.
void mergesort(int a[],int l,int h)
{
int c[10];
int m=(l+h)/2;
if(l<h)
{
mergesort(a,l,m); // Recursive call to sort first half
mergesort(a,m+1,h); // Recursive call to sort second half
}
int i,k=l,j=l;
while(k<=h) // Merging the first and second half of the array
{
if(a[j]<a[m+1])
{
c[k]=a[j];
k++;
j++;
}
else
{
c[k]=a[m+1];
k++;
m++;
}
}
for(i=l;i<=h;i++)
a[i]=c[i];
}
One of the few problems: Your value of l is no longer a valid left limit after the while loop since you are incrementing it. So when you are copying from array c to a later in the for loop, you are copying invalid data.
The problem is that in your while loop you are sometimes looking outside the bounds you should be.
Assuming you put in a check at the beginning of your function saying if l==h then return (since sorting a one element array is unnecessary) then the first time it will do anything is when it recurses to mergesort(a, 0,1). Here we are basically merging two single element arrays.
When going through your loop the first time we have i,j,k,m=0. We will go into the else part of the if because element 0 is greater than 1. We thus write out a[1] into the output array and we now have i,j=0 and k,m=1. What we now should do is note that our second array is exhausted and fill from the remaining elements in the first array.
Instead what we do is compare elements j and m+1 (ie 0 and 2). This is clearly wrong because element 2 shouldn't appear in the array. We of course find element 2 is smaller and thus put that into the output array at position two. Once we copy this over a we get {8, 7, 7, 6, 5, 4, 3, 2, 1, 0} and this is where it all goes wrong.

quicksort special case - seems to be a faulty algorithm from K&R

I have a problem understanding quicksort algorithm (the simplified version without pointers) from K&R. There is already a thorough explanation provided by Dave Gamble here explanation.
However I noticed that by starting with a slightly changed string we can obtain no swaps during many loops of the for loop.
Firstly the code:
void qsort(int v[], int left, int right)
{
int i, last;
void swap(int v[], int i, int j);
if (left >= right) /* do nothing if array contains */
return; /* fewer than two elements */
swap(v, left, (left + right)/2); /* move partition elem */
last = left; /* to v[0] */
for (i = left + 1; i <= right; i++) /* partition */
if (v[i] < v[left])
swap(v, ++last, i);
swap(v, left, last); /* restore partition elem */
qsort(v, left, last-1);
qsort(v, last+1, right);
}
Walkthrough in my opinion:
we start with CADBE; left=0; right=4; D is the pivot
so according to algorithm we swap D with C obtaining DACBE
last = left =0
i = 1 if ( v1 < v[0] ) it is true so we swap v1 (because last is incremented before operation) with v1 so nothing changes, last = 1, still having DACBE;
now i = 2 if ( v[2] < v[0] ) -> true so we swap v[2] with v[2] nothing changed again; last = 2
now i = 3 if ( v[3] < v[0] ) -> true so we swap v[3] with v[3] nothing changed AGAIN (!), last = 3
So apparently something is wrong, algorithm does nothing.
Your opinions appreciated very much. I must be wrong, authors are better than me ;D
Thanks in advance!
The loop goes from left + 1 up to and including right. When i=4, the test fails and last does not get incremented.
Then the recursive calls sort BACDE with left=0,right=2 and left=4,right=4. (Which is correct when D is the pivot.)
Well, it just so happened that your input sub-array ACBE is already partitioned by D (ACB is smaller than D and E is bigger than D), so it is not surprising the partitioning cycle does not physically swap any values.
In reality, it is not correct to say that it "does nothing". It does not reorder anything in the cycle, since your input data need no extra reordering. But it still does one thing: it finds the value of last that says where smaller elements end and bigger elements begin, i.e. it separates ACBE into ACB and E parts. The cycle ends with last == 3, which is the partitioning point for further recursive steps.

Resources