轻舟未过万重山ing

轻舟未过万重山ing

leetcode51.N皇后(困难)-回溯法-LMLPHP

leetcode51.N皇后(困难)-回溯法-LMLPHP

 思路

都知道n皇后问题是回溯算法解决的经典问题,但是用回溯解决多了组合、切割、子集、排列问题之后,遇到这种二维矩阵还会有点不知所措。

首先来看一下皇后们的约束条件:

  1. 不能同行
  2. 不能同列
  3. 不能同斜线

确定完约束条件,来看看究竟要怎么去搜索皇后们的位置,其实搜索皇后的位置,可以抽象为一棵树。

下面我用一个 3 * 3 的棋盘,将搜索过程抽象为一棵树,如图:

leetcode51.N皇后(困难)-回溯法-LMLPHP

 从图中,可以看出,二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。

那么我们用皇后们的约束条件,来回溯搜索这棵树,只要搜索到了树的叶子节点,说明就找到了皇后们的合理位置了

然后套用递归三部曲解决。

  • 递归函数参数
  • 递归终止条件
  • 单层搜索的逻辑

这里还要外加一个 :验证棋盘是否合法

按照如下标准去重:

  1. 不能同行
  2. 不能同列
  3. 不能同斜线 (45度和135度角)

 java版本代码:

class Solution {
    List<List<String>> res = new ArrayList<>(); //全局变量res集合,用于存储解决方案的列表

    // 解决N皇后问题的方法,返回解决方案的集合
    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n]; // 创建一个N*N的字符棋盘
        for (char[] c : chessboard) {
            Arrays.fill(c, '.'); // 初始化棋盘,填充为'.'
        }
        backTrack(n, 0, chessboard); // 调用回溯方法,开始搜索解决方案
        return res; // 返回所有解决方案的集合
    }

    // 回溯方法,用于搜索解决N皇后问题的解决方案
    // n 为输入的棋盘大小
    // row 是当前递归到棋盘的第几行了
    public void backTrack(int n, int row, char[][] chessboard) {
        if (row == n) { // 终止条件:如果已经填满了所有行,即找到了一种解决方案
            res.add(Array2List(chessboard)); // 将当前棋盘状态添加到解决方案的集合中(list集合里的元素还是个集合list,见示例)
            return;
        }

        for (int col = 0; col < n; ++col) { // 遍历当前行的所有列
            if (isValid(row, col, n, chessboard)) { // 如果当前位置可以放置皇后
                chessboard[row][col] = 'Q'; // 放置皇后
                backTrack(n, row + 1, chessboard); // 递归搜索下一行的解决方案
                chessboard[row][col] = '.'; // 回溯,撤销当前位置的皇后(把Q替换成 .即可)
            }
        }
    }

    // 将二维字符数组转换为字符串集合
    public List Array2List(char[][] chessboard) {
        List<String> list = new ArrayList<>(); // 创建一个空集合

        for (char[] c : chessboard) { // 遍历棋盘的每一行 字符数组:char[] c,eg:{"Q",".",".",".",} 、
            list.add(String.copyValueOf(c)); // 将当前行的字符数组char[] c 转换为字符串并添加到集合list中
        }
        return list; // 返回转换后的集合 eg:[".Q..","...Q","Q...","..Q."]
    }

    // 检查当前位置是否可以放置皇后isValid()方法  :从上往下去找的,所以对角线只找了左上和右上
    public boolean isValid(int row, int col, int n, char[][] chessboard) {
        // 检查同一列是否已经放置了皇后
        for (int i = 0; i < row; ++i) { // 遍历当前列之前的所有行
            if (chessboard[i][col] == 'Q') { // 如果当前位置上方有皇后
                return false; // 返回false,表示当前位置不能放置皇后
            }
        }

        // 检查45度对角线上是否已经放置了皇后
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { // 向左上方遍历
            if (chessboard[i][j] == 'Q') { // 如果当前位置左上方有皇后
                return false; // 返回false,表示当前位置不能放置皇后
            }
        }

        // 检查135度对角线上是否已经放置了皇后
        for (int i = row - 1, j = col + 1; i >= 0 && j <= n - 1; i--, j++) { // 向右上方遍历
            if (chessboard[i][j] == 'Q') { // 如果当前位置右上方有皇后
                return false; // 返回false,表示当前位置不能放置皇后
            }
        }
        return true; // 如果以上条件都不满足,表示当前位置可以放置皇后,返回true
    }
}
04-30 19:42