알고리즘 - 백트랙킹

Backtracking

Posted by Yan on August 18, 2021

백트랙킹

  • ‘되추적’이라는 뜻
  • 어떤 노드의 유망성을 판단한 뒤, 해당 노드가 유망하지 않다면 부모 노드로 돌아가 다른 자식 노드를 찾는 방법
  • 모든 경우의 수를 찾아보지만 그 중에서도 가능성있는 경우의 수만 찾아보는 방법

브루트포스, DFS와 차이점

  • 브루트포스
    • 말 그대로 모든 경우의 수를 찾아보는 것
    • 장점 : 모든 경우의 수를 탐색하기 때문에 만족하는 값을 100% 찾아낼 수 있다.
    • 단점 : 모든 경우의 수를 탐색하기 때문에 경우의 수가 많으면 자원을 많이 필요로 한다.
  • 백트래킹
    • 해당 노드의 유망성 판단 = 해당 범위 내에서 조건을 추가하여 값의 유망성을 판단한다.
    • 장점 : 탐색하는데 필요한 자원을 줄일 수 있다.
  • DFS 깊이우선탐색
    • 트리순회의 한 형태. 백트래킹에 사용하는 대표적인 탐색 알고리즘
    • 백트래킹 방법 중 하나가 DFS
    • 깊이를 우선으로 먼저 탐색하는 방법

백트래킹 대표 예제 - 순열 Permutation 구현 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.util.*;

public class Permutation {
    public static void main(String[] args) {
        List<String> arr = new ArrayList<>();
        arr.add("a");
        arr.add("b");
        arr.add("c");

        int r = 2; // 뽑을 개수
        String[] result = new String[r];
        permutation(arr, result, 0, arr.size(), r);
    }

    /**
     * 순열 구하기
     * @param arr : 입력 리스트
     * @param result : 결과 리스트
     * @param depth : 배열을 채워 넣을 순서 (ex. result[depth])
     * @param n : 전체 개수
     * @param r : 뽑을 개수
     */
    private static void permutation(List<String> arr, String[] result, int depth, int n, int r) {

        if (depth == r) {
            System.out.println(Arrays.toString(result));
            return;
        }

        for (int i = 0; i < n - depth; i++) {

            // arr.remove(i)는 삭제되는 arr의 인덱스 i번째 값을 반환한다.
            result[depth] = arr.remove(i);

            // 재귀호출 depth를 1 늘려 result의 다음 인덱스 값을 구한다.
            permutation(arr, result, depth + 1, n, r);

            // 다시 원본 배열에 지웠던 값을 넣는다.
            arr.add(i, result[depth]);
        }
    }
}

DFS 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    boolean[] visit = new boolean[N];
    int[] arr = new int[M];
    
    public static void dfs(int N, int M, int depth) {
        //재귀 깊이가 M과 같아지면 탐색과정에서 담았던 배열을 출력
        if (depth == M) {
            for (int val : arr) {
                System.out.println(val + " ");
            }
            System.out.println();
            return;
        }
        
        for (int i = 0; i < N; i++) {
            //만약 해당 노드를 방문하지 않았다면
            if (visit[i] == false) {
                //해당 노드를 방문 상태로 변경
                visit[i] = true;
                
                //해당 깊이를 index로 하여 i+1값 저장
                arr[depth] = i + 1;
                
                //다음 자식 노드 방문을 위해 depth를 1씩 증가시키면서 재귀 호출
                dfs(N, M, depth + 1);
                
                //자식노드 방문이 끝나고 돌아오면 방문노드를 방문하지 않은 상태로 변경
                visit[i] = false;
            }
        }
        return;
    }

reference

[백준] 15649번 : N과 M (1) - JAVA [자바]
부분집합_재귀함수_순열 구하기 (JAVA)