

하노이의 탑은 고전적인 재귀 문제로 잘 알려져있다.
이전에도제로베이스에서 학습한적이 있는데
아직 해당 알고리즘이 이해가 되지 않아 다시 학습을 하며 참고했다.
문제 접근 방식
- 한번에 하나의 원판만 이동 가능
- 원판의 갯수가 짝수이면, 첫번째 원판은 중간 기둥에 놓는다.
- 원판의 갯수가 홀수이면, 첫번째 원판은 마지막 기둥에 놓는다.
- 마지막 원판은 무조건 마지막 기둥에 있어야 한다.
- 원판을 이동하기 위해 하위 문제로 원판을 나누고 재귀적으로 호출한다.
public static List<int[]> list;
public static void hanoi(int n, int from, int sub, int to)
if (n == 1) {
list.add(new int[]{from, to});
return;
}
hanoi(n - 1, from, to, sub);
list.add(new int[]{from, to});
hanoi(n - 1, sub, from, to);
}
list = new ArrayList<>();
hanoi(n, 1, 2, 3);
int [][] answer = new int[list.size()][];
for (int i = 0; i < list.size(); i++) {
answer[i] = list.get(i);
}
시각적으로 이해하기
n = 2
if (n == 1) {
list.add(new int[]{from, to});
return;
}
hanoi(n - 1, from, to, sub); // 1, 1, 3, 2
if (n == 1) {
list.add(new int[]{from, to}); // 1, 2
return;
}
list.add(new int[]{from, to}); // 1, 3
hanoi(n - 1, sub, from, to); // 1, 2, 1, 3
함수 호출 흐름
hanoi(2, 1, 2, 3)
- hanoi(1, 1, 3, 2)
- list.add(new int[]{1, 2})
- list.add(new int[]{1, 3})
- hanoi(1, 2, 1, 3)
- list.add(new int[]{2, 3})
표로 이동을 구현했을 때
| n | f | s | t |
|---|---|---|---|
| 2 | 1 | 2 | 3 |
| 1 | 1 | 3 | 2 |
| 2 | 1 | 2 | 3 |
| 1 | 2 | 1 | 3 |
1번째, 3번째가 분기점
{{1, 2}, {1, 3}, {2, 3}}
재귀 트리 구조
hanoi(n, from, sub, to)
-> hanoi(n-1, from, to, sub)
-> move largest from `from` to `to`
-> hanoi(n-1, sub, from, to)
n = 3 일때 재귀 트리 구조
hanoi(3, 1, 2, 3)
|
|- hanoi(2, 1, 3, 2)
|
|- hanoi(1, 1, 2, 3)
|- move 1 from 1 to 2
|- hanoi(1, 3, 1, 2)
|- move 2 from 1 to 3
|- hanoi(2, 2, 1, 3)
|
|- hanoi(1, 2, 3, 1)
|- move 2 from 2 to 3
|- hanoi(1, 1, 2, 3)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Solution {
public static List<int[]> list;
public int[][] solution(int n) {
list = new ArrayList<>();
hanoi(n, 1, 2, 3);
int [][] answer = new int[list.size()][];
for (int i = 0; i < list.size(); i++) {
answer[i] = list.get(i);
}
return answer;
}
public static void hanoi(int n, int from, int sub, int to) {
if (n == 1) {
list.add(new int[]{from, to});
return;
}
hanoi(n - 1, from, to, sub);
list.add(new int[]{from, to});
hanoi(n - 1, sub, from, to);
}
public static void main(String[] args) {
int n = 15;
Solution sol = new Solution();
int[][] arr = sol.solution(n);
System.out.println(Arrays.deepToString(arr));
}
}
해당 코드로 확인해본 결과 32767번의 계산 횟수가 확인되었다.
public class Main {
public static int hanoi(int n, int from, int sub, int to) {
if (n == 1) {
return 1;
}
int cnt = 0;
cnt += hanoi(n - 1, from, to, sub);
cnt++;
cnt += hanoi(n - 1, sub, from, to);
return cnt;
}
public static void printHanoi(int n) {
System.out.println(hanoi(n, 1, 2, 3));
}
public static void main(String[] args) {
int n = 15;
printHanoi(n); // 32767
}
}

재귀호출이 아직 많이 헷갈리고 부족하지만,
해당 문제를 풀어보면서 재귀호출이 뭔가멀티버스?같이 분기가 나뉘는걸로 느껴졌다.
제대로 이해한건진 모르겠지만, 재귀는 학습이 더 많이 필요한것 같다.