Understanding the Recursion of mergesort

2019-01-31 08:45发布

Most of the mergesort implementations I see are similar to this. intro to algorithms book along with online implentations I search for. My recursion chops don't go much further than messing with Fibonacci generation (which was simple enough) so maybe it's the multiple recursions blowing my mind, but I can't even step through the code and understand whats going on even before I even hit the merge function.

How is it stepping through this? Is there some strategy or reading I should undergo to better understand the process here?

void mergesort(int *a, int*b, int low, int high)
{
    int pivot;
    if(low<high)
    {
        pivot=(low+high)/2;
        mergesort(a,b,low,pivot);
        mergesort(a,b,pivot+1,high);
        merge(a,b,low,pivot,high);
    }
}

and the merge(although frankly I'm mentally stuck before I even get to this part)

void merge(int *a, int *b, int low, int pivot, int high)
{
    int h,i,j,k;
    h=low;
    i=low;
    j=pivot+1;

    while((h<=pivot)&&(j<=high))
    {
        if(a[h]<=a[j])
        {
            b[i]=a[h];
            h++;
        }
        else
        {
            b[i]=a[j];
            j++;
        }
        i++;
    }
    if(h>pivot)
    {
        for(k=j; k<=high; k++)
        {
            b[i]=a[k];
            i++;
        }
    }
    else
    {
        for(k=h; k<=pivot; k++)
        {
            b[i]=a[k];
            i++;
        }
    }
    for(k=low; k<=high; k++) a[k]=b[k];
}

9条回答
SAY GOODBYE
2楼-- · 2019-01-31 09:20

process to divide the problem into subproblems Given example will help you understand recursion. int A[]={number of element to be shorted.}, int p=0; (lover index). int r= A.length - 1;(Higher index).

class DivideConqure1 {
void devide(int A[], int p, int r) {
    if (p < r) {
        int q = (p + r) / 2; // divide problem into sub problems.
        devide(A, p, q);   //divide left problem into sub problems
        devide(A, q + 1, r); //divide right problem into sub problems
        merger(A, p, q, r);  //merger the sub problem
    }
}

void merger(int A[], int p, int q, int r) {
    int L[] = new int[q - p + 1];
    int R[] = new int[r - q + 0];

    int a1 = 0;
    int b1 = 0;
    for (int i = p; i <= q; i++) {  //store left sub problem in Left temp
        L[a1] = A[i];
        a1++;
    }
    for (int i = q + 1; i <= r; i++) { //store left sub problem in right temp
        R[b1] = A[i];
        b1++;
    }
    int a = 0;
    int b = 0; 
    int c = 0;
    for (int i = p; i < r; i++) {
        if (a < L.length && b < R.length) {
            c = i + 1;
            if (L[a] <= R[b]) { //compare left element<= right element
                A[i] = L[a];
                a++;
            } else {
                A[i] = R[b];
                b++;
            }
        }
    }
    if (a < L.length)
        for (int i = a; i < L.length; i++) {
            A[c] = L[i];  //store remaining element in Left temp into main problem 
            c++;
        }
    if (b < R.length)
        for (int i = b; i < R.length; i++) {
            A[c] = R[i];  //store remaining element in right temp into main problem 
            c++;
        }
}
查看更多
来,给爷笑一个
3楼-- · 2019-01-31 09:26

I know this is an old question but wanted to throw my thoughts of what helped me understand merge sort.

There are two big parts to merge sort

  1. Splitting of the array into smaller chunks (dividing)
  2. Merging the array together (conquering)

The role of the recurison is simply the dividing portion.

I think what confuses most people is that they think there is a lot of logic in the splitting and determining what to split, but most of the actual logic of sorting happens on the merge. The recursion is simply there to divide and do the first half and then the second half is really just looping, copying things over.

I see some answers that mention pivots but I would recommend not associating the word "pivot" with merge sort because that's an easy way to confuse merge sort with quicksort (which is heavily reliant on choosing a "pivot"). They are both "divide and conquer" algorithms. For merge sort the division always happens in the middle whereas for quicksort you can be clever with the division when choosing an optimal pivot.

查看更多
仙女界的扛把子
4楼-- · 2019-01-31 09:29

My apologies if this has been answered this way. I acknowledge that this is just a sketch, rather than a deep explanation.

While it is not obvious to see how the actual code maps to the recursion, I was able to understand the recursion in a general sense this way.

Take a the example unsorted set {2,9,7,5} as input. The merge_sort algorithm is denoted by "ms" for brevity below. Then we can sketch the operation as:

step 1: ms( ms( ms(2),ms(9) ), ms( ms(7),ms(5) ) )

step 2: ms( ms({2},{9}), ms({7},{5}) )

step 3: ms( {2,9}, {5,7} )

step 4: {2,5,7,9}

It is important to note that merge_sort of a singlet (like {2}) is simply the singlet (ms(2) = {2}), so that at the deepest level of recursion we get our first answer. The remaining answers then tumble like dominoes as the interior recursions finish and are merged together.

Part of the genius of the algorithm is the way it builds the recursive formula of step 1 automatically through its construction. What helped me was the exercise of thinking how to turn step 1 above from a static formula to a general recursion.

查看更多
在下西门庆
5楼-- · 2019-01-31 09:31

Concerning the recursion part of the merge sort, I've found this page to be very very helpful. You can follow the code as it's being executed. It shows you what gets executed first, and what follows next.

Tom

查看更多
Root(大扎)
6楼-- · 2019-01-31 09:32

the mergesort() simply divides the array in two halves until the if condition fails that is low < high. As you are calling mergesort() twice : one with low to pivot and second with pivot+1 to high, this will divide the sub arrays even more further.

Lets take an example :

a[] = {9,7,2,5,6,3,4}
pivot = 0+6/2 (which will be 3)
=> first mergesort will recurse with array {9,7,2} : Left Array
=> second will pass the array {5,6,3,4} : Right Array

It will repeat until you have 1 element in each left as well as right array. In the end you'll have something similar to this :

L : {9} {7} {2} R : {5} {6} {3} {4} (each L and R will have further sub L and R)
=> which on call to merge will become

L(L{7,9} R{2}) : R(L{5,6} R{3,4})
As you can see that each sub array are getting sorted in the merge function.

=> on next call to merge the next L and R sub arrays will get in order
L{2,7,9} : R{3,4,5,6}

Now both L and R sub array are sorted within
On last call to merge they'll be merged in order

Final Array would be sorted => {2,3,4,5,6,7,9}

See the merging steps in answer given by @roliu

查看更多
萌系小妹纸
7楼-- · 2019-01-31 09:33

I think the "sort" function name in MergeSort is a bit of a misnomer, it should really be called "divide".

Here is a visualization of the algorithm in process.

enter image description here

Each time the function recurses, it's working on a smaller and smaller subdivision of the input array, starting with the left half of it. Each time the function returns from recursion, it will continue on and either start working on the right half, or recurse up again and work on a larger half.

Like this

[************************]mergesort
[************]mergesort(lo,mid)
[******]mergesort(lo,mid)
[***]mergesort(lo,mid)
[**]mergesort(lo,mid)
 [**]mergesort(mid+1,hi)
[***]merge
   [***]mergesort(mid+1,hi)
   [**]mergesort*(lo,mid)
    [**]mergesort(mid+1,hi)
   [***]merge
[******]merge
      [******]mergesort(mid+1,hi)
      [***]mergesort(lo,mid)
      [**]mergesort(lo,mid)
       [**]mergesort(mid+1,hi)
      [***]merge
         [***]mergesort(mid+1,hi)
         [**]mergesort(lo,mid)
           [**]mergesort(mid+1,hi)
         [***]merge
      [******]merge
[************]merge
            [************]mergesort(mid+1,hi)
            [******]mergesort(lo,mid)
            [***]mergesort(lo,mid)
            [**]mergesort(lo,mid)
             [**]mergesort(mid+1,hi)
            [***]merge
               [***]mergesort(mid+1,hi)
               [**]mergesort(lo,mid)
                 [**]mergesort(mid+1,hi)
               [***]merge
            [******]merge
                  [******]mergesort(mid+1,hi)
                  [***]mergesort(lo,mid)
                  [**]mergesort*(lo,mid)
                    [**]mergesort(mid+1,hi)
                  [***]merge
                     [***]mergesort(mid+1,hi)    
                     [**]mergesort(lo,mid)
                      [**]mergesort(mid+1,hi)
                     [***]merge
                  [******]merge
            [************]merge
[************************]merge
查看更多
登录 后发表回答