面试题(十二)谈谈递归

递归程序简洁,但是不是很好理解,当自己写的时候,每个人的递归设计可能还不一样,

递归程序的设计首先是接口的设计,明白每一层递归:

向下请求什么?

向上交付什么?

什么时候截止?

参数怎么传递?

下面分析几种递归的模式

一、自顶向下,逐层收拢,上层需要下层的结果

代码(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->nData<pHead2->nData)
	{
		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 (i<j)
	{
		int x=(i+j)/2;
		if (pArray[x]<toFind)
		{
			return binarySearch(pArray,toFind,x+1,j);
		}
		else if(pArray[x]>toFind)
		{
			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<nLen;i++)
	{
		swap(&pStr[i],&pStr[index]);
		test(pStr,index+1);
		swap(&pStr[i],&pStr[index]);
	}
}

这是一个打印全排列的小程序,可以打印出一个字符串的全排列,这个代码的核心在于每次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<<pRoot->nData<<'\t';
		traverse(pRoot->left);
		traverse(pRoot->right);
	}
}

第一段是代码是利用数组创建一颗二叉树,传递进去的是一个二叉树的根节点地址

createBinaryTree(int *pArray,int nSize,int index,Node *&pCur)  

index是数组的标号,pCur是当前节点的地址,在递归代码中我们经常要传递指针,但是要考虑清楚我们是不是应该传递引用

原则是这样的,如果我们仅仅通过这个指针进行取值操作,那么没必要引用,如果要改变这个指针的指向,比如让他指向一个新的节点,代码中有类似  pCur=new Node  这样的语句,那么就要传递指针的引用

第二段代码是最最普通的二叉树的递归遍历,属于规模递减形式的,很容易理解。






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值