合并排序函数中的两个递归调用混淆

时间:2012-06-22 14:12:46

标签: java recursion

我已经与算法脱节了一段时间,并且最近开始修改我的概念。令我惊讶的是,最后我记得我的递归技巧是我很擅长但不再了。所以,我有一个基本的问题,你们这让我很困惑。请先看下面的代码..

private void mergesort(int low, int high) {
    if (low < high) {
        int middle = (low + high)/2 ;
        System.out .println ("Before the 1st Call");
        mergesort(low, middle);
        System.out .println ("After the 1st Call");
        mergesort(middle+1, high);
        System.out .println ("After the 2nd Call");
        merge(low, middle, high);
    }
}

函数调用

mergesort(0,7);

输出

  

在第一次通话之前

     

在第一次通话之前

     

在第一次通话之前

     

第一次通话后

     

第二次电话会议后

     

第一次通话后

     

在第一次通话之前

     

第一次通话后

     

第二次电话会议后

     

第二次电话会议后

     

第一次通话后

     

在第一次通话之前

     

在第一次通话之前

     

第一次通话后

     

第二次电话会议后

     

第一次通话后

     

在第一次通话之前

     

第一次通话后

     

第二次电话会议后

     

第二次电话会议后

     

第二次电话会议后

令我困惑的是上面的代码和结果是第二次递归调用。我理解直到第四个输出线的流程(即:第一次呼叫后)。但我无法理解为什么它(在第一次通话后)输出(第二次通话后)。根据代码的理解,在输出之后(第一次调用之后)应该调用带参数(中间+ 1,高)的mergesort函数,它应该输出(在第一次调用之前)并使用mergesort进入递归调用(低,中)。我熟悉一个递归调用函数并理解并与foreg fibonacci示例同步。

9 个答案:

答案 0 :(得分:17)

在第四个输出行,您已从第一个调用和随后的2个递归调用返回,因此现在控件到达System.out .println ("After the 1st Call");

因此,在第二次递归调用之后,条件low < high为false,因此您只需退出该函数。然后,控制在第二次递归调用之后立即返回到该行。

提示 我在学习递归时经常做的一件事就是跟踪堆栈深度(例如为此传递参数),然后在输出中根据堆栈深度缩进输出。这有助于您可视化递归链中的位置,并使调试更容易。

因此,您的调试输入可能类似于以下内容:

entered method, low = 0, high = 10
  entered method, low = 0, high = 5
     entered method, low = 0, high = 2
     exiting method, low = 0, high = 2
  exiting method, low = 0, high = 5
exiting method, low = 0, high = 10

答案 1 :(得分:5)

按照执行......

First call 0,7 --> enters if, middle = 3 (integer division), calls again as (0,3)
Second call 0,3 --> enters if, middle = 1, calls again as (0,1)
Third call 0,1 --> enters if, middle = 0, calls again as (0,0)
Fourth call 0,0 --> does not enter if, back to third call
Third call 0,1 --> calls as middle+1,high which is (1,1)
Fifth call 1,1 --> does not enter if, back to third call
Third call 0,1 --> calls the string you didn't expect

可以继续,但这是你不期望的字符串被执行的地方。

答案 2 :(得分:2)

您也可以打印highlow的值。跟随递归会容易得多。

答案 3 :(得分:1)

尝试打印middle变量的值。

最佳实践要求您不要在没有任何变量输出的“函数前”样式调试消息中进行编码。

答案 4 :(得分:1)

4行输出低= 0,中间= 0,高= 1所以调用mergesort(中间+ 1,高)不会打印任何内容(1&lt; 1为假)

答案 5 :(得分:1)

以下缩进对应于递归:

mergesort(0, 7)
    middle=3
    "Before the 1st Call"
    mergesort(0, 3)
        middle=1
        "Before the 1st Call"
        mergesort(0, 1)
            middle=0
            "Before the 1st Call"
            mergesort(0, 0)
                (0 < 0) is false so return
        "After the 1st Call"
        mergesort(1, 1)
            (1 < 1) is false so return
        "After the 2nd Call"

        etc ...

答案 6 :(得分:1)

运行这段代码来理解递归。我已经考虑了控制台中的堆栈深度。希望它易于理解!

    #include "stdafx.h"
    #include <iomanip>
    using namespace std;
    static int stackdepth=0;
    void mergesort(int[],int,int);
    void merge(int[],int,int,int);
    void  space(int);
    int main(int argc,char *argv[])
    {
        int a[8]={5,7,1,4,9,3,2,0};
        mergesort(a,0,7);
        for(int i=0;i<10;i++)
    //  cout<<a[i]<<endl;
        return 0;
    }
    void mergesort(int a[],int low,int high)
    {
        int mid;

        if(low<high)
        {

            mid=(low+high)/2;
            space(stackdepth);
            cout<<"First Recursion Enter";
            cout<<" Low :"<<low<<" Mid :"<<mid<<endl;
            stackdepth++;
            mergesort(a,low,mid);
            stackdepth--;
            space(stackdepth);
            cout<<"First Recursion Exit";
            cout<<" Low :"<<low<<" Mid :"<<mid<<endl;
            space(stackdepth);
            stackdepth++;
            cout<<"Second Recursion Enter";
            cout<<" Mid+1 :"<<mid+1<<" High :"<<high<<endl;
            mergesort(a,mid+1,high);
            stackdepth--;
            space(stackdepth);
            cout<<"Second Recursion Exit";
            cout<<" Low :"<<mid+1<<" High :"<<high<<endl;
            space(stackdepth);
            cout<<"Merge Low :"<<low<<" Mid :"<<mid<<"High :"<<high<<endl;
            merge(a,low,mid,high);
            cout<<endl;
            space(stackdepth);
            cout<<"------------------------------------------------------------------------------------------"<<endl;
        }
    }
    void space(int stackdepth)
    {
        for(int i=0;i<stackdepth;i++)
        cout<<"                     ";

    }
    void merge(int a[],int low,int mid,int high)
    {
    //  cout<<endl;
    //  cout<<"Merging Begins"<<endl;
        int b[8];
        int i,k,j;
        i=low;k=low;j=mid+1;
        while(i<=mid && j<=high)
        {
            if(a[i]<a[j])
            {
                    b[k++]=a[i++];
            }
            else
            {
                b[k++]=a[j++];
            }
        }
        while(i<=mid)
            b[k++]=a[i++];
        while(j<=high)
            b[k++]=a[j++];
        space(stackdepth);
        for(int i=low;i<=high;i++)
        {
            a[i]=b[i];
        cout<<a[i]<<" ";
        }
            //cout<<"Low :"<<low<<" Mid :"<<mid<<" High :"<<high<<endl;
        //  cout<<"Merging Ends"<<endl;
        //  cout<<endl;
    }

答案 7 :(得分:0)

Merge Sort使用递归算法创建一个高度为Log N的完整二叉树,N是该树的节点数(这就是为什么效率如此高)。在下一个图像中,您可以逐步查看针对您的案例的此算法的执行流程,以及创建的二叉树(我认为这是理解其工作原理的最佳方式):

Binary tree that is generated using Merge Sort with an array of 8 positions

Merge Sort的作用是将数组递归地分成两半,首先到达最低的一半,直到我们到达一个单一元素,然后从最近到达的最低元素中分离出更高的元素。这就是为什么它每次调用时自己调用两次,以便创建一个完整的二叉树,当我们到达一个单元(带有叶节点)时停止,并且只有当我们有两个(带有父节点)时才合并。在下图中,您可以逐步查看数组是如何递归拆分的:

Step by step division of an array of 8 elements using Merge Sort

答案 8 :(得分:0)

转到eclipse调试工具。按照步骤,您将找到规则双递归。那就是我做的事。