재귀(Recursion)란 하나의 함수에서 자기 자신을 다시 호출해서 작업을 수행하는 알고리즘이다. 우리는 일반적으로 문제를 풀 때 A 다음엔 B를 하고, B 다음에는 C를 하고... 와 같은 절차지향적 사고를 하는 경향이 있다.
하지만 재귀함수를 잘 다루기 위해서는 이러한 절차지향적 사고의 틀에서 벗어나 귀납적 사고를 가져야 한다.
귀납적 사고란 k=1일때 성립하고, k=n일때 성립한다고 가정했을 때, k=n+1도 성립한다면 이 함수는 참이라고 생각하는 사고이다.
즉, 이 함수는 정답을 구해줄 것이라고 믿고 프로그램을 작성해야한다. 그렇지 않고 절차적으로 생각하면 생각이 점점 꼬여버린다....
그렇다면 귀납적 사고를 이용해 재귀함수를 작성하는 방법에 대해 알아보자.
재귀함수에는 2가지의 핵심 요소가 반드시 필요하다.
1. Base condition(종료 조건): 재귀가 멈추는 가장 작은 단위, 더 이상 쪼갤 수 없는 상태
2. Recursive Step(재귀 식): 큰 문제를 작은 문제로 쪼개는 방식, 모든 입력은 Base condition으로 수렴해야 한다.
예를 들어 임의의 숫자 1부터 N까지를 출력하는 코드를 작성해보자.
void func(int n){
if(n==0) return; // Base Condition
func(n-1); // Recursive Step, n==0으로 수렴한다.
cout<<n<<' ';
}
n==0일때 프로그램을 종료하여 무한 반복을 방지하고,
func(n-1)을 호출하여 큰 문제를 점점 작은 문제로 쪼개면서 n==0으로 수렴하고 있다.
만약 n이 5라면 func(5) -> func(4) -> func(3) -> .... -> 1출력 -> 2출력 -> ... 와 같이 절차지향적으로 생각할 수 있지만, 만약 n이 매우 큰 숫자라면 절차지향적 사고는 지양해야한다.
재귀함수에 대한 몇 가지 특징에 대해 알아보자.