递归改非递归

[简化0-1背包问题]

        我们有n件物品,物品i的重量为w[i]。每种物品要么完全放入背包要么不放入背包,问:能否从这n件物品中选择若干件放入背包,使之重量和恰好为s。

我们可以很容易地写出递归解:

bool ans(int s,int n){
	if(s == 0) return true;
	else if(s<0 || (s>0 && n<1)) return false;
	else if(ans(s-w[n-1], n-1)){
		cout<<w[n-1]<<" ";
		return true;
	}
	else{
		bool tmp=ans(s, n-1);
		return tmp;
	}
}

下面开始改写为非递归:

step1:定义调用栈帧:

注意到我们递归代码中每一层函数有两个参数,那么栈帧有两个参数:

struct Elem{
	int s,n;
	int rd	// 返回地址(何处调用了这个函数)
	
	// 栈帧构造函数
	Elem(int s,int n,int rd):
		s(n), n(n), rd(n) 
}; 

step2:将原问题入栈:

bool ans(int s,int n){
	// 生成一个栈 
	stack<Elem> st;
	// 创建一个栈帧 
	Elem x(s,n,0);
	// 入栈
	st.push(x);
	// 储存最近一次调用结果 
	bool ret; 
	...
}

step3:程序分析:

有n个递归调用就有n+1个递归出口,将原函数划分为n+1个区域

三个箭头即为三个出口,三个方框代表三个出口将函数划分的三个区域

 

 step4:划分标签并翻译:

将三个区域分别编号并翻译:

有n个递归调用就有n+2个标签

bool ans(int s,int n){
	stack<Elem> st;
	Elem x(s,n,0);
	st.push(x);
	bool ret;
	
	L0:
		s=st.top().s; n=st.top().n;
		if(s==0){
			ret=true;
			TODO return;
		}
		if((s<0) || (s>0 && n<1)){
			ret=false;
			TODO return;
		}
	// 第一次递归调用 
	
	L1:
		s=st.top().s; n=st.top().n;
		if(ret){
			cout<<w[n-1];
			ret=true;
			TODO return;
		}else{
	// 第二次递归调用 
		
	L2:
		TODO return;
	
	L3:
		// 递归退出,决定返回什么地址 
		...
}

 step5:goto实现递归调用:

rd=1与rd=2代表不同的调用出口(递归出口)

  step6:return全部到最后一个标签并实现递归退出:

bool ans(int s,int n){
	stack<Elem> st;
	Elem x(s,n,0);
	st.push(x);
	bool ret;
	
	L0:
		s=st.top().s; n=st.top().n;
		if(s==0){
			ret=true;
			goto L3;
		}
		if((s<0) || (s>0 && n<1)){
			ret=false;
			goto L3;
		}
	// 第一次递归调用 
        st.push((s-w[n-1], n-1, 1));
        goto L3;
	
	L1:
		s=st.top().s; n=st.top().n;
		if(ret){
			cout<<w[n-1];
			ret=true;
			goto L3;
		
		}
		else{
			// 第二次递归调用 
        	st.push((s, n-1, 2))
        	goto L0;
    	}
		
	L2:
		goto L3;
	
	L3:
		// 递归退出,决定返回什么地址 
		switch((x=st.top().rd)){
			case 0: st.pop(); return ret;
			case 1: st.pop(); goto L1;
			case 2: st.pop(); goto L2;
			default: assert(false);
		}
}

 来源:https://www.icourse163.org/learn/PKU-1002534001?tid=1470937462#/learn/announce

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值