算法设计: 二、递归转非递归的转化方案(Hanio塔非递归算法) —— java实现 - 算法分析

非递归化

非递归化就是指不引入堆栈等辅助空间进行计算的算法。更确切地说,非递归是指辅助的空间为O(1)的算法(辅助空间的规模和问题的输入规模无关)。引入堆栈的所谓非递归本质上还是递归,只不过原来由编译器做的事让你的程序来模拟实现了。

不是所有的可计算问题都可以由递归转化为递推的。例如著名的Ackermann函数,就没有递推算法,除非引入堆栈等辅助结构。尾递归是最简单的一类直接可以转化为递推的递归算法,另外还有很多方法将递归转化为递推,例如利用Cooper变换,反演变换,T1,T2变换,C变换,RR变换等等,在程序等价变换理论中对递归递推的变换已经研究的比较多了。

解决方法:

在递归算法中消除递归调用,使其转化为非递归算法。

  1. 采用一个用户定义的栈来模拟系统的递归调用工作栈。该方法通用性强,但本质上还是递归,只不过人工做了本来由编译器做的事情,优化效果不明显。
  2. 用递推来实现递归函数 。
  3. 通过Cooper变换、反演变换能将一些递归转化为尾递归,从而迭代求出结果。

后两种方法在时空复杂度上均有较大改善,但其适用范围有限。

1.采用栈的非递归转化

package iterativeHanoi;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Scanner;
import java.util.Stack;

public class IterativeHanoi implements Serializable {

	private int number;			// 盘子编号
	private Character from;		// 出发点
	private Character pass;		// 转运点
	private Character to;		// 终点
	private static int times; 	// 记录移动的次数

	IterativeHanoi() {}

	IterativeHanoi(int number, char from, char pass, char to) {
		this.number = number;
		this.from = from;
		this.pass = pass;
		this.to = to;
	}

	
	public static void main(String[] args) {

		System.out.println("汉诺塔游戏开始");
		System.out.println("请输入盘子数:");

		Scanner s = new Scanner(System.in);
		final int n = s.nextInt();
		s.close();
		// 调用汉诺塔 首先将n传给number创建对象,以该对象进行函数调用
		IterativeHanoi iterHanoi = new IterativeHanoi(n, 'A', 'B', 'C');
		iterHanoi.showHanoi();
	}

	
	/**
	 * 演示过程函数
	 */
	private void showHanoi() {
		// 创建栈
		Stack<IterativeHanoi> s = new Stack<IterativeHanoi>();
		// 出栈对象 这里不能省略创建该对象而直接用this对象,因为出栈对象需要一个变量来接收,而this进入该函数时总是指当前对象,不可变。否则程序错误。
		IterativeHanoi iter_outer = CloneObj(this);

		// 出栈对象n==0同时栈为空,则退出循环
		while (!(iter_outer.number <= 0 && s.empty())) {
			// 入栈对象
			IterativeHanoi iter_inner = CloneObj(iter_outer);

			while (iter_inner.number > 0) {

				// 序列化对象入栈,解除对象实例引用
				s.push(CloneObj(iter_inner));
				iter_inner.number--;

				// 属性值交换
				iter_inner.swap(iter_inner.pass, iter_inner.to);
				/*
				 * 等价于:
				 * char temp = par_inner.pass; par_inner.pass = par_inner.to; par_inner.to = temp;
				 */
			}
			
			// 对象出栈
			iter_outer = s.pop();
			iter_outer.move();
			iter_outer.number--;

			// 属性值交换
			iter_outer.swap(iter_outer.from, iter_outer.pass);
			/*
			 *等价于:
			 * char temp = iter_outer.from; iter_outer.from = iter_outer.pass; iter_outer.pass = temp;
			 */
		}
		
	}
	

	/**
	 * 交换对象的两个属性值 采用反射机制
	 */
	public void swap(Character x, Character y) {
		
		try {
			// 创建反射对象
			Field field = x.getClass().getDeclaredField("value");
			// 修改属性权限
			field.setAccessible(true);
			
			char temp = x;
			try {
				// 属性赋值
				field.set(x, y);
				field.set(y,new Character(temp));
			} catch (IllegalArgumentException |IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
		} catch (NoSuchFieldException | SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	
	/**
	 * 移动盘子,将number号盘子从 ? 移到 ?
	 */
	private void move() {

		System.out.println("第" + (++times) + "次移动, 盘子" + number + "  " + from + "------->" + to);
	}

	
	/**
	 * 对象序列化
	 * 用于在对象赋值时,解除对象实例的引用
	 * 
	 * @param <T>	对象类型:泛型
	 * @param obj	待序列化对象
	 * @return		返回序列化后的对象
	 */
	private <T> T CloneObj(T obj) {
		T retobj = null;
		try { 
			/*
			 * 注意:输入还是输出不能弄错参照点,所有的输入输出的参照点是当前项目,而不是其他的文件。
			 */
			// 创建字节输入流
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			// 创建对象输入流 嵌套流
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			// 写入对象
			oos.writeObject(obj);
			// 对象输出流
			ObjectInputStream ios = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
			// 从对象输出流获取对象 此时对象已经过字节转换的序列化过程
			retobj = (T) ios.readObject();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return retobj;
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whitemeen太白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值