递归程序简洁,但是不是很好理解,当自己写的时候,每个人的递归设计可能还不一样,
递归程序的设计首先是接口的设计,明白每一层递归:
向下请求什么?
向上交付什么?
什么时候截止?
参数怎么传递?
下面分析几种递归的模式
一、自顶向下,逐层收拢,上层需要下层的结果
代码(1)
int feb(int n)
{
if (n==1||n==2)
{
return 1;
}
else
return feb(n-1)+feb(n-2);
}
这是最简单的斐波那契数列的递归形式,这是典型的自顶向下,递归的的开头是截止条件,上层需要下层的结果,然后每一层都处于等待状态,最后达到截止条件,逐层向上收拢交付。
代码(2)
Node *mergeLinkListRC(Node *pHead1,Node *pHead2)
{
if (pHead1==NULL)
{
return pHead2;
}
if (pHead2==NULL)
{
return pHead1;
}
Node *pCur=NULL;
if (pHead1->nDatanData)
{
pCur=pHead1;
pCur->next=mergeLinkListRC(pHead1->next,pHead2);
}
else
{
pCur=pHead2;
pCur->next=mergeLinkListRC(pHead1,pHead2->next);
}
return pCur;
}
这是合并两个有序链表的递归写法,比代码1看起来复杂一点,开头的两个判断仍然是截止条件,当递归调用自己的时候,必然会发生一个事情,就是参数的改变,同时如果本身有返回值,则需要利用这个返回值向上层交付,如果没有返回值,则可能是以引用参数传递的形式,让下层去改变这个引用
对于代码( 2 ),要将两个有序链表合并,每层函数的参数是两个头节点,返回值是这两个头结点应该在新链表中靠前的那个节点,同时还要改变这个靠前节点的next域;而改变这个next域需要下层提供一个结果,于是同样的问题,不一样的参数,让下一递归去解决,当到达截止条件的时候,逐层交付,那些处于等待状态的next域依次被改变;
代码中有个pCur指针,这个指针是个中间变量,用来保存那些需要改变next域的节点地址,同时这个pCur也作为一个返回值。这个递归是很巧妙的;
二、自顶向下,将问题的规模减小后直接return自己
这种类型的递归,与第一种相比,逻辑上缺少了收缩的过程,每一层的任务是将问题的规模减小
代码(3)
int binarySearch(int *pArray,int toFind,int i,int j)
{
if (i>j)
{
return -1;
}
if (i==j)
{
if (pArray[i]==toFind)
{
return i;
}
else
return -1;
}
if (itoFind)
{
return binarySearch(pArray,toFind,i,x-1);
}
else
{
return x;
}
}
}
这是二分查找的递归形式,这里是递归的另一种形式,将问题的规模减小,让下层去解决,上层并不执行任何东西,凡是看到return XXX(参数变化),这种大多是如此;三、自上而下,依次递减规模,带for循环
代码(4)
void test(char *pStr,int index)
{
int nLen=strlen(pStr);
if (index==nLen-1)
{
printf("%s\n",pStr);
}
for (int i=index;i
这是一个打印全排列的小程序,可以打印出一个字符串的全排列,这个代码的核心在于每次test结束时候字符串的排列是否会改变?
分成两个问题:
如果内层test不改变,那么外层test不改变,这个很明显;
问题变成如果内层test改变,那么在哪儿改变?这与外层test矛盾
所以test对pStr并不改变,得到这个结论,这个递归就容易理解了,这段程序就是一个规模递减的递归调用
四、二叉树类型的
二叉树与递归有着天然的切合度
代码(5)
void createBinaryTree(int *pArray,int nSize,int index,Node *&pCur)
{
if (index>nSize-1)
{
pCur=NULL;
return;
}
else
{
if (2*index+1>nSize-1)//是叶子
{
pCur=new Node;
pCur->left=NULL;
pCur->right=NULL;
pCur->nData=pArray[index];
return;
}
else if (2*index+2>nSize-1)//只有左节点
{
pCur=new Node;
pCur->nData=pArray[index];
pCur->left=NULL;
pCur->right=NULL;
createBinaryTree(pArray,nSize,2*index+1,pCur->left);
}
else
{
pCur=new Node;
pCur->nData=pArray[index];
pCur->left=NULL;
pCur->right=NULL;
createBinaryTree(pArray,nSize,2*index+1,pCur->left);
createBinaryTree(pArray,nSize,2*index+2,pCur->right);
}
}
}
void traverse(Node *pRoot)
{
if (pRoot==NULL)
{
return;
}
else
{
cout<nData<<'\t';
traverse(pRoot->left);
traverse(pRoot->right);
}
}
第一段是代码是利用数组创建一颗二叉树,传递进去的是一个二叉树的根节点地址
createBinaryTree(int *pArray,int nSize,int index,Node *&pCur)
index是数组的标号,pCur是当前节点的地址,在递归代码中我们经常要传递指针,但是要考虑清楚我们是不是应该传递引用
原则是这样的,如果我们仅仅通过这个指针进行取值操作,那么没必要引用,如果要改变这个指针的指向,比如让他指向一个新的节点,代码中有类似 pCur=new Node 这样的语句,那么就要传递指针的引用
第二段代码是最最普通的二叉树的递归遍历,属于规模递减形式的,很容易理解。