Tower of Hanoi problem
In both merge sorting and building binary trees, we decompose the original problem into two sub-problems that are half the size of the original problem. However, for the Tower of Hanoi problem, we adopt a different decomposition strategy.
!!! question
给定三根柱子,记为 `A`、`B` 和 `C` 。起始状态下,柱子 `A` 上套着 $n$ 个圆盘,它们从上到下按照从小到大的顺序排列。我们的任务是要把这 $n$ 个圆盘移到柱子 `C` 上,并保持它们的原有顺序不变。在移动圆盘的过程中,需要遵守以下规则。
1. 圆盘只能从一个柱子顶部拿出,从另一个柱子顶部放入。
2. 每次只能移动一个圆盘。
3. 小圆盘必须时刻位于大圆盘之上。
We will scale as iiThe Tower of Hanoi problem for i is denoted asf ( i ) f(i)f ( i ) . For examplef (3) f(3)f ( 3 ) represents3 3Tower of Hanoi problem with 3 disksA
movingC
Consider the base case
As shown in the figure below, for the problem f (1) f(1)f ( 1 ) , that is, when there is only one disk, weA
move it directly from toC
.
=== “<1>”
=== “<2>”
As shown in the figure below, for the problem f (2) f(2)f ( 2 ) , that is, when there are two disks,since the small disk must always be on top of the large disk, it is necessary to useB
to complete the movement.
A
First move the upper small disk from toB
.- Then move the large disk from
A
toC
. - Finally move the small disk from
B
toC
.
=== “<1>”
=== “<2>”
=== “<3>”
=== “<4>”
Solve problem f (2) f(2)The process of f ( 2 ) can be summarized as:movingthe two disksB
from toA
C
. Among them,C
they are called target columns andB
buffer columns.
Subproblem decomposition
For problem f (3) f(3)f ( 3 ) , i.e. when there are three disks, the situation becomes slightly more complicated.
Because it is known that f ( 1 ) f(1)f ( 1 ) andf (2) f(2)The solution of f ( 2 ) , so we can think from the perspective of divide and conquer,regardA
the two disks at the top as a whole, and perform the steps shown in the figure below. In this way, the three disks are smoothlyA
moved from toC
.
- Let
B
be the target column andC
be the buffer column, move the two disks fromA
toB
. - Move
A
the remaining disk in fromA
directly toC
. - Let
C
be the target column andA
be the buffer column, move the two disks fromB
toC
.
=== “<1>”
=== “<2>”
=== “<3>”
=== “<4>”
Essentially, we take the problem f (3) f(3)f ( 3 ) is divided into two sub-problemsf (2) f(2)f ( 2 ) and subproblemf (1) f(1)f ( 1 ) . After solving these three sub-problems in sequence, the original problem is solved. This shows that the subproblems are independent and the solutions can be combined.
At this point, we can summarize the divide-and-conquer strategy for the Tower of Hanoi problem shown in the figure below: convert the original problem f ( n ) f(n)f ( n ) is divided into two sub-problemsf (n − 1) f(n-1)f(n−1 ) and a subproblemf (1) f(1)f ( 1 ) , and solve these three subproblems in the following order.
- Put n − 1 n-1n−1 diskis moved
C
from to.A
B
- will remain 1 11 disc
A
is moved directlyC
. - Put n − 1 n-1n−1 diskis moved
A
from to.B
C
For these two subproblems f ( n − 1 ) f(n-1)f(n−1 ) ,can be divided recursively in the same wayuntil the minimum subproblemf (1) f(1)f ( 1 ) . Andf ( 1 ) f(1)The solution of f ( 1 ) is known and only requires one shift operation.
Code
In the code, we declare a recursive function dfs(i, src, buf, tar)
that will move src
the top ii of the barThe i diskbuf
moves to the target columntar
.
=== “Python”
```python title="hanota.py"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solve_hanota}
```
=== “C++”
```cpp title="hanota.cpp"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solveHanota}
```
=== “Java”
```java title="hanota.java"
[class]{hanota}-[func]{move}
[class]{hanota}-[func]{dfs}
[class]{hanota}-[func]{solveHanota}
```
=== “C#”
```csharp title="hanota.cs"
[class]{hanota}-[func]{move}
[class]{hanota}-[func]{dfs}
[class]{hanota}-[func]{solveHanota}
```
=== “Go”
```go title="hanota.go"
[class]{}-[func]{move}
[class]{}-[func]{dfsHanota}
[class]{}-[func]{solveHanota}
```
=== “Swift”
```swift title="hanota.swift"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solveHanota}
```
=== “JS”
```javascript title="hanota.js"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solveHanota}
```
=== “TS”
```typescript title="hanota.ts"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solveHanota}
```
=== “Dart”
```dart title="hanota.dart"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solveHanota}
```
=== “Rust”
```rust title="hanota.rs"
[class]{}-[func]{move_pan}
[class]{}-[func]{dfs}
[class]{}-[func]{solve_hanota}
```
=== “C”
```c title="hanota.c"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solveHanota}
```
=== “Zig”
```zig title="hanota.zig"
[class]{}-[func]{move}
[class]{}-[func]{dfs}
[class]{}-[func]{solveHanota}
```
As shown in the figure below, the Tower of Hanoi problem forms a height of nnRecursion tree of n , each node represents a sub-problem and corresponds to an opendfs()
function,so the time complexity is O ( 2 n ) O(2^n)O(2n ), the space complexity isO ( n ) O(n)O ( n )。
!!! quote
汉诺塔问题源自一种古老的传说故事。在古印度的一个寺庙里,僧侣们有三根高大的钻石柱子,以及 $64$ 个大小不一的金圆盘。僧侣们不断地移动原盘,他们相信在最后一个圆盘被正确放置的那一刻,这个世界就会结束。
然而,即使僧侣们每秒钟移动一次,总共需要大约 $2^{64} \approx 1.84×10^{19}$ 秒,合约 $5850$ 亿年,远远超过了现在对宇宙年龄的估计。所以,倘若这个传说是真的,我们应该不需要担心世界末日的到来。