回溯算法 - N皇后问题

回溯算法

  • 实际上就是一个决策树的遍历过程
  1. 路径:已经做出的选择
  2. 选择列表:是你当前可以做的选择
  3. 结束条件:到达决策树底层,无法再做选择的条件
  • 经典问题:【全排列】【N皇后问题】

1. 全排列问题

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X2pwZy9naWJrSXowTVZxZEYxdW1BZHlYdVBxNTRpYnc3WDIzbW5hR0ppYW1idHBxYzR2a2hzcVpZb0VaV2lhbmlic0ltdER2bVhSTUNKdVVCOGdrTGZDSlZBUUR0MlJBLzY0MD93eF9mbXQ9anBlZw?x-oss-process=image/format,png

 // 全排列问题
 public class Backtrack {
     List<List<Integer>> res = new LinkedList<>();
     List<List<Integer>> permute(int[] nums) {
         LinkedList<Integer> list  = new LinkedList<>();
         backtrack(nums, list);
         return res;
     }
 ​
     void backtrack(int[] nums, LinkedList<Integer> list) {
         if (nums.length == list.size()) {
             res.add(list);
             return;
         }
         for (int i = 0; i < nums.length - 1; i++) {
             if (list.contains(nums[i])) {
                 continue;
             }
             // 做决策
             list.add(nums[i]);
             // 进入下一层决策树
             backtrack(nums, list);
             // 取消决策
             list.removeLast();
         }
     }
 }

2. N皇后问题

  • 给你一个NxN的期盼,让你放置N个皇后,使得他们不能互相攻击

  • 皇后可以攻击同一行,同一列,左上左下,右上右下 四个方向的任意单位

  • 解法也就是上述全排列方案的适当调整

    • 添加了isValid()的函数校验是否有皇后冲突
    • 初始化了一个字符串链表棋盘
public class Backtrack {

    List<List<String>> queueMatrix = new LinkedList<>();
    List<String> board = new LinkedList<>();

    int n = 0;

    List<List<String>> solveNQueens(int n) {
        this.n = n;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) sb.append('.');
        for (int i = 0; i < n; i++) board.add(sb.toString());
        queenBacktrack( 0);
        return queueMatrix;
    }

    void queenBacktrack(int row) {
        if (row == n) {
            // 结束了一次决策树(全排列)
            queueMatrix.add(new LinkedList<>(board));
            return;
        }
        for (int col = 0; col < n; col++) {
            if (!isValid(row, col)) {
                continue;
            }
            // 合法,做决策
            StringBuilder sb = new StringBuilder(board.get(row));
            sb.setCharAt(col, 'Q');
            board.set(row, sb.toString());
            // 递归
            queenBacktrack(row + 1);
            // 回退
            StringBuilder sb2 = new StringBuilder(board.get(row));
            sb2.setCharAt(col, '.');
            board.set(row, sb2.toString());
        }
    }

    boolean isValid(int row, int col) {
        for (int i = 0; i < row; i++) {
            // 上面不能有Q
            if (board.get(i).charAt(col) == 'Q') {
                return false;
            }
        }
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            // 左上方,不能有Q
            if (board.get(i).charAt(j) == 'Q') {
                return false;
            }
        }
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            // 右上方,不能有Q
            if (board.get(i).charAt(j) == 'Q') {
                return false;
            }
        }
        return true;
    }
}