본문 바로가기
알고리즘

[알고리즘 문제해결 전략] 재귀함수

by 슈슈슉민 2023. 6. 9.

재귀 함수를 만들 key point를 `알고리즘 문제해결 전략`을 보고 정리해보려고 한다.

 

원자성 : 더이상 쪼개지지 않는 성질

  재귀함수에서는 원자성을 갖는 작업들을 가리켜 기저사례(base case)라고 한다.

1부터 n까지의 합을 계산하는 반복 함수를 생각해보자

더보기

n*(n-1)*(n-2)*...*1

이 식은 n개의 숫자들로 이뤄져있다. 여기서 원자성은 무엇일까? 

1이다. n=1인 case와 그 외 case로 나눠서 코드로 생각해보면

일단 n != 1부터

int recursiveSum(int n) {
	return n + recursiveSum(n-1);
}

이것을 생각할 수 있다. n=1일때 이 식을 사용하면 갯수는 양수이어야 하므로 문제가 생긴다. 그래서 base case를 나눠 다시 코드를 생각해보면 

int recursiveSum(int n) {
	if(n == 1) return 1;
    return n + recursiveSum(n-1);
}

이렇게 된다.

 

연습!!

NCM : N개 중 M개 고르기

B.C : 아무것도 고르지 않을때(m=0) 또는 고를 수 있는 원소가 없을 때(n=0, m>0)

import java.util.ArrayList;
import java.util.List;

public class Test {
	public static void main(String[] args) {
		int n = 5; // n 값 설정
        	int m = 3; // m 값 설정
        List<Integer> picked = new ArrayList<>(); // 결과를 저장할 리스트

        NCM(n, picked, m);
	}
	
	// picked : 지금까지 고른 원소들의 번호
	public static void NCM(int n, List<Integer> picked, int m) {
		// 종료 조건: 더 이상 고를 것이 없으면 결과 출력
		if(m == 0) {
			printPicked(picked); 
			return;
		}
		
		// 리스트가 비어있는지 확인하여 최소한으로 선택 가능한 숫자 결정
       		// 처음에는 0부터 시작하고, 그 이후에는 마지막으로 고른 숫자의 다음 숫자부터 시작
		int smallest = picked.isEmpty() ? 0 : picked.get(picked.size() - 1) + 1;
		// 예를 들면, n이 5이고 현재 picked 리스트가 [1, 2]라면 smallest는 3
        
		// smallest부터 n까지 반복
		for (int next = smallest; next < n; ++next) {
			// 다음 숫자를 선택
			picked.add(next);
			// 나머지 숫자들을 재귀적으로 선택
            	NCM(n, picked, m - 1);
            	// 선택된 숫자를 다시 되돌림 (백트래킹)
            	picked.remove(picked.size() - 1);
            	// 예를 들면, 처음에 0을 골랐더라도 이후에는 1, 2, 3, 4 중에 고를 수 있음
        }
		
	}
	
	public static void printPicked(List<Integer> picked) {
	    for (int num : picked) {
	        System.out.print(num);
	    }
	    System.out.println();
	}

}

 

보글 문제

Y에 근접한 경우의 수 : 8

B.C : 1. 위치(y, x)에 있는 글자가 원하는 단어의 첫 글자가 아닌 경우 항상 실패

         2. (1번 경우에 해당되지 않을 경우) 원하는 단어가 한 글자인 경우 항상 성공

public class WordSearch {
    private static final int[] dx = {-1, -1, -1, 1, 1, 1, 0, 0};
    private static final int[] dy = {-1, 0, 1, -1, 0, 1, -1, 1};
    private static char[][] board = {
            {'N', 'N', 'N', 'N', 'S'},
            {'N', 'E', 'E', 'E', 'N'},
            {'N', 'E', 'Y', 'E', 'N'},
            {'N', 'E', 'E', 'E', 'N'},
            {'N', 'E', 'E', 'E', 'N'}
    };
    
    public static void main(String[] args) {
        System.out.println(hasWord(2, 2, "YES"));
    }

    private static boolean hasWord(int y, int x, String word) {
    	// B.C 1: 시작 위치가 범위 밖이면 무조건 실패
        if (!inRange(y, x)) return false;
        // B.C 2: 첫글자가 일치하지 않으면 실패
        if (board[y][x] != word.charAt(0)) return false;
        // B.C 3: 단어의 길이가 1이면 성공
        if (word.length() == 1) return true;
        // 인접한 8칸 검사한다.
        for (int direction = 0; direction < 8; ++direction) {
            int nextY = y + dy[direction];
            // 다음 칸이 범위 안에 있는지, 첫 글자는 일치하는지 확인할 필요가 없다.
            int nextX = x + dx[direction];
            if (hasWord(nextY, nextX, word.substring(1))) {
                return true;
            }
        }
        return false;
    }

    private static boolean inRange(int y, int x) {
        int boardHeight = board.length;
        int boardWidth = board[0].length;
        return 0 <= y && y < boardHeight && 0 <= x && x < boardWidth;
    }
}

 

소풍

예제 입출력 분석

더보기

3 // 테스트 케이스 수(C<=50)

2 1 // 학생의 수(2<=n<=10), 친구 쌍의 수(0<=m<=n(n-1)/2)

0 1 // 서로 친구인 두 학생의 번호 // 출력 결과 : 1

4 6 // 학생의 수(2<=n<=10), 친구 쌍의 수(0<=m<=n(n-1)/2)

0 1 1 2 2 3 3 0 0 2 1 3 // 서로 친구인 두 학생의 번호 // 출력 결과 : 3

6 10 // 학생의 수(2<=n<=10), 친구 쌍의 수(0<=m<=n(n-1)/2)

0 1 0 2 1 2 1 3 1 4 2 3 2 4 3 4 3 5 4 5 // 서로 친구인 두 학생의 번호 // 출력 결과 : 4

'알고리즘' 카테고리의 다른 글

[알고리즘 문제 해결 전략] 20장 문자열  (0) 2023.06.16
[BOJ]4358 생태학  (0) 2023.06.09
[Programmers] 카펫  (0) 2023.06.07
[BOJ]14889 스타트와 링크  (0) 2023.06.03
[Programmers] 최소직사각형  (0) 2023.06.03