非递归化
非递归化就是指不引入堆栈等辅助空间进行计算的算法。更确切地说,非递归是指辅助的空间为O(1)的算法(辅助空间的规模和问题的输入规模无关)。引入堆栈的所谓非递归本质上还是递归,只不过原来由编译器做的事让你的程序来模拟实现了。
不是所有的可计算问题都可以由递归转化为递推的。例如著名的Ackermann函数,就没有递推算法,除非引入堆栈等辅助结构。尾递归是最简单的一类直接可以转化为递推的递归算法,另外还有很多方法将递归转化为递推,例如利用Cooper变换,反演变换,T1,T2变换,C变换,RR变换等等,在程序等价变换理论中对递归递推的变换已经研究的比较多了。
解决方法:
在递归算法中消除递归调用,使其转化为非递归算法。
- 采用一个用户定义的栈来模拟系统的递归调用工作栈。该方法通用性强,但本质上还是递归,只不过人工做了本来由编译器做的事情,优化效果不明显。
- 用递推来实现递归函数 。
- 通过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;
}
}