# 재귀 알고리즘 (Recursive) ![](https://hackmd.io/_uploads/SJJbvpN43.png) ## 재귀 알고리즘이란? - 자신이 수행할 작업을 처리하는 도중에 자기 자신을 호출하여 작업을 수행하는 알고리즘 - 함수나 프로시저가 자기 자신을 호출하는 것 - 재귀 알고리즘은 깊이가 무한정하게 들어갈 수 있으며, 무한루프에 빠질 가능성도 있으므로 조심해서 사용해야 한다 - 장점 - 코드가 간결하고 이해하기 쉽다 - 명확하게 해결하고자 하는 문제를 표현하기 쉽다 - 단점 - 재귀 알고리즘은 반복문보다 실행 속도가 느릴 수 있다 - 함수 호출에 따른 메모리 공간이 필요하므로 메모리 사용량이 증가할 수 있다 - 재귀 호출이 너무 깊어지면 스택 오버플로우가 발생할 수 있다 - 재귀 호출이 무한루프에 빠질 가능성이 있으므로, 종료 조건을 반드시 설정해야 한다 ## 재귀 알고리즘 예시 1. 팩토리얼 계산 문제 - 정수 N이 주어졌을 때, N!을 계산하는 문제 2. 피보나치 수열 문제 - 정수 N이 주어졌을 때, 피보나치 수열의 N번째 항을 구하는 문제 3. 이진 검색 문제 - 정렬된 배열과 찾고자 하는 값이 주어졌을 때, 이진 검색을 이용하여 해당 값의 인덱스를 구하는 문제 4. 하노이의 탑 문제 - N개의 원판이 있는 탑이 있을 때, 모든 원판을 다른 기둥으로 옮기는 문제 - 백준 알고리즘 1914번 ==[링크](https://www.acmicpc.net/problem/1914)== 5. 최대공약수 문제 - 두 수가 주어졌을 때, **유클리드 호재법**을 이용하여 최대공약수를 구하는 문제 > 재귀 문제는 반복문으로 대체가 가능하다. 반복문이 아닌 재귀를 사용할 경우 메모리 측면에서 비효율적이기 때문에 알고리즘 문제를 풀 때 적합한 방법은 아니다. ### 하노이탑 문제 - 백준 알고리즘 1914번 ==[링크](https://www.acmicpc.net/problem/1914)== - 위 문제를 보고 수도코드 작성 및 풀이를 해보자 #### 하노이탑 문제 수도코드 예 ```plaintext 1. 원판의 개수 N을 입력받는다. 2. 최소 이동 횟수를 계산한다 (2^N - 1). 3. 최소 이동 횟수를 출력한다. 4. 원판 개수가 20개 이하인 경우 이동 과정을 출력한다. 5. 하노이 탑 알고리즘을 구현한다 (재귀 함수). a. 이동할 원판이 없는 경우 반환한다. b. N-1개의 원판을 출발 기둥에서 보조 기둥으로 이동한다 (하노이 탑 알고리즘 호출). c. N번째 원판을 출발 기둥에서 도착 기둥으로 이동한다. d. N-1개의 원판을 보조 기둥에서 도착 기둥으로 이동한다 (하노이 탑 알고리즘 호출). ``` #### 하노이탑 문제 소스코드 예 ```java import java.math.BigInteger; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int N = sc.nextInt(); // 원판의 개수 입력 // 최소 이동 횟수 계산 및 출력 BigInteger minMoves = BigInteger.valueOf(2).pow(N).subtract(BigInteger.ONE); System.out.println(minMoves); // 원판 개수가 20개 이하인 경우 이동 과정 출력 if (N <= 20) { hanoi(N, 1, 2, 3); } } // 하노이 탑 알고리즘 구현 public static void hanoi(int N, int from, int aux, int to) { if (N == 0) return; // 이동할 원판이 없는 경우 반환 hanoi(N - 1, from, to, aux); // N-1개의 원판을 출발 기둥에서 보조 기둥으로 이동 System.out.println(from + " " + to); // N번째 원판을 출발 기둥에서 도착 기둥으로 이동 hanoi(N - 1, aux, from, to); // N-1개의 원판을 보조 기둥에서 도착 기둥으로 이동 } } ``` ### 최대공약수 구하기 #### 유클리드 호제법(Euclidean algorithm) ![Euclidean_algorithm_252_105_animation_flipped](https://hackmd.io/_uploads/BkmTgAj1C.gif) - 두 개의 자연수의 최대공약수(GCD, Greatest Common Divisor)를 구하는 알고리즘 - 원리 - 두 수 a와 b (a > b)의 최대공약수는 a를 b로 나눈 나머지와 b의 최대공약수와 같다 - 즉, `GCD(a, b) = GCD(b, a % b)`가 성립한다 - 예시 : `a = 56`이고 `b = 48`일 때 1. `GCD(56, 48) = GCD(48, 56 % 48) = GCD(48, 8)` 2. `GCD(48, 8) = GCD(8, 48) = GCD(8, 48 % 8) = GCD(8, 0)` 3. b가 0이 되었으므로, a와 b의 최대공약수는 8이다 #### 최대공약수 구하기 수도코드 예 ```plaintext 1. 두 수 A와 B를 입력받는다. 2. B가 0이 될 때까지 다음 과정을 반복한다. a. A를 B로 나눈 나머지를 R이라고 한다. b. A에 B의 값을, B에 R의 값을 할당한다. 3. A의 값을 반환한다. ``` #### 최대공약수 구하기 소스코드 예 (재귀 사용) ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int A = sc.nextInt(); // 첫 번째 수 입력 int B = sc.nextInt(); // 두 번째 수 입력 System.out.println(gcd(A, B)); // 최대공약수 출력 } public static int gcd(int A, int B) { if (B == 0) return A; // B가 0이면 A를 반환 return gcd(B, A % B); // GCD(B, A % B)를 반환 } } ``` #### 최대공약수 구하기 소스코드 예 (반복문 사용) ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int A = sc.nextInt(); // 첫 번째 수 입력 int B = sc.nextInt(); // 두 번째 수 입력 System.out.println(gcd(A, B)); // 최대공약수 출력 } public static int gcd(int A, int B) { while (B != 0) { int R = A % B; A = B; B = R; } return A; } } ``` #### 최소공배수를 구하는 방법 - 유클리드 호제법을 사용하여 최대공약수를 구했다면, 최소공배수(LCM, Least Common Multiple)도 쉽게 구할 수 있다. - `LCM(A, B) = A * B / GCD(A, B)` 공식을 이용하면 된다. #### 최소공배수 구하기 수도코드 예 ```plaintext 1. 두 수 A와 B를 입력받는다. 2. 최대공약수를 구하는 함수를 호출하여 최대공약수를 구한다. 3. 최소공배수를 계산하여 출력한다. ``` #### 최소공배수 구하기 소스코드 예 ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int A = sc.nextInt(); // 첫 번째 수 입력 int B = sc.nextInt(); // 두 번째 수 입력 int GCD = gcd(A, B); // 최대공약수 계산 int LCM = A * B / GCD; // 최소공배수 계산 System.out.println(LCM); // 최소공배수 출력 } public static int gcd(int A, int B) { while (B != 0) { int R = A % B; A = B; B = R; } return A; } } ```