public class RecursiveFunctionExample01 {
public static void main(String[] args) {
System.out.println(PlusPlus(5));
}
public static int PlusPlus(int n) {
if(n == 0) return 0;
return n += PlusPlus(n-1); // 재귀함수 시작
}
}
예시) 0,0,0,0,0 부터 2,2,2,2,2까지 콘솔창에 찍어내기
첫째, 기존의 문제에서 출발하여 더 작은 경우 (이전 단계)를 생각합니다.
둘째, 같은 방식으로, 문제가 더는 작아지지 않을 때까지 더 작은 경우(이전 단계)를 생각합니다.
셋째, 문제가 간단해져서 바로 풀 수 있게 되는 순간부터 앞서 생성한 문제를 차근차근 해결
int maxNum = 2;
if (m != maxNum) {m++;}
else if (l != maxNum) {m=0; l++;}
else if (k != maxNum) {m=0; l=0; k++;}
else if (j != maxNum) {m=0; l=0; k=0; j++;}
else if (i != maxNum) {m=0; l=0; k=0; j=0; i++;}
static void RFPrintNum(int i, int j, int k, int l, int m, int maxNum){
System.out.println(i+" "+ j+" "+ k+" "+ l+" "+m);
// 종료 조건
if(i == maxNum && j == maxNum && k == maxNum && l == maxNum && m == maxNum)
return;
// 다음 단계로 넘어가는 조건
if (m != maxNum) {m++;}
else if (l != maxNum) {m=0; l++;}
else if (k != maxNum) {m=0; l=0; k++;}
else if (j != maxNum) {m=0; l=0; k=0; j++;}
else if (i != maxNum) {m=0; l=0; k=0; j=0; i++;}
// 재귀
RFPrintNum(i,j,k,l,m,maxNum);
}
재귀가 적합한 경우
반복문 vs 재귀
실행컨텍스트는 메모리를 차지하기 때문에, 재귀 함수 사용시 메모리 요구사항에 유의해야한다. (재귀 함수 호출 시 마다 스택 공간을 이용하여 스택 오버플로우가 일어날 수 있음)
깊이(depth)를 늘리면 깊이가 줄어들 때마다 depth 만큼의 실행 컨텍스트가 저장될 메모리 공간 필요 → 반복문 기반 알고리즘을 통해 메모리 사용((=함수 호출의 비용) 절약 가능
재귀 호출이 끝나면 아무 일도 하지 않고 결과만 바로 반환되도록 하는 방법
장점
재귀 vs 꼬리 재귀
// 호출시에 사용
static int sumFromZero(int n){
return sumFromZero(n, 0);
}
// 실제 겨산시에 사용
static int sumFromZero(int n, int acc){
if (n == 0) return acc; // n-0에 도달하면 연산 완료
return sumFromZero(n-1, acc + n);
// 여러개의 call stack이 쌓였을때, 연산없이 값만을 전달해줌
}
하노이의 탑 (최소 이동 횟수)
// 꼬리 재귀 이용
private static long hanoi(int floor, long sum){
if(floor == 1) return sum;
return hanoi(floor-1, 2*sum+1);
}
// 호출시 사용
static long hanoi(int floor){
return hanoi(floor, 0L);
}
조합
static int combination(int n, int r){
if(r==0 || r==n) return 1;
return combination(n-1,r) + combination(n-1,r-1);
}