2023年10月18日 星期三

how to solve the tower of hanoi by recursion versus simulated call stack?

  1.  
  2. /*
  3. TowerOfHanoi.java 遞迴版 及 模擬呼叫堆疊版 求解河內塔
  4.  
  5. > java TowerOfHanoi
  6. Hanoi Tower by Implicit Call Stack 遞迴版
  7. A:[3, 2, 1], B:[], C:[] 1: Move disk 1 from A to C
  8. A:[3, 2], B:[], C:[1] 2: Move disk 2 from A to B
  9. A:[3], B:[2], C:[1] 3: Move disk 1 from C to B
  10. A:[3], B:[2, 1], C:[] 4: Move disk 3 from A to C
  11. A:[], B:[2, 1], C:[3] 5: Move disk 1 from B to A
  12. A:[1], B:[2], C:[3] 6: Move disk 2 from B to C
  13. A:[1], B:[], C:[3, 2] 7: Move disk 1 from A to C
  14. A:[], B:[], C:[3, 2, 1]
  15.  
  16. Hanoi Tower by Explicit Stack 模擬呼叫堆疊版
  17. A:[3, 2, 1], B:[], C:[] 1: Move disk 1 from A to C
  18. A:[3, 2], B:[], C:[1] 2: Move disk 2 from A to B
  19. A:[3], B:[2], C:[1] 3: Move disk 1 from C to B
  20. A:[3], B:[2, 1], C:[] 4: Move disk 3 from A to C
  21. A:[], B:[2, 1], C:[3] 5: Move disk 1 from B to A
  22. A:[1], B:[2], C:[3] 6: Move disk 2 from B to C
  23. A:[1], B:[], C:[3, 2] 7: Move disk 1 from A to C
  24. A:[], B:[], C:[3, 2, 1]
  25. */
  26. import java.util.Stack;
  27.  
  28. public class TowerOfHanoi
  29. {
  30. static Stack stackA = new Stack<>(); // 柱A
  31. static Stack stackB = new Stack<>(); // 柱B
  32. static Stack stackC = new Stack<>(); // 柱C
  33. static int nDisks = 5; // Number of disks 盤數
  34. static int count = 0; // 步數
  35. static boolean printGoal = false; // 列印目標否
  36. static boolean printOperation = true; // 列印步驟否
  37. static boolean printStack = false; // 列印模擬呼叫堆疊否
  38. // 印n格空白
  39. public static void printSpaces(int n)
  40. {
  41. for(int i=0; i <= n -1; i++) System.out.print(" ");
  42. }
  43. // 印柱A,柱B,柱C堆疊,前面n層內縮
  44. public static void printStacks(int n)
  45. {
  46. StringBuilder sb = new StringBuilder();
  47. sb.append("A:" + stackA);
  48. sb.append(", B:" + stackB);
  49. sb.append(", C:" + stackC);
  50. System.out.println();
  51. printSpaces(n*8); // 每層內縮8格
  52. System.out.print(sb.toString());
  53. }
  54. // 搬移柱from頂一個盤子到柱to
  55. public static void transfer(char from, char to)
  56. {
  57. if(from=='A' && to=='B') stackB.push(stackA.pop());
  58. if(from=='A' && to=='C') stackC.push(stackA.pop());
  59. if(from=='B' && to=='A') stackA.push(stackB.pop());
  60. if(from=='B' && to=='C') stackC.push(stackB.pop());
  61. if(from=='C' && to=='A') stackA.push(stackC.pop());
  62. if(from=='C' && to=='B') stackB.push(stackC.pop());
  63. }
  64. // 遞迴版解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
  65. public static void solveHanoi(int n, char source, char auxiliary, char target)
  66. {
  67. if(printGoal)
  68. {
  69. System.out.println();
  70. printSpaces((nDisks - n)*8);
  71. System.out.print(String.format("hanoi(n:%d,s:%c -> t:%c)",n,source,target));
  72. }
  73. if (n == 1)
  74. {
  75. if(printOperation)
  76. System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));
  77. transfer(source, target);
  78. }
  79. else
  80. {
  81. solveHanoi(n - 1, source, target, auxiliary);
  82. printStacks(nDisks - n);
  83. if(printOperation)
  84. System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
  85. transfer(source, target);
  86. printStacks(nDisks - n);
  87. solveHanoi(n - 1, auxiliary, source, target);
  88. }
  89. }
  90. // 模擬呼叫記錄
  91. static class HanoiCallRecord
  92. {
  93. int num;
  94. char source;
  95. char auxiliary;
  96. char target;
  97. int stage; // 0 for moving n-1 disks from source to auxiliary rods;
  98. // 1 for moving the disk n from source to target rods
  99. // and moving n-1 disks from auxiliary to target rods
  100. // 2 for backtracking to the previous call record
  101. // 建構子
  102. public HanoiCallRecord(int num, char source, char auxiliary, char target)
  103. {
  104. this.num = num;
  105. this.source = source;
  106. this.auxiliary = auxiliary;
  107. this.target = target;
  108. this.stage = 0; // 預設從階段0開始
  109. }
  110. // 列印呼叫記錄
  111. public String toString()
  112. {
  113. return String.format("(n:%d,s:%c,a:%c,t:%c,s:%d)",
  114. num, source, auxiliary, target, stage);
  115. }
  116. }
  117. // 模擬呼叫堆疊,解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
  118. public static void hanoiUsingStacks(int num, char src, char aux, char tgt)
  119. {
  120. Stack stack = new Stack<>();
  121. HanoiCallRecord initial = new HanoiCallRecord(num, src, aux, tgt);
  122. stack.push(initial); // 壓入第1層呼叫記錄
  123. while (stack.isEmpty()==false)
  124. {
  125. if(printStack) System.out.print("\n" + stack);
  126. HanoiCallRecord current = stack.peek(); // 檢視本層呼叫記錄
  127. int n = current.num;
  128. char source = current.source;
  129. char auxiliary = current.auxiliary;
  130. char target = current.target;
  131. int stage = current.stage;
  132. if (n == 1) // 執行特別任務,然後退回上一層任務
  133. {
  134. if(printOperation)
  135. System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));
  136. transfer(source, target);
  137. stack.pop(); // 彈出本層呼叫記錄,
  138. }
  139. else if(stage == 0) // 階段0, 執行本層第0階段任務
  140. {
  141. // solveHanoi(n - 1, source, target, auxiliary);
  142. HanoiCallRecord next = new HanoiCallRecord(n - 1, source, target, auxiliary);
  143. stack.push(next); // 壓入下層呼叫記錄
  144. current.stage++; // 更新本層呼叫記錄的階段欄位
  145. }
  146. else if(stage == 1) // 階段1, 執行本層第1階段任務
  147. {
  148. printStacks(nDisks - n);
  149. if(printOperation)
  150. System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
  151. transfer(source, target);
  152. printStacks(nDisks - n);
  153. // solveHanoi(n - 1, auxiliary, source, target);
  154. HanoiCallRecord next = new HanoiCallRecord(n - 1, auxiliary, source, target);
  155. stack.push(next); // 壓入下層呼叫記錄
  156. current.stage++; // 更新本層呼叫記錄的階段欄位
  157. }
  158. else if(current.stage == 2) // 階段2,本層任務完成,退回上一層任務
  159. {
  160. stack.pop(); // 彈出本層呼叫記錄,
  161. }
  162. }
  163. }
  164. // 測試主程式
  165. public static void main(String[] args)
  166. {
  167. nDisks = 3; // Number of disks
  168. count = 0;
  169. for(int i=nDisks; i >= 1; i--) stackA.push(i);
  170. System.out.print("Hanoi Tower by Implicit Call Stack 遞迴版");
  171. printStacks(0);
  172. solveHanoi(nDisks, 'A', 'B', 'C');
  173. printStacks(0);
  174. // ===================================
  175. count = 0;
  176. stackA.clear();
  177. stackB.clear();
  178. stackC.clear();
  179. for(int i=nDisks; i >= 1; i--) stackA.push(i);
  180. System.out.print("\n\nHanoi Tower by Explicit Stack 模擬呼叫堆疊版");
  181. printStacks(0);
  182. hanoiUsingStacks(nDisks, 'A', 'B', 'C');
  183. printStacks(0);
  184. }
  185. }