【忍者算法】图解N皇后:用“象棋战术“巧解经典难题!|LeetCode 51「N皇后」

图解N皇后:用"象棋战术"巧解经典难题!

大家好,我是忍者算法。今天要挑战的是一道算法界的"皇后级"难题 - LeetCode 51「N皇后」。这道题困扰了无数程序员,但今天我们用象棋玩家的思维,一步步将它拆解成简单易懂的小问题!

🎮 从象棋到算法

你有没有下过国际象棋?皇后是棋盘上最强大的棋子,她可以横向、纵向、斜向任意方向移动任意步数。N皇后问题就像是在玩一局特殊的象棋:我们需要在N×N的棋盘上放置N个皇后,让她们互相都无法攻击到对方。

想象你在玩一局特殊的象棋,需要一步步地把皇后放在安全的位置上。每放置一个皇后,就会在棋盘上形成一片"危险区域",下一个皇后就不能放在这些位置上。这不就是我们程序要解决的问题吗?

💡 问题本质剖析

题目要求
在一个N×N的棋盘上放置N个皇后,使得任意两个皇后都不能互相攻击。返回所有可能的解决方案。

输入输出示例

输入:n = 4
输出:[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],
  
 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

🎯 思路突破

就像下象棋时,我们会思考:

  1. 每一行必须有一个皇后(因为如果同一行有两个皇后必然会互相攻击)
  2. 每一列必须有一个皇后(同理)
  3. 任意两个皇后不能在同一条斜线上

这启发我们可以:

  • 逐行放置皇后
  • 记录已经被攻击的列和斜线
  • 用回溯法尝试所有可能的放置方案

🚀 代码实现

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> result = new ArrayList<>();
        // 用一维数组queens记录每行皇后的列位置
        int[] queens = new int[n];
        // 三个布尔数组分别记录:列、主对角线、副对角线的占用情况
        boolean[] cols = new boolean[n];
        boolean[] diag1 = new boolean[2*n-1];  // 主对角线 r+c
        boolean[] diag2 = new boolean[2*n-1];  // 副对角线 r-c+n-1
        
        // 从第0行开始放置皇后
        backtrack(n, 0, queens, cols, diag1, diag2, result);
        return result;
    }
    
    private void backtrack(int n, int row, int[] queens, boolean[] cols, 
                          boolean[] diag1, boolean[] diag2, List<List<String>> result) {
        // 成功放置完所有皇后
        if (row == n) {
            result.add(generateBoard(queens, n));
            return;
        }
        
        // 尝试在当前行的每一列放置皇后
        for (int col = 0; col < n; col++) {
            // 计算当前位置的对角线索引
            int d1 = row + col;
            int d2 = row - col + n - 1;
            
            // 如果当前位置不被攻击
            if (!cols[col] && !diag1[d1] && !diag2[d2]) {
                // 放置皇后
                queens[row] = col;
                cols[col] = true;
                diag1[d1] = true;
                diag2[d2] = true;
                
                // 递归处理下一行
                backtrack(n, row + 1, queens, cols, diag1, diag2, result);
                
                // 回溯:撤销当前位置的皇后
                cols[col] = false;
                diag1[d1] = false;
                diag2[d2] = false;
            }
        }
    }
    
    private List<String> generateBoard(int[] queens, int n) {
        List<String> board = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            char[] row = new char[n];
            Arrays.fill(row, '.');
            row[queens[i]] = 'Q';
            board.add(new String(row));
        }
        return board;
    }
}

📝 解题思路详解

让我们像下象棋一样,一步步理解这个解法:

1. 棋盘表示

我们用一维数组 queens 记录每行皇后的列位置,这比二维数组更高效。比如 queens[2] = 3 表示第2行的皇后放在第3列。

2. 攻击区域检查

为了快速判断一个位置是否安全,我们用三个布尔数组记录被攻击的位置:

  • cols[j] 表示第j列是否有皇后
  • diag1[i+j] 表示主对角线是否有皇后
  • diag2[i-j+n-1] 表示副对角线是否有皇后

这就像象棋中提前计算好皇后的攻击范围!

3. 回溯过程

就像下象棋时的思考过程:

  • 在当前行找一个安全的位置放皇后
  • 标记这个皇后的攻击范围
  • 转移到下一行继续放置
  • 如果遇到死路,就回溯到上一步重新尝试

🔍 优化技巧

  1. 判断优化

    • 使用位运算代替布尔数组,可以进一步优化空间和时间
    • 预先计算每个位置的攻击范围
  2. 空间优化

    • 只用一维数组记录皇后位置
    • 用整数代替布尔数组记录攻击情况
  3. 回溯优化

    • 可以利用对称性减少搜索范围
    • 首行皇后只需要搜索一半位置

🎯 相关题目推荐

  • N皇后 II(LeetCode 52)- 只需要计算解的数量
  • 解数独(LeetCode 37)- 类似的回溯思想
  • 放置盒子(LeetCode 1411)- 类似的约束放置问题

🌟 面试常见追问

  1. 如何处理大规模问题?

    • 可以使用并行计算
    • 利用问题的对称性减少计算量
  2. 能否用其他算法解决?

    • 可以用约束编程(Constraint Programming)
    • 可以用遗传算法等启发式方法

作者:忍者算法
公众号:忍者算法

🎁 回复【刷题清单】获取LeetCode高频面试题合集
🧑‍💻 回复【代码】获取多语言完整题解
💡 回复【加群】加入算法交流群,一起进步

#算法面试 #LeetCode #回溯算法 #经典算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忍者算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值