Backtracking

1. N-Queens 문제

  • N-Queens 문제는 N x N 체스판에 N개의 퀸을 서로 공격하지 못하도록 배치하는 문제입니다.

void solveNQueens(int n) {
    char[][] board = new char[n][n];
    for (int i = 0; i < n; i++) {
        Arrays.fill(board[i], '.');  // 체스판 초기화
    }
    List<List<String>> solutions = new ArrayList<>();
    backtrack(board, 0, solutions);
}

void backtrack(char[][] board, int row, List<List<String>> solutions) {
    if (row == board.length) {
        solutions.add(construct(board));
        return;
    }

    for (int col = 0; col < board.length; col++) {
        if (isValid(board, row, col)) {
            board[row][col] = 'Q';  // 퀸 배치
            backtrack(board, row + 1, solutions);  // 다음 행으로 이동
            board[row][col] = '.';  // 백트래킹
        }
    }
}

boolean isValid(char[][] board, int row, int col) {
    for (int i = 0; i < row; i++) {
        if (board[i][col] == 'Q') return false;  // 같은 열에 퀸이 있는지 확인
    }

    for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q') return false;  // 왼쪽 대각선에 퀸이 있는지 확인
    }

    for (int i = row - 1, j = col + 1; i >= 0 && j < board.length; i--, j++) {
        if (board[i][j] == 'Q') return false;  // 오른쪽 대각선에 퀸이 있는지 확인
    }

    return true;
}

List<String> construct(char[][] board) {
    List<String> solution = new ArrayList<>();
    for (int i = 0; i < board.length; i++) {
        solution.add(new String(board[i]));
    }
    return solution;
}

2. Subsets 문제

  • 모든 부분 집합 구하기 (백트래킹을 사용한 조합):

void subsets(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    backtrackSubsets(result, new ArrayList<>(), nums, 0);
}

void backtrackSubsets(List<List<Integer>> result, List<Integer> tempList, int[] nums, int start) {
    result.add(new ArrayList<>(tempList));

    for (int i = start; i < nums.length; i++) {
        tempList.add(nums[i]);  // 요소 추가
        backtrackSubsets(result, tempList, nums, i + 1);  // 다음 단계로 진행
        tempList.remove(tempList.size() - 1);  // 백트래킹
    }
}

3. Combination Sum 문제

  • 주어진 배열에서 가능한 모든 조합으로 합이 target이 되는 조합 찾기:

void combinationSum(int[] candidates, int target) {
    List<List<Integer>> result = new ArrayList<>();
    backtrackCombinationSum(result, new ArrayList<>(), candidates, target, 0);
}

void backtrackCombinationSum(List<List<Integer>> result, List<Integer> tempList, int[] candidates, int remain, int start) {
    if (remain < 0) return;  // 조건을 만족하지 않으면 종료
    if (remain == 0) result.add(new ArrayList<>(tempList));  // 합이 target이 되면 결과에 추가

    for (int i = start; i < candidates.length; i++) {
        tempList.add(candidates[i]);  // 요소 추가
        backtrackCombinationSum(result, tempList, candidates, remain - candidates[i], i);  // 재귀 호출
        tempList.remove(tempList.size() - 1);  // 백트래킹
    }
}

4. Permutations 문제

  • 주어진 배열의 모든 순열을 구하는 문제:

void permute(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    backtrackPermutations(result, new ArrayList<>(), nums);
}

void backtrackPermutations(List<List<Integer>> result, List<Integer> tempList, int[] nums) {
    if (tempList.size() == nums.length) {
        result.add(new ArrayList<>(tempList));  // 모든 요소를 다 사용한 경우 결과에 추가
    } else {
        for (int i = 0; i < nums.length; i++) {
            if (tempList.contains(nums[i])) continue;  // 이미 사용한 숫자는 건너뜀
            tempList.add(nums[i]);  // 요소 추가
            backtrackPermutations(result, tempList, nums);  // 재귀 호출
            tempList.remove(tempList.size() - 1);  // 백트래킹
        }
    }
}

5. Palindrome Partitioning 문제

  • 주어진 문자열을 가능한 모든 팰린드롬 부분 문자열로 나누기:

void partition(String s) {
    List<List<String>> result = new ArrayList<>();
    backtrackPalindrome(result, new ArrayList<>(), s, 0);
}

void backtrackPalindrome(List<List<String>> result, List<String> tempList, String s, int start) {
    if (start == s.length()) {
        result.add(new ArrayList<>(tempList));  // 끝까지 탐색 완료된 경우 결과에 추가
    } else {
        for (int i = start; i < s.length(); i++) {
            if (isPalindrome(s, start, i)) {
                tempList.add(s.substring(start, i + 1));  // 팰린드롬인 경우 추가
                backtrackPalindrome(result, tempList, s, i + 1);  // 재귀 호출
                tempList.remove(tempList.size() - 1);  // 백트래킹
            }
        }
    }
}

boolean isPalindrome(String s, int low, int high) {
    while (low < high) {
        if (s.charAt(low++) != s.charAt(high--)) return false;
    }
    return true;
}

  • 2D 그리드에서 주어진 단어를 찾는 문제 (백트래킹 사용):

boolean exist(char[][] board, String word) {
    for (int i = 0; i < board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            if (backtrackWordSearch(board, word, i, j, 0)) {
                return true;
            }
        }
    }
    return false;
}

boolean backtrackWordSearch(char[][] board, String word, int i, int j, int index) {
    if (index == word.length()) return true;  // 모든 문자 탐색 완료
    if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(index)) {
        return false;  // 조건에 맞지 않으면 종료
    }

    char temp = board[i][j];
    board[i][j] = '*';  // 이미 방문한 곳은 '*'로 표시

    boolean found = backtrackWordSearch(board, word, i + 1, j, index + 1)
            || backtrackWordSearch(board, word, i - 1, j, index + 1)
            || backtrackWordSearch(board, word, i, j + 1, index + 1)
            || backtrackWordSearch(board, word, i, j - 1, index + 1);

    board[i][j] = temp;  // 백트래킹 후 복원
    return found;
}

Last updated