笑谈模拟递归

摘自《Java数据结构和算法》:

        递归和栈之间有一种紧密的联系。事实上,大部分的编译器都是使用栈来实现递归的。正如我们曾提到过的,当调用一个方法的时候,编译器会把这个方法的所有参数及其返回地址(这个方法返回时到达的地方)都压入栈中,然后把控制转移给这个方法。当这个方法返回的时候,这些值退栈。参数消失了,并且控制权重新回到返回地址处。

大家应该都知道高斯的故事,说是有一天他的老师比特纳说:“今天给大家出一道算术题,谁算完,就可以先回家吃饭!”说完,他在黑板上写了一道算数题,题是这样的:“1+2+3+4+5+……+100=?”同学们都低头做题,老师开始看起了小说,可没等他看上两页,就听见小高斯说:“报告老师,我做完了。”比特纳头也没抬,就说:“这么快就做完了,肯定不对,回去重做。”高斯却说:“不会错的,肯定是5050。”老师听到这个答案非常惊讶,因为答案的确是5050。小高斯解释道:“我发现这许多数中,一头一尾两个数相加的和都是一样的,1加100是101;2加99是101;3加98是101……50加51也是101,就是说一共有50个101,因此很容易就能算出答案是5050。”

现在我们知道除了用这种方法,我们还可以尝试用递归来完成,当然在实际应用中简便简便,高斯的算法效率是非常高的,我们不想也没有必要去把问题搞复杂,但今天我们有人专门用递归来做,是为了给大家引入一个递归的例子,更好的去理解递归,为了再深入理解递归到底是怎么实现的,我们就来模拟一下这道数学题目的递归过程。

我们以累加4+3+2+1为例:

我们前面讲了递归与栈是因为我们在模拟递归的时候往往用到栈,所以在前面做一个铺垫。我们在用递归解4+3+2+1的时候,我们是不是一直先回溯到1,然后再递推到4?没错,递归就是一个回溯然后递推的过程,当我们回溯到1的时候我们返回,回溯到4的时候,进行累加以后程序整个递归过程也就结束了。我们给每一个值,比如在这里是4,3,2和1都另附上一个地址,由于递归到4的时候整个递归过程也就结束了,所以它显得有点特殊,特殊的我们当然要把它区分出来,“嘿,你是不是歧视我”,“嘿,我就是歧视你”,所以我们把4附上一个地址6(你也可以附上其它杂七杂八的数字,我这里赋为6),然后为它安家,也就是入栈,然后将其余的不特殊的数字也就是1,2,3,每次到这个数字,我们都给他们附上一个地址4,没错,地址和数字他们是一起的,生是它的字,死是它的魂,是一个整体,然后给他们安家,也就是入栈。

但是!好景不长!拆迁队来了,说是他们每个人的房子都是违建的!由于4比较特殊,家住的比较远,住的6号区,所以拆迁队暂时不去拆它家,一看好几个都住在4号区,立马火速敢去拆迁,1,2,3哭天喊地也没用,4在那边由于自己特殊,得意洋洋,却不知马上要向它下手,“因为咋是拆迁队啊!”拆迁队如此说道,拆迁队从人数最少的开始拆,所以从1开始拆,不过政府还是够意思的,给了点钱,1就走了,也就是出栈,拆迁队还要负责干一个活,统计走的人数,刚开始拆迁队出了点错误,默认走的人数初始值为1,所以这次不用拿笔写在名为“theAnswer”的纸上,然后又到了2,拆迁队怕出错,所以这次就在给钱拆迁之前,在1后面写上了+2,然后同理拆到3,在2后面写上+3,拆完4号区以后,就跑去6号区拆房,那4个人什么都没说啊,什么都没拿,突然在拆迁队的面前消失了,拆迁队吓得赶紧在3后面写上+4,自此以后,拆迁队再也不敢拆迁(递归结束)。

看完故事再看一下全过程:

Enter a number:4

现栈:
top→ n-->4 地址-->6

现栈:
top→ n-->3 地址-->4
     n-->4 地址-->6

现栈:
top→ n-->2 地址-->4
     n-->3 地址-->4
     n-->4 地址-->6

现栈:
top→ n-->1 地址-->4
     n-->2 地址-->4
     n-->3 地址-->4
     n-->4 地址-->6

现程序回溯到了1,弹出栈顶,theAnswer赋值为1

栈顶现在指向的是: 2
将栈顶元素的值加到theAnswer中,累加:
现在theAnswer的值为: 1+2=3

栈顶现在指向的是: 3
将栈顶元素的值加到theAnswer中,累加:
现在theAnswer的值为: 3+3=6

栈顶现在指向的是: 4
将栈顶元素的值加到theAnswer中,累加:
现在theAnswer的值为: 6+4=10

所以最终累计的结果就是:10
 

将数字 跟 地址附在一起然后安家的theNumber类:

/**
 * 带n和地址的家 
 * @author Administrator
 *
 */
public class Number {
	public int n;
	public int returnAddress; 
	
	public Number(int nn,int ra){
		n = nn;
		returnAddress = ra; 
	}
}

安家当然得先有个家———

定义一个theHouse类,其实就是个栈,本质是个数组:

/**
 * House类栈
 * @author Administrator
 *
 */
public class House {
	private Number[] theStack; 
	private int maxSize;
	private int top;
	
	public House(int m) {
		maxSize = m;
		theStack = new Number[maxSize];
		top=-1;
	}
	
	//入栈
	public void push(Number p) {
		theStack[++top] = p;
	}
	
	//出栈
	public Number pop() {
		return theStack[top--];
	}
	
	//获取栈顶元素
	public Number peek() {
		if(top!=-1)
		return theStack[top];
		else return null; 
	}
	
	public void display() {
		int i=top;
		System.out.println("现栈:");
		if(i==top) {
			System.out.print("top→ ");
			System.out.println("n-->"+theStack[i].n+" 地址-->"+theStack[i].returnAddress);
		}
		for(i=top-1;i>=0;i--) {
			System.out.println("     n-->"+theStack[i].n+" 地址-->"+theStack[i].returnAddress);
		} 
		System.out.println();
	}
}

拆迁队要来拆迁了!

import java.io.*;

public class StackTriangleApp {
	
	static int theFirst;//输入值
	static int theAnswer;//答案
	static int codePart;//第几个步骤
	static House theHouse; //创建一个栈
	static Number theNumber;//保留特定n值和其地址
	
	public static void main(String[] args) throws IOException{
		System.out.print("Enter a number:");
		theFirst = getInt();
		accumulate();
		System.out.println("所以最终累计的结果就是:"+theAnswer);
	}
	
	public static void accumulate() {
		theHouse = new House(1000);
		codePart = 1 ;
		while(step()==false);
	}
	
	//实现步骤
	public static boolean step() {
		switch(codePart) {
		
		case 1:
			theNumber = new Number(theFirst,6);//将第一个元素入栈,地址设为6,当地址为6时,完成所需
			theHouse.push(theNumber);//入栈
			codePart = 2;//跳到2判断其是否为1(我们有可能输入的是1)
			break;
		
		case 2:
			theNumber = theHouse.peek();
			theHouse.display();
			if(theNumber.n==1) {//如果是栈顶值为1,则将theAnswer赋为1,跳到步骤5
				System.out.println("现程序回溯到了1,弹出栈顶,theAnswer赋值为1\n");
				theAnswer = 1;
				codePart = 5;
			}else
				codePart = 3;//跳到步骤3入栈
			break;
		
		case 3:
			Number newParams =  new Number(theNumber.n-1,4);//地址设为4 
			theHouse.push(newParams);//入栈
			codePart = 2;//回到步骤2,判断栈顶是否到1
			break;
		
			//先加后出栈
		case 4:
			theNumber = theHouse.peek();//获取栈顶元素
			System.out.println("将栈顶元素的值加到theAnswer中,累加:");
			
			System.out.print("现在theAnswer的值为: "+theAnswer+"+"+theNumber.n+"=");
			theAnswer = theAnswer+theNumber.n;//将值累加到theAnswer中
			System.out.println(theAnswer+"\n");
			codePart = 5;//跳到步骤5,出栈
			break;
		
		case 5:
			theNumber = theHouse.peek();//获取栈顶元素
			codePart = theNumber.returnAddress;//获取地址,除了第一个元素地址为6(为6的时候结束运行),其余的元素我们都放在地址4里头
			theHouse.pop(); //出栈
			if(theHouse.peek()!=null)
			System.out.println("栈顶现在指向的是: "+theHouse.peek().n);
			break;	
		
		case 6:
			return true;	
		}
		return false;
	}
	
	
	
	//输入
	public static String getString() throws IOException{
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		String s = br.readLine();
		return s;
	}
	
	public static int getInt() throws IOException{
		String s =getString();
		return Integer.parseInt(s);
	}
	
}

结合代码对照上面的全过程,会容易理解得多。

 

搞了那么一大圈,还不是就是用栈搞定,所以下面是简化版本(可以更简化,但是要用到栈,我们的目的是说明递归的过程)

import java.io.*;

public class StackTriangleApp {
	
	static int theNumber;//输入值
	static int theAnswer;//答案
	static StackX theStack; //创建一个栈
	
	public static void main(String[] args) throws IOException{
		System.out.print("Enter a number:");
		theNumber = getInt();
		accumulate();
		System.out.println("所以最终累计的结果就是:"+theAnswer);
	}
	
	public static void accumulate() {
		StackX theStack = new StackX(1000);
		
		while(theNumber>0)
			theStack.push(theNumber--);
		
		while(!theStack.isEmpty())
			theAnswer+=theStack.pop();
	}

	//输入
	public static String getString() throws IOException{
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		String s = br.readLine();
		return s;
	}
	
	public static int getInt() throws IOException{
		String s =getString();
		return Integer.parseInt(s);
	}
	
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值