C++易忽略点学习递归函数的调用超清晰分析(二)

来源:互联网 发布:学历网络教育报考时间 编辑:程序博客网 时间:2024/04/30 03:00

递归函数学习。

窃以为,在我接触的C语言程序中,递归函数是最复杂的。递归函数看似简单,通过函数自身调用实现无限光明的功能,但是往往调试起来,错误百出,而且在某一环稍微出错,结果千差万别,因此,在写递归函数时,思路必须十分清晰,否则,中枪的肯定是你!

被誉为“算法圣经”的Introduction to Algorithms, Thomas H. Cormen中第二章讲述了归并排序的函数,是使用递归函数的经典例子。看完该例后,我跃跃欲试想把它用C语言实现。该书中,所有的数组都是A[1...n],是从下标为1开始的。

1、假设A[p...q], A[q+1...r]是两个分别排好序的数组,进行合并即可得排好序的A[p...r].



2、使用递归函数的归并排序:

使用这两个函数,就能实现复杂度为O(nlgn)的排序。

具体步骤如下:


算法的描述非常清晰,我本想实现之应该很轻松,但是结果让我大为惊讶,竟然调试了两个多小时之久。

下面描述我的做法:

1、写好程序整个框架。

做算法和写程序是完全的两码事,光会算法还不足以产生生产力,它只解决了理论上的问题,要把它付之实现还有很长的路要走。所以必须先写好程序的框架,如如何从文件中输入整型数组,如何实现算法中的∞。

输入整型数组的问题在写insert_sort.c(插入排序)时就已经研究出来了(见2013.4.15 1、求整形数组的长度),而∞的表示该如何呢,记得最近在看的一本书The Standard C Library,P.J.Plauger中讲解了<limits.h>的使用,有一个INT_MAX的宏定义,其含义为整型变量的最大值。可以使用这个宏来代替∞。

2、实现merge_sort(),merge()函数。

由于算法导论中描述的算法中数组的小标均是从1开始,所以用C实现必须考虑下标的问题。

下面后面备注了change的说明某些值和原算法中有1的改变。

void merge(int *A, int p, int q, int r)

{

int n, m;

int i, j;

int L[SIZE], R[SIZE];

static int k;

= q - p + 1;

= r - q;

for(i=0; i< n; i++)//change

L[i] = A[p+i];//change

for(j=0; j<m; j++)

R[j] = A[q+j+1];//change

L[n] = INT_MAX;

R[m] = INT_MAX;

= 0;

= 0;

for(k=p; k<r+1; k++)

{

if(L[i]<R[j])

{

A[k] = L[i];

++;

}

else

{

A[k] = R[j];

++;

}

}

};

void merge_sort(int *A, int p, int r)

{

int q;

if(p<r)

{

= (p+r)/2;

merge_sort(A, p, q);

merge_sort(A, q+1, r);

merge(A, p, q, r);

}

};

3、调试

上面的代码时调试正确后的表示。开始有一处有问题,但是隐藏的较深,我仔细分析了整个递归函数的过程才发现问题所在。

merge_sort()中,我这样定义q

static int q = 0;

结果导致了每次调用merge_sort()都会定义一次q=0,然而我曾经看过一本书,书上写道

static int n = 0 

只会定义一次n,可是这里竟然出现了问题。static这个问题下次一定要解决!!

虽然将代码改为

int q;

即可。但是为了找到这个隐藏的错误我花了近两个小时的时间,可见Debug是一门很深的学问。值得欣慰的是,我首先写框架时就将代码测试了一遍,所以我可以迅速锁定错误是merge_sort()merge(),首先将下标问题解决了,但是还是出现了错误,我又画了一张递归函数调用的步骤图,


第一次如此清晰的描述递归函数的过程,原来抽象的递归函数现在十分的具体了。我按照这些步骤一步一步调试代码,终于找出了q这个值的问题,因为在第2步时,q应为1,但是它显示为0,所以我就怀疑是初始化的问题,结果还真是。务必记住:千万不要在递归函数中初始化值为0,除非它每次都必须是0,不然每次调用时都会把它初始化为0,这不是我们的初衷。