【每日一题】51. N 皇后

195 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

题目

题目链接:51. N 皇后

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

示例 1:

img-力扣51-1.jpg

输入:n = 4

输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]

解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1

输出:[["Q"]]

提示:

  • 1 <= n <= 9

解题思路

N 皇后算是一道很经典的递归回溯的题了。

👉递归

这题只需要遍历所有情况即可,但需要对每行每列和每个对角进行标记。其实思路不是太难想到,就是具体实现的代码有点难实现。思路就是遍历这个二维数组,尝试每个空位放入 Q ,看看是否满足题意。递归的深度和宽度都就是这个二维数组的横纵数,深度就是上图的纵坐标,宽度就是上图的横坐标,即 n 。具体做法:

  • 递归函数的参数

    • n ,需要用皇后数作为返回条件和遍历范围,因为递归的深度和宽度都是 n
    • m ,作为遍历的深度
  • 递归函数的返回条件

    当递归到 n 时就可以返回,同时把满足的情况放入二维数组中

  • 递归函数的功能

    由于递归的深度是“皇后数组”的纵坐标(从上向下看),所以递归函数里面需要遍历数组的横坐标,判断当前位置是否满足条件(横纵和两条对角线都没有皇后),满足就将其放入皇后(Q)做好标记,进入下一层(因为如果一层放入了一个皇后,那么这一层就不会再放了)。递归结束后要恢复原状。

这里有一个难点,就是标记横纵和两条对角线有无皇后。如果使用二维数组标记,那么每次放入一个皇后前都需要循环遍历横纵和两条对角线,放入后还需要重复遍历。对于横纵两条线可以用一个一维数组处理,因为如果当前位置放入皇后,那么就会进入下一层,所以横着的线可以不用标记,那么纵着的线可以用当前的纵坐标标记。对于对角线如何标记是一个难点(数学厉害的另说:stuck_out_tongue:),如下图

img-力扣51-2.png

根据同一直线的截距相同,只要把皇后所在的那条线的截距作为下标进行标记即可。所以两条对角线可以分别用一个一维数组标记。这样这题就解决了

代码(C++)

递归

class Solution {
public:
    vector<vector<string>> str;
    vector<string> ss;
    bool h[20], s[20], l[20];
    void dfs(int n, int m) {
        if (n == m) {
            str.push_back(ss);
            return ;
        }
        for (int i = 0; i < n; ++ i) {
            if (!h[i] && !s[i - m + n] && !l[m + i]) { //这里需要加n,因为i可能小于m
                ss[m][i] = 'Q';
                h[i] = s[i - m + n] = l[m + i] = true;
                dfs(n, m + 1);
                h[i] = s[i - m + n] = l[m + i] = false;
                ss[m][i] = '.';
            }
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        for (int i = 0; i < n; ++ i) {
            string a;
            for (int j = 0; j < n; ++ j) {
                a += '.';
            }
            ss.push_back(a);
        }
        dfs(n, 0);
        return str;
    }
};

总结

这题的思路不难想到,只是一些细节和几个难点加在一起就让整体的代码难以实现。这题我的写法是看 y 总的,不是官方的题解,当看到 y 总对于对角线的处理时,真的就一个字:绝!。如果不用这个方法,而用二维数组,真的会慢很多,这么简单的数学方法就提高了一个算法的效率,很让人佩服。所以学好数学很重要,数学对于算法也很重要。