【Hot 100 刷题计划】 LeetCode 51. N 皇后 | C++ 回溯算法&状态数组

LeetCode 51. N 皇后

📌 题目描述

题目级别:困难

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

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

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


💡 解法一:DFS 逐行放置与绝对值校验

这是回溯算法的最经典应用。我们不需要在一个 N × N N \times N N×N 的格子里一个个盲目去试,因为题目明确规定"同行不能有皇后"。

因此,我们可以按行进行 DFS 递归

  1. 每一层递归负责在当前行 u 中找一个合适的列 i 放置皇后。
  2. 放置前,通过 check 函数校验是否合法。
  3. 这里的校验逻辑非常具有几何直觉:
    • 同一列不冲突:j != y
    • 同一斜线不冲突:利用斜率为 1 和 -1 的直线特性,如果两个点在同一条斜线上,它们的横坐标之差的绝对值必然等于纵坐标之差的绝对值,即 abs(i - x) != abs(j - y)

💻 C++ 代码实现 (精简修正版)

cpp 复制代码
class Solution {
public:
    vector<vector<string>> res;
    vector<string> tmp;

    vector<vector<string>> solveNQueens(int n) {
        // 优雅初始化:生成一个 n 行,每行包含 n 个 '.' 的字符串数组
        tmp = vector<string>(n, string(n, '.'));
        
        // 从第 0 行开始深度优先搜索
        dfs(0, n);
        return res;
    }

    void dfs(int u, int n)
    {
        // 递归终止条件:已经成功放置到了第 n 行(0 到 n-1 都放好了)
        if (u == n)
        {
            res.push_back(tmp);
            return ;
        }

        // 尝试在当前行 u 的每一列 i 放置皇后
        for (int i = 0; i < n; i ++ )
        {
            if (check(u, i, n))
            {
                tmp[u][i] = 'Q';     // 处理节点
                dfs(u + 1, n);       // 向下递归,处理下一行
                tmp[u][i] = '.';     // 回溯:撤销处理,尝试下一列
            }
        }
    }

    // 校验在 (x, y) 放置皇后是否合法
    bool check(int x, int y, int n)
    {
        // 只需要检查 x 行之前的行即可,因为下面的行还没放皇后
        for (int i = 0; i < x; i ++ )
        {
            for (int j = 0; j < n; j ++ )
            {
                if (tmp[i][j] == 'Q')
                {
                    // 检查列冲突 或 对角线冲突
                    if (j == y || abs(i - x) == abs(j - y)) return false;
                }
            }
        }
        return true;
    }
};

💡 解法二:状态数组降维打击

在基础版本中,每次放置皇后都要回头遍历棋盘检查冲突,校验的时间复杂度是 O ( N 2 ) O(N^2) O(N2)。

为了追求极致性能,我们可以开辟三个布尔数组,专门记录哪些列、哪些斜线已经被占用了:

  1. col 数组 :记录哪一列有皇后。col[i] = true 表示第 i 列被占。
  2. dg 数组(正对角线) :观察棋盘发现,处于同一条正对角线(右上到左下, /)上的点,它们的行号和列号之和是固定的x + y = const)。我们用 dg[u + i] 来标记这条斜线。
  3. udg 数组(反对角线) :处于同一条反对角线(左上到右下, \)上的点,它们的行号和列号之差是固定的y - x = const)。为了防止数组下标出现负数,我们加上一个偏移量 n,即用 udg[n - u + i] 来标记这条斜线。

有了这三个数组,判断一个点能不能放皇后,只需要查三个布尔值即可,瞬间降维到 O ( 1 ) O(1) O(1)!


💻 C++ 代码实现 (极客优化版)

cpp 复制代码
class Solution {
private:
    vector<vector<string>> res;
    vector<string> board;
    // 状态数组:因为是对角线,最大数量是 2N-1,所以开大一点 20 足够应付 n=9
    bool col[20], dg[20], udg[20]; 

    void dfs(int u, int n) {
        // 成功放置到最后一行,收集结果
        if (u == n) {
            res.push_back(board);
            return;
        }

        // 尝试在第 u 行的每一列 i 放置皇后
        for (int i = 0; i < n; i++) {
            // O(1) 极速校验:该列、该正对角线、该反对角线均没有被占用
            if (!col[i] && !dg[u + i] && !udg[n - u + i]) {
                
                board[u][i] = 'Q';
                // 同步更新三个状态数组,宣告占领
                col[i] = dg[u + i] = udg[n - u + i] = true;
                
                dfs(u + 1, n); // 向下递归
                
                // 回溯:撤销皇后,并释放三个状态数组的占领
                col[i] = dg[u + i] = udg[n - u + i] = false;
                board[u][i] = '.';
            }
        }
    }

public:
    vector<vector<string>> solveNQueens(int n) {
        board.assign(n, string(n, '.'));
        // 初始化状态数组
        memset(col, false, sizeof col);
        memset(dg, false, sizeof dg);
        memset(udg, false, sizeof udg);
        
        dfs(0, n);
        return res;
    }
};
相关推荐
MC皮蛋侠客1 天前
Google Test 单元测试指南
c++·单元测试·google test
艾莉丝努力练剑1 天前
【Linux:文件】Ext系列文件系统进阶
linux·运维·服务器·c++·文件系统·文件io·ext
kkeeper~1 天前
0基础C语言积跬步之数据在内存中的存储
c语言·数据结构·算法
wabs6661 天前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_876964131 天前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
basketball6161 天前
C++ NULL 和 nullptr 区别 以及 nullptr 的核心实现
java·开发语言·c++
嗝o゚1 天前
CANN GE 算子融合——融合算法与调度策略
算法·昇腾·cann·ge
小江的记录本1 天前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试
Fre丸子_1 天前
自定义文件夹选取功能
c++
Ulyanov1 天前
用声明式语法重新定义Python桌面UI:QML+PySide6现代开发入门(一)
开发语言·python·算法·ui·系统仿真·雷达电子对抗仿真