从递归版归并排序算法看递归函数连续两次调用自己函数每步如何返回,看递归和栈的关系

来源:互联网 发布:java.equal例子 编辑:程序博客网 时间:2024/06/06 02:30
 这个题目牵涉问题比较多,主要涉及几个关键词:递归函数,栈,归并排序算法(这里使用递归实现),调用和返回。

首先解释一下关键词。1、递归函数是直接调用自己或通过一系列的调用语句间接地调用自己的函数。递归函数必须至少有一个退出条件,即不再继续调用自己而是返回值退出。
2、栈,栈既是一种先进后出的数据结构,也可以指具有以上属性的动态内存区域
3、递归函数和栈的关系:递归函数的运行其实就是前行和退回。递归过程退回的顺序使前行顺序的逆序。在退回过程中,可能要执行某些动作,包括恢复在前行过程中存储起来的某些顺序。这种退回返回顺序很符合栈数据结构特征,所以编译器就是用栈实现递归,也就是说,对于每一层递归,函数的局部变量、参数值以及返回地址 都被压人栈中。在退回的阶段,位于栈顶的局部变量、参数值和返回地址都被弹出,用于返回调用层次中执行代码的其他部分。
4、归并排序算法: 归并排序是利用递归和分而治之的技术将数据序列划分成为越来越小的半子表,再对半子表排序,最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列。
再来看看归并排序算法实现:(以大话数据结构为参考)

void MergeSort(SqList *L){     MSort(L->r,L->r,1,L->length);}void MSort(int SR[],int TR1[],int s, int t){    int m;    int TR2[MAXSIZE+1];    if(s==t)        TR1[s]=SR[s];    else    {        m=(s+t)/2;              /* 将SR[s..t]平分为SR[s..m]和SR[m+1..t] */        MSort(SR,TR2,s,m);      /* 递归地将SR[s..m]归并为有序的TR2[s..m] */        MSort(SR,TR2,m+1,t);    /* 递归地将SR[m+1..t]归并为有序的TR2[m+1..t] */        Merge(TR2,TR1,s,m,t);   /* 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t] */    }}

Msort()函数是将顺序表依次一分为二划分为有序小序列,然后使用merge()函数合并小序列。merge函数实现思想参考这里。大话数据结构上说每次递归返回都会执行Merge()函数。大概本身对递归的调用不清楚,所以对这句话就很不理解, msort函数中重要的三句话:
void MSort(int SR[],int TR1[],int s, int t)
{
……..省略了其他的
MSort(SR,TR2,s,m); (1)
MSort(SR,TR2,m+1,t); (2)
Merge(TR2,TR1,s,m,t); (3)
}
为什么执行到1返回后不是2而是3呢? 也就是说1,2,3的执行顺序到底是怎样的,参看知乎上一个问题一个递归函数里连续调用两次递归函数本身,具体到每一步时是怎样调用和返回的?
为了理解调用两次的递归函数如何执行和返回,先来看个简单的调用一次的递归函数执行和返回顺序:用递归实现从0到n打印数字:
void printN(int n)
{
if(n>0)
{
Pri(n-1);
}
printf(“%d\n”,n);
}
int main()
{
printN(4);
system(“pause()”);
return 0;
}
这个简单的打印函数假设传参为4,函数执行顺序为:
printN(4)–printN(3)–printN(2)–printN(1)–printN(0)-不再满足n>0,开始执行printf函数 – printf(“%d\n”,0)–printf(“%d\n”,1)–printf(“%d\n”,2)–printf(“%d\n”,3)–printf(“%d\n”,4) 。从这里可以看书,递归函数参数的传递就是入栈出栈的关系。
那下面就来看一下连续调用两次的归并排序算法调用和返回顺序。比如顺序表L的元素为50,10,90,30。标号为1,2,3,4. 调用顺序(我省略数组,只写起始终止序号) 见下图1,如果你看这个调用顺序看晕了, 图2使用栈中进栈出栈顺序模拟递归的调用。图1图2,(1,1),(2,2)画线push为pop
使用大话数据结构的例子,线性表L的元素为[50,10,90,30,70,40,80,60,20],长度为9时,从栈的角度看msort调用顺序见图3(我依然只写调用功能的起始终止序号)。图3,铅笔箭头是递归函数前行顺序,红色是递归函数退回顺序
图4,栈中标号和元素merge对照图
从图3可以看出,其实就是要退回两次再去调用merge函数,因为调用了自己两次。自己在递归这里纠结了很久,不知道每一步怎么返回,参数怎么传递,通过一步步调试断点加栈的图示花了一天时间终于明白一点,我力图用图2,3,4描述清楚两次调用自己的递归函数的前行退回参数传递等情况,文中肯定会有用词不精确描述不清楚的情况,欢迎指正和交流。

0 0
原创粉丝点击