2 분 소요

1. 점화식

수 열의 규칙성을 정의하는 것.

n번째 항(일반항)을 그 전 항들을 이용하여 표현한 식.

ex) 2의 배수를 나열한 수에서 n번째 항의 수는? => 2n

​ 피보나치 수열에서 n번째 항은? F1 = F2 = 1, Fn = F(n-2) + F(n-1)

*점화식을 통해 수열의 규칙성을 발견하고 메서드 내 함수식으로 활용할 수 있음.

2. 재귀함수(Recursion)

함수 내에 자기 자신을 다시 호출하여 작업을 수행하는 함수.(마치 인셉션!)

recursion

[구조]
(변환타입)  메서드(함수)이름(매개변수) {
	//종료조건
	if(  ) {
		return
	}
	...
	메서드(함수)이름(매개변수)
	=> 재귀로 자신을 호출하는 부분들. 여러번 호출할 수도 있으며, 
     뒤로 여러 작업들도 같이 진행할  있음.
	...
}

// 종료조건이 재귀 부분이나 다른 곳에 있을수 있음. 즉, 어떻게 구현하느냐에 따라 달라질 수 있음.
// 중요한 것은, 반드시 재귀를 탈출하도록 끝나는 곳이 있어야 한다는 것.
  • 재귀함수 예시코드

    // 피보나치 수열의 n번째 수
    static int fibo(int n) {
        if(n<3) {
            return 1;
        }
        return fibo(n-2) + fibo(n-1);
    }
    
    // 팩토리얼
    static int fact(int n) {
        if(n==1) {
            return 1;
        }
        return n*fact(n-1);
    }
    
    // 최대공약수 (유클리드 호제법)
    static int gcdInt;
    public static void gcd(int a, int b) {
        int tempInt = Math.max(a, b);
        b = Math.min(a, b);
        a = tempInt;
      
        if(b != 0) {
            gcd(b, a%b);
        } else {
            gcdInt = a;
        }
    }
    

1) 모든 재귀함수는 반복문으로 표현이 가능하다. (역도 성립)

Q. 1, 2, 4, 8, 16, ...  n번째  구하기
    // 1) 반복문
    static int timesFor(int n) {
        int result = 1;
        for (int i = 1; i < n; i++) {
            result *= 2;
        }
        return result;
    }
    // 2) 재귀함수
    static int timesRe(int n) {
        if(n==1) {
            return 1;
        }
        return 2 * timesRe(--n);
    }

	[추가공부]
	// n을 r제곱 하는 메서드 오버라이딩
    static int timesRe(int n, int r) {
        if(r==1) {
            return n;
        }
        return n * timesRe(n, --r);
    }

2) 재귀함수를 표현하기 위해 반드시 필요한 것.

  • 종료 조건 (인셉션에 빠져나와야 함)

  • 문제의 정의

  • => 선언적인 프로그래밍 방법(디클래러티브)

    “선언적인 프로그래밍”은 목표를 명시하지만 알고리즘을 명시하지 않음.

    vs 명령적인 프로그래밍(임퍼래이티브)

    반면, “명령적인 프로그래밍”은 알고리즘을 명시하지만 목표를 명시하지 않음.

    *컬스텍. 즉, 호출의 흐름을 잘 이해해야 함. (인셉션에서 어느 스텍에 있는지 알아야 함)

    *컬스텍이 쌓이기 때문에 메모리 공간을 많이 차지하며 복잡도가 증가함.

3) 재귀함수를 코드로 구현할 때 신경써야 할 것.

1) 재귀함수를 탈출해서 나올 때, 나와서의 값을 어떻게 처리할 지.

public static String recursive(String s) {
        String answer = "";
        String tmp = "";
        int num = 0;
        while(idx<s.length()) {
            if(Character.isDigit(s.charAt(idx))) {
                num = Character.getNumericValue(s.charAt(idx));
                idx++;
            } else if(s.charAt(idx)=='{') {
                idx++;
                tmp = recursive(s); 
                // 이 부분에서 재귀 탈출을 하는데, 그 값을 사용하기 위해 tmp 변수로 받아줌.
                answer += tmp.repeat(num);
            } else if(s.charAt(idx)=='}') {
                idx++;
                break;
            } else {
                answer += s.charAt(idx);
                idx++;
            }
        }

        return answer;

2) 맴버 변수, 혹은 매개변수를 통해 재귀에 전체적으로 활용할 변수 설정

public class test {
        static int idx = 0; // 해당 재귀함수에서는 맴버변수를 사용하여 인덱싱을 유지함.
    					  // depth를 +1씩 하여 매개변수로 다음 재귀에 전달해주는 방법도 있음.
    public static String solution(String code) {
        return recursive(code);
    }

    public static String recursive(String s) {
        String answer = "";
        String tmp = "";
        int num = 0;
        while(idx<s.length()) {
            if(Character.isDigit(s.charAt(idx))) {
                num = Character.getNumericValue(s.charAt(idx));
                idx++;
            } else if(s.charAt(idx)=='{') {
                idx++;
                tmp = recursive(s);
                answer += tmp.repeat(num);
            } else if(s.charAt(idx)=='}') {
                idx++;
                break;
            } else {
                answer += s.charAt(idx);
                idx++;
            }
        }

        return answer;
    }

    public static void main(String[] args) {
        System.out.println(solution("5{he2{l}o}friends"));

    }
}

4) 재귀함수를 사용하는 이유.

  • 코드 이해를 용이하게 하기 위해
  • 유지보수를 용이하게 하기 위해

*재귀함수의 대명사, ‘하노이의 탑’


마지막 수정일시: 2022-08-25 15:20

댓글남기기