You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
4.4 KiB

2 years ago
package class17;
2 years ago
import java.util.HashSet;
2 years ago
import java.util.Stack;
public class Code02_Hanoi {
public static void hanoi1(int n) {
leftToRight(n);
}
// 请把1~N层圆盘 从左 -> 右
public static void leftToRight(int n) {
if (n == 1) { // base case
System.out.println("Move 1 from left to right");
return;
}
leftToMid(n - 1);
System.out.println("Move " + n + " from left to right");
midToRight(n - 1);
}
// 请把1~N层圆盘 从左 -> 中
public static void leftToMid(int n) {
if (n == 1) {
System.out.println("Move 1 from left to mid");
return;
}
leftToRight(n - 1);
System.out.println("Move " + n + " from left to mid");
rightToMid(n - 1);
}
public static void rightToMid(int n) {
if (n == 1) {
System.out.println("Move 1 from right to mid");
return;
}
rightToLeft(n - 1);
System.out.println("Move " + n + " from right to mid");
leftToMid(n - 1);
}
public static void midToRight(int n) {
if (n == 1) {
System.out.println("Move 1 from mid to right");
return;
}
midToLeft(n - 1);
System.out.println("Move " + n + " from mid to right");
leftToRight(n - 1);
}
public static void midToLeft(int n) {
if (n == 1) {
System.out.println("Move 1 from mid to left");
return;
}
midToRight(n - 1);
System.out.println("Move " + n + " from mid to left");
rightToLeft(n - 1);
}
public static void rightToLeft(int n) {
if (n == 1) {
System.out.println("Move 1 from right to left");
return;
}
rightToMid(n - 1);
System.out.println("Move " + n + " from right to left");
midToLeft(n - 1);
}
public static void hanoi2(int n) {
if (n > 0) {
func(n, "left", "right", "mid");
}
}
public static void func(int N, String from, String to, String other) {
if (N == 1) { // base
System.out.println("Move 1 from " + from + " to " + to);
} else {
func(N - 1, from, other, to);
System.out.println("Move " + N + " from " + from + " to " + to);
func(N - 1, other, to, from);
}
}
public static class Record {
2 years ago
public int level;
2 years ago
public String from;
public String to;
public String other;
2 years ago
public Record(int l, String f, String t, String o) {
level = l;
2 years ago
from = f;
to = t;
other = o;
}
}
2 years ago
// 之前的迭代版本,很多同学表示看不懂
// 所以我换了一个更容易理解的版本
// 看注释吧!好懂!
// 你把汉诺塔问题想象成二叉树
// 比如当前还剩i层其实打印这个过程就是
// 1) 去打印第一部分 -> 左子树
// 2) 打印当前的动作 -> 当前节点
// 3) 去打印第二部分 -> 右子树
// 那么你只需要记录每一个任务 : 有没有加入过左子树的任务
// 就可以完成迭代对递归的替代了
2 years ago
public static void hanoi3(int N) {
if (N < 1) {
return;
}
2 years ago
// 每一个记录进栈
2 years ago
Stack<Record> stack = new Stack<>();
2 years ago
// 记录每一个记录有没有加入过左子树的任务
HashSet<Record> finishLeft = new HashSet<>();
// 初始的任务,认为是种子
stack.add(new Record(N, "left", "right", "mid"));
2 years ago
while (!stack.isEmpty()) {
2 years ago
// 弹出当前任务
2 years ago
Record cur = stack.pop();
2 years ago
if (cur.level == 1) {
// 如果层数只剩1了
// 直接打印
2 years ago
System.out.println("Move 1 from " + cur.from + " to " + cur.to);
} else {
2 years ago
// 如果不只1层
if (!finishLeft.contains(cur)) {
// 如果当前任务没有加入过左子树的任务
// 现在就要加入了!
// 把当前的任务重新压回去,因为还不到打印的时候
// 再加入左子树任务!
finishLeft.add(cur);
2 years ago
stack.push(cur);
2 years ago
stack.push(new Record(cur.level - 1, cur.from, cur.other, cur.to));
2 years ago
} else {
2 years ago
// 如果当前任务加入过左子树的任务
// 说明此时已经是第二次弹出了!
// 说明左子树的所有打印任务都完成了
// 当前可以打印了!
// 然后加入右子树的任务
// 当前的任务可以永远的丢弃了!
// 因为完成了左子树、打印了自己、加入了右子树
// 再也不用回到这个任务了
System.out.println("Move " + cur.level + " from " + cur.from + " to " + cur.to);
stack.push(new Record(cur.level - 1, cur.other, cur.to, cur.from));
2 years ago
}
}
}
}
public static void main(String[] args) {
int n = 3;
hanoi1(n);
System.out.println("============");
hanoi2(n);
2 years ago
System.out.println("============");
hanoi3(n);
2 years ago
}
}