

【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用) - dayceng
source link: https://www.cnblogs.com/DAYceng/p/17209162.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用)
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

- 输入:n = 4
- 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
- 解释:如上图所示,4 皇后问题存在两个不同的解法。
- 输入:n = 1
- 输出:[["Q"]]
如何使用回溯方法去搜索一个二维数组?(难点)
其实本题的难点就主要是对于二维数组的操作的不熟练造成的,画个图示先再说:

上图展示了在一个 4X4 的棋盘中,其中一种正确摆放结果的获取过程。如图所示,实际上在棋盘(二维数组)中搜索摆放结果时,可以逐层搜索
即:把每层递归看做棋盘中的一层,当前递归处理当前层棋盘的搜索任务
那么棋盘有多大,最后就会触发多少层递归(这里是 4X4 所以有4层递归)
二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。当我们遍历到棋盘的最底层时也就到了叶子节点处,此时搜索结束。(结束条件)
还是老一套,回溯三部曲
1、确定回溯函数的参数以及返回值
看题目给的输出结果得知,我们仍需定义一个二维结果数组res;
输入参数有:棋盘的大小n, 遍历行数记录遍历row以及一维数组chessboard(充当单层棋盘,不要在一开头就定义,因为要每行都清空)
class Solution {
private:
vector<vector<string>> res;
void backtracking(int n, int row, vector<string>& chessboard){//确定回溯函数的参数以及返回值
}
public:
vector<vector<string>> solveNQueens(int n) {
}
};
2、确定终止条件
根据上面的讨论,我们希望在遍历到棋盘底部的时候结束
这很好判断,通过row来看即可,row == n
就到底了
class Solution {
private:
vector<vector<string>> res;
void backtracking(int n, int row, vector<string>& chessboard){//确定回溯函数的参数以及返回值
//确定终止条件
if(row == n){//将单层棋盘结果,也就是chessboard,保存至二维结果数组
res.push_back(chessboard);
return;
}
}
public:
vector<vector<string>> solveNQueens(int n) {
}
};
3、确定单层处理逻辑
变量row代表着棋盘的行,也控制着递归的深度
而每层里面的for中的循环变量我们命名为col,其控制着棋盘的列
通过行列变量的配合最终确定皇后的位置
与此同时,在单层处理逻辑中,我们还要加入对N皇后问题规则进行判断的函数isVaild,用以确定当前摆放位置是否合法
class Solution {
private:
vector<vector<string>> res;
void backtracking(int n, int row, vector<string>& chessboard){//确定回溯函数的参数以及返回值
//确定终止条件
if(row == n){//将单层棋盘结果,也就是chessboard,保存至二维结果数组
res.push_back(chessboard);
return;
}
//确定单层处理逻辑,每次都从新的列开始搜,因此col初始值是0
for(int col = 0; col < n; ++col){
if(isVaild()){
chessboard[row][col] = 'Q';
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.';//题干中给了用'.'表示空
}
}
}
public:
vector<vector<string>> solveNQueens(int n) {
}
};
注意在进入下一层递归时要跳过当前行
规则判断函数isVaild
在N皇后问题中,皇后的摆放规则如下:
- 同一行上不能有两个皇后(不能同行)
- 同一列上不能有两个皇后(不能同列)
- 45度和135度角斜线上不能有两个皇后(不能同斜线)
那么我们只要在isVaild函数中对行、列以及斜线上的皇后情况进行检查就行
那么容易得出,isVaild函数的输入参数是与回溯函数相同的,即int n, int row, vector<string>& chessboard
不过,我们还需要将col也作为参数输入,既然要检查行,行不能不给啊
bool isVaild(int row, int col, vector<string>& chessboard, int n){
//检查列,就要指定列遍历行
for(int i = 0; i < row, ++i){
if(chessboard[i][col] == 'Q') return false;
}
//检查45°,以4X4为例,检查以下坐标
//(0,0)(1,1)(2,2)(3,3)
for(int i = row - 1, j = col - 1; i >= 0 && j>= 0; --i , --j){
if(chessboard[i][j] == 'Q') return false;
}
//检查135°
//检查除45°涉及的坐标以外的所有坐标,顺序可能是乱的,但一定都会检查到,不理解子集画一画想一想
for(int i = row - 1, j = col + 1; i >= 0 && j < n; --i , ++j){//注意条件,j要++
if(chessboard[i][j] == 'Q') return false;
}
return true;
}
注意事项:
1、这里其实我们不用去检查行(类似检查列的那种操作),因为一层递归for只拿行中的一个数,不会有重
2、关于遍历45度和135度的逻辑,如果实在忘了就自己画个图理解一下
3、实现45度和135度遍历时,我们使用的for的遍历条件是关键,请注意记忆
- 45度时,row和col作为输入肯定越给越大,因此i、j的值每次遍历时都会变大,而遍历条件是
i >= 0 && j>= 0
,因此需要-- - 135度时,row和col作为输入也会越给越大,但j的遍历条件是要小于n,因此其要++
(有新理解再补充)
class Solution {
private:
vector<vector<string>> res;
void backtracking(int n, int row, vector<string>& chessboard){//确定回溯函数的参数以及返回值
//确定终止条件
if(row == n){//将单层棋盘结果,也就是chessboard,保存至二维结果数组
res.push_back(chessboard);
return;
}
//确定单层处理逻辑,每次都从新的列开始搜,因此col初始值是0
for(int col = 0; col < n; ++col){
if(isVaild(row, col, chessboard, n)){
chessboard[row][col] = 'Q';
backtracking(n, row + 1, chessboard);//注意要跳过当前行
chessboard[row][col] = '.';//题干中给了用'.'表示空
}
}
}
bool isVaild(int row, int col, vector<string>& chessboard, int n){
//检查列,就要指定列遍历行
for(int i = 0; i < row, ++i){
if(chessboard[i][col] == 'Q') return false;
}
//检查45°,以4X4为例,检查以下坐标
//(0,0)(1,1)(2,2)(3,3)
for(int i = row - 1, j = col - 1; i >= 0 && j>= 0; --i , --j){
if(chessboard[i][j] == 'Q') return false;
}
//检查135°
//检查除45°涉及的坐标以外的所有坐标,顺序可能是乱的,但一定都会检查到,不理解子集画一画想一想
for(int i = row - 1, j = col + 1; i >= 0 && j < n; --i , ++j){//注意条件,j要++
if(chessboard[i][j] == 'Q') return false;
}
return true;
}
public:
vector<vector<string>> solveNQueens(int n) {
//定义单行棋盘chessboard
vector<string> chessboard(n, '.');
backtracking(n, 0, chessboard);
return res;
}
};
本文作者:dayceng
本文链接:https://www.cnblogs.com/DAYceng/p/17209162.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
Recommend
-
11
leetcode算法笔记:二叉树,动态规划和回溯法广发证券 技术工程师写的比较匆忙,测试用例是能全部跑通的,不过考虑内存和效率的话,还有许多需要改进的地方,请多指教...
-
4
Armin's BlogHDU2553 N皇后(回溯)December 22, 2015题目链接 N 皇后问题 题解:首先应该意识到,在棋盘(二维数组)中,同...
-
8
*n 皇后问题 研究的是如何将 n 个皇后放置...
-
3
两两交换链表中的节点 力扣题目链接(opens new window) 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能...
-
5
复原IP地址 力扣题目链接(opens new window) 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。 有效的 IP 地址 正好...
-
7
【LeetCode回溯算法#07】子集问题I+II,巩固解题模板并详解回溯算法中的去重问题 ...
-
7
递增子序列 力扣题目链接(opens new window) 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。...
-
8
用最少数量的箭引爆气球 力扣题目链接(opens new window) 在二维空间中有许多球形的气球。对于每个气球,提...
-
12
不同的二叉搜索树 力扣题目链接(opens new window) 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
-
10
【LeetCode.384打乱数组】Knuth洗牌算法详解 前两天看...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK