带返回值的递归转为非递归

带返回值的递归转为非递归与不带返回值的递归转为非递归相似。不同的地方是有个如何保存和使用返回值的问题。

以组合数计算为例。计算公式是,C(n, k) = C(n-1, k) + C(n-1, k-1)。写成递归的代码是,

c 复制代码
func cbn(n, k)
{
	if(n==k||k==0) return 1;
	else if(k==1) return n;	
	else return cbn(n-1, k)+cbn(n-1,k-1);
}

这个函数有返回值。如何改为非递归呢。分解成任务仍然是2个,C(n-1, k) 和 C(n-1, k-1)。但这里还有个求和问题。

为了便于观察,把C(n-1, k) 和 C(n-1, k-1)的返回值逐个分配在alloc数组a\[\]里面。ai指向a\[\]中空闲的下一个可以分配的索引值。并且增加一个特别的H()任务来求和,H()任务和C(n-1, k) 和 C(n-1, k-1)压入同一栈中,所以H()任务用一个不可能出现的n值,即-1,来表示 和 C()任务的区别。注意压栈的顺序是逆序即H(), C(n-1, k-1),C(n-1, k)。针对特定问题的优化这里就不讨论了:

c 复制代码
func cbn(n, k)
{
	if(k<0 || n<k) return 0;

	ai=0;
	a[ai]=0;
	n[top]=n;
	k[top]=k;
	r[top]=ai++;

	++top;
	while(top>0) {
		--top;
		n= n[top];
		k= k[top];
		r= r[top];

		if(n==-1) {
			print "r=",r, "ai=",ai;

			a[r]=a[ai-1]+a[ai-2];
			ai -= 2;
		}
		else if(n==k||k==0) a[r]=1;
		else if(k==1) a[r]=n;	
		else {
			n[top]=-1;
			k[top]=0;
			r[top++]=r;

			n[top]=n-1;
			k[top]=k-1;
			a[ai]=0;
			r[top++]=ai++;
			print "C(",n-1,",",k-1,") return @a[", ai-1,"]";

			n[top]=n-1;
			k[top]=k;
			a[ai]=0;
			r[top++]=ai++;
			print "C(",n-1,",",k,") return @a[", ai-1,"]";
		}
		for(i=0; i<ai; i++) print a[i],"\b";
		print "";
	}
	return a[0];
}

入栈的r指向给对应的组合数计算返回值分配的a\[\]数组位置。代码加入了适量的打印语句,以便观察每一步的运行。

以C(6,3)为例,看下运行结果:

clike 复制代码
print cbn(6,3);
C( 5 , 2 ) return @a[ 1 ]
C( 5 , 3 ) return @a[ 2 ]
0 0 0
C( 4 , 2 ) return @a[ 3 ]
C( 4 , 3 ) return @a[ 4 ]
0 0 0 0 0
C( 3 , 2 ) return @a[ 5 ]
C( 3 , 3 ) return @a[ 6 ]
0 0 0 0 0 0 0
0 0 0 0 0 0 1
C( 2 , 1 ) return @a[ 7 ]
C( 2 , 2 ) return @a[ 8 ]
0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0 1
0 0 0 0 0 0 1 2 1
r= 5 ai= 9
0 0 0 0 0 3 1
r= 4 ai= 7
0 0 0 0 4
C( 3 , 1 ) return @a[ 5 ]
C( 3 , 2 ) return @a[ 6 ]
0 0 0 0 4 0 0
C( 2 , 1 ) return @a[ 7 ]
C( 2 , 2 ) return @a[ 8 ]
0 0 0 0 4 0 0 0 0
0 0 0 0 4 0 0 0 1
0 0 0 0 4 0 0 2 1
r= 6 ai= 9
0 0 0 0 4 0 3
0 0 0 0 4 3 3
r= 3 ai= 7
0 0 0 6 4
r= 2 ai= 5
0 0 10
C( 4 , 1 ) return @a[ 3 ]
C( 4 , 2 ) return @a[ 4 ]
0 0 10 0 0
C( 3 , 1 ) return @a[ 5 ]
C( 3 , 2 ) return @a[ 6 ]
0 0 10 0 0 0 0
C( 2 , 1 ) return @a[ 7 ]
C( 2 , 2 ) return @a[ 8 ]
0 0 10 0 0 0 0 0 0
0 0 10 0 0 0 0 0 1
0 0 10 0 0 0 0 2 1
r= 6 ai= 9
0 0 10 0 0 0 3
0 0 10 0 0 3 3
r= 4 ai= 7
0 0 10 0 6
0 0 10 4 6
r= 1 ai= 5
0 10 10
r= 0 ai= 3
20
20

可以看到,这个计算次序和递归解法是完全一样的。

相关推荐
今晚打老虎5 小时前
c++之提高A(前缀和)(第三课)
数据结构·c++·算法
不会就选b6 小时前
算法日常・每日刷题--<贪心>26
数据结构·算法
Logic10110 小时前
C语言/数据结构位运算题解:异或XOR找出书柜中编号只出现一次的那一层——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
青山木10 小时前
Hot 100 --- 下一个排列
java·数据结构·算法·leetcode
SelectDB技术团队12 小时前
Apache Doris 高性能 Open Lake Variant 读写技术解析(含对比数据)
数据结构·数据库·clickhouse·日志·apache doris·selectdb·湖仓架构升级
kiracrimson12 小时前
希尔排序:让插入排序不再怕逆序
数据结构·算法·排序算法
Logic10112 小时前
C语言/数据结构位运算题解:异或XOR找出星球探险中的“独特能量石“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
hnjzsyjyj15 小时前
洛谷 P1339:[USACO09OCT] Heat Wave G ← Bellman-Ford 算法
数据结构·bellman-ford 算法
SelectDB技术团队16 小时前
ClickHouse 存日志的能力边界:并发、检索与 trace 回放的实测对照
数据结构·数据库·clickhouse·日志分析·apache doris·日志存储
垆边人似月.16 小时前
华为算法题:最长连续递增子序列的变
数据结构·算法·leetcode