二路归并排序问题
我采用分治法进行自顶向下的算法设计
具体算法如下:
void MergeSortDC(SeqList R,int low,int high)
{//用分治法对R[low..high]进行二路归并排序
int mid;
if(low <high){//区间长度大于1
mid=(low+high)/2; //分解
MergeSortDC(R,low,mid); //递归地对R[low..mid]排序
MergeSortDC(R,mid+1,high); //递归地对R[mid+1..high]排序
Merge(R,low,mid,high); //组合,将两个有序区归并为一个有序区
}
}//MergeSortDC
我的问题如下:假如high=7;low=1;mid=3;他的初始化关键字为25,57,48,37,12,92,76。
(1)第一次程序执行是这样的吗?
mid=4; //分解
MergeSortDC(R,1,4); //递归地对R[low..mid]排序
MergeSortDC(R,5,7); //递归地对R[mid+1..high]排序
Merge(R,1,4,7); //组合,将两个有序区归并为一个有序区
然后在执行函数Merge( );对他进行归并。
(2)第一次程序执行是这样的吗?
mid=4; //分解
MergeSortDC(R,1,4); //递归地对R[low..mid]排序
然后又调用函数void MergeSortDC(SeqList R,int low,int high);直到他终止执行,但是我不知道他的终止条件是多少?
如果函数MergeSortDC(R,low,mid);执行结束之后。
开始执行函数MergeSortDC(R,mid+1,high);
而他又要调用函数void MergeSortDC(SeqList R,int low,int high);调用之后,那么它又要执行函数MergeSortDC(R,low,mid);了,
到这个地方我不知道该怎么去分析了。想请教各位高手,谢谢!
[解决办法]
楼主的第一次执行是对的
然后这样调用:
MergeSortDC(R,1,4);
MergeSortDC(R,1,2);
MergeSortDC(R,1,1);
MergeSortDC(R,2,2);
MergeSortDC(R,3,4);
MergeSortDC(R,3,3);
MergeSortDC(R,4,4);
注意这时才调用:
MergeSortDC(R,5,7);
依次如上调用, 如同树的深度优先遍历
[解决办法]
每调用一次 MergeSortDC(R,row,high) 只要low <high,在该函数内部都会调用一次Merge()
要想形象地观察递归函数是如何被执行的,可以借助画图来辅助理解:
找一A4纸,画一个很大的矩形,在其内部的顶端写上 low=1;mid=4;high=7;
现在这个大矩形就代表调用函数 MergeSortDC(R,1,7);
然后在这个大矩形里面再画两个比较大的和一个比较小的矩形,它们基本上要占满大矩形空间,
现在这三个矩形依次代表调用函数 MergeSortDC(R,1,4); MergeSort(R,5,7); Merge(R,1,4,7);
因此要在这三个矩形的内部顶端分别写上相应的 low=?;mid=?;high=?;
第三个小矩形不需要细化了,上面两个比较大的矩形内部用同样的方法嵌套再画小矩形 ...
注意如果发现某个矩形顶端的参数low> =high,那么是你画错了,它是不该出现的!
最后你可以从上到下整理出归并有序区函数的调用顺序如下:(臆猜之,请验证)
Merge(R,3,3,4);(1)
Merge(R,1,1,2);(2)
Merge(R,1,2,4);(3)
Merge(R,6,6,7);(4)
Merge(R,5,5,6);(5)
Merge(R,5,6,7);(6)
Merge(R,1,4,7);(7)
其实手工写出如上的代码,一样可以实现对数组R的排序(先排局部再排整体)
由于 Merge() 函数的使用前提是数组已经部分有序,所以它的调用顺序至观重要!
函数 MergeSortDC() 的作用也就是要实现以上的调用顺序
上图只能帮助你从逻辑上去理解函数调用和执行的顺序,但每个函数具体是怎样运行的?
这里又涉及到函数重入的问题,先讲明函数的形参(low,high)等同于局部变量(mid),它们在函数被重叠执行(函数还没执行完,又调用同一函数)会不会被自身的新值覆盖旧值。
我要说的是,即使你对这方面的问题感到很疑惑,也不要去怀疑上面你推导出来的东西!
如果你能真正理解普通函数的调用,就能理解递归,其实道理是完全一样的!
fun1(){} fun2(){fun1();} main(){fun2();} // 递归只因为它调用了“另一个自己”
递归函数的执行流程是一个树状结构,函数的调用和返回顺序不是线性排列的,
为什么你一开始知道要分成“(1)先调用”和“(2)再调用”两个分支来写函数序列,
后来却想不要在分支(1)和分支(2)里面也还是要继续开枝散叶呢?