N皇后

力扣题目链接(opens new window)

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

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

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

示例 1:

  • 输入:n = 4
  • 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
  • 解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

  • 输入: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;
}
};

【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用)的更多相关文章

  1. 剑指offer系列——二维数组中,每行从左到右递增,每列从上到下递增,设计算法找其中的一个数

    题目:二维数组中,每行从左到右递增,每列从上到下递增,设计一个算法,找其中的一个数 分析: 二维数组这里把它看作一个矩形结构,如图所示: 1 2 8 2 4 9 12 4 7 10 13 6 8 11 ...

  2. LeetCode二维数组中的查找

    LeetCode 二维数组中的查找 题目描述 在一个 n*m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增.请完成一个搞笑的函数,输入这样的一个二维数组和一个整数,判断数 ...

  3. 【LeetCode】剑指 Offer 04. 二维数组中的查找

    二维数组查找:线性查找法 有二维数组: [  [1,   4,  7, 11, 15],  [2,   5,  8, 12, 19],  [3,   6,  9, 16, 22],  [10, 13, ...

  4. Java数组排序基础算法,二维数组,排序时间计算,随机数产生

    import java.util.Arrays; //包含Arrays import java.util.Random; public class HelloWorld { public static ...

  5. 《剑指Offer》算法题——二维数组查找

    题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. class Solutio ...

  6. 递归分治算法之二维数组二分查找(Java版本)

    [java] /** * 递归分治算法学习之二维二分查找 * @author Sking 问题描述: 存在一个二维数组T[m][n],每一行元素从左到右递增, 每一列元素从上到下递增,现在需要查找元素 ...

  7. java se系列(四) 函数、数组、排序算法、二分法、二维数组

    1 函数 1.1  数的概述 发现不断进行加法运算,为了提高代码的复用性,就把该功能独立封装成一段独立的小程序,当下次需要执行加法运算的时候,就可以直接调用这个段小程序即可,那么这种封装形形式的具体表 ...

  8. 【2048小游戏】——原生js爬坑之遍历算法显示二维数组内容

    引言:做2048小游戏会将横纵方向的数字内容,存储在一个二维数组中,要将这个二维数组中的内容显示在页面上,就一定要用遍历算法来实现了. 一.二维数组存储    首先考虑用二维数组存储所有行数,列数   ...

  9. 【算法编程 C++ Python】二维数组查找

    题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 思路: 最简单:每一行都 ...

  10. C# 实现二维数组的排序算法(代码)

    class Order { /// <summary> /// 对二维数组排序 /// </summary> /// <param name="values&q ...

随机推荐

  1. ubuntu22.04安装mysql5.7

    22版本的ubuntu默认安装mysql8.0版本,要想安装5.x的版本就得下载安装,这个比较详细的教程,可以参考: https://www.cnblogs.com/juanxincai/p/1648 ...

  2. Linux服务器时间校准

    当发现系统时间不正确时可以通过 date 命令查看Linux系统的当前时间 最简便的修改方法,我们可以通过 date -s "20190917 22:04:00" 来直接修改Lin ...

  3. 最长上升子序列(LIS) dp典型例题(tzoj 矩形嵌套,Rectangles )

    5985: 矩形嵌套 题意:求最长递增子序列(包含两个元素) 思路:先找出关系式子: li=lj+1(当ai<aj时) 两层循环 第一层i从1-n 第二层j 从0-i :求出i前面的每个j 的m ...

  4. Docker学习——Docker 三剑客(七)

    Docker Compose 简介 Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责arg>...] [options] [COMMAND] ...

  5. 由浇花工具开始IOT物联网平台之开始前言篇【1】

    在2020年时,突然有个想法,就是做个浇花工具,因为平时喜欢养花,有时忘记浇花,有时感觉手动浇花太麻烦,所以做个这个小玩意,是用.NET 开发的WinForm小程序,来控制单片机,带动水泵浇花,还可以 ...

  6. mysql之字段约束-第五篇

    数据库的完整性约束是为了在数据库应用中保证数据的一致性和正确性.所以在创建数据表时定义其字段的约束是非常有必要的. 主键约束 主键(primary key)是一个列或者列的组合,其值能唯一地标识表中的 ...

  7. simis报错总结

    --笔记开始: 1.在前台模块处理时,[单位应收核定]比[人员缴费信息]的在职人员多一人,但是总金额一样,可能是以下原因造成!!! A.从后台看,若正常核定在职的ab08比ac13多一个人,可能是ac ...

  8. 2022竞赛新方法学习1--学习Proceedings of SAT Competition 2022 : Solver and Benchmark Descriptions

    Proceedings of SAT Competition 2022 : Solver and Benchmark Descriptions https://helda.helsinki.fi/ha ...

  9. SpringBoot - Lombok使用详解3(@NoArgsConstructor、@AllArgsConstructor、@RequiredArgsConstructor)

    五.Lombok 注解详解(3) 5,@NoArgsConstructor 注解在类上,为类提供一个无参的构造方法. 注意: 当类中有 final 字段没有被初始化时,编译器会报错,此时可用 @NoA ...

  10. Vue修改

    今天做的是一个Vue的修改操作: Vue主要是用来做视图来显示数据的,理解起来的话可能比较困难,学了好几天了,才刚摸到一点头绪,还是需要努力