递归理解

178 阅读2分钟

一.分组问题

题目:用递归法计算从n个人中选选k个人组成一个委员会的不同组合数。 分析:由n个人里选k个人的组合数 = 由n-1个人里选k个人的组合数 + 由n-1个人里选k-1个人的组合数;当n = k或k = 0时,组合数为1。

#include <iostream>
using namespace std;
/*双递归的执行过程,可以借助二叉树结构来描述*/
int comm(int n, int k) {//排除n < k的情况
	if (n < k) {
		return 0;
	}
	else if(n == k || k == 0)//递归函数的出口
	{
		return 1;
	}
	else {//两个递归深入点,但是同函数中左右深入点的深入层次并不一定相同
		return comm(n - 1, k) + comm(n -1, k - 1);
	}
}

int main() {
	int n, k;
	cin >> n >> k;
	cout << comm(n, k);
	system("pause");
	return 0;
}

 

 

二.双递归函数的执行顺序

双递归的执行过程,可以借助二叉树结构来描述:

二叉树描述双递归执行过程

  1. 程序从首先左端的递归深入点进入,再次调用函数(4, 3),再次从左侧深入点(3, 3)深入。此时得到第一个递归的终止条件:n = k,组合数为1,将1返回给父级节点;在此之前,(4, 3)右端的函数深入点并未执行,而是作为没有执行完毕的函数被保存在栈中。此时则是执行(4, 3)右侧递归深入点(3, 2)函数。剩余执行过程由红色箭头标识,不再一一说明。

  2. 无论执行左侧还是右侧函数,调用时必定先执行左子树所代表的函数。若此时这棵左子树依然存在后代,则先执行它的左子树函数,然后循环此逻辑;直到左子树没有下级节点时,那么就返回它的上级节点,来执行上级节点的右子树函数。

  3. 使用二叉树表示的双递归函数执行过程中,任何一个节点若存在子节点,那么它的左后代与右后代必然是同时存在的。原因就在于在递归调用函数时,两个调用自身的函数也是同时存在的。

  4. 当一个节点找到递归出口时,那么与它有相同父级节点的兄弟节点不会停止递归深入,直到得到一个可以返回的值为止(即找到递归出口)。所以,叶子结点返回值逐级累加,所有的返回值之和,就是题目所求结果,也就是整个递归最外层函数的返回值即函数调用者(此题为main函数)所需要的结果。

  5. 可以使用断点调试对变量进行监视,从而更好地理解双深入点递归函数的执行过程。