首页 > 编程语言 >【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用)

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

时间:2023-03-12 21:22:17浏览次数:51  
标签:10 return int chessboard 算法 vector 回溯 col row

N皇后

力扣题目链接(opens new window)

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

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

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

示例 1:

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

标签:10,return,int,chessboard,算法,vector,回溯,col,row
From: https://www.cnblogs.com/DAYceng/p/17209162.html

相关文章

  • 代码随想录算法Day39 | 62.不同路径,63. 不同路径 II
    62.不同路径题目链接:62.不同路径-力扣(LeetCode)思路确定dp数组(dptable)以及下标的含义dp[i][j]:表示从(0,0)出发,到(i,j)有dp[i][j]条不同的路径。确定递推公式......
  • 基于5G密集网络模型的资源分配和负载均衡算法matlab仿真
    目录一、理论基础二、核心程序三、测试结果一、理论基础首先,5G模型的基本结构如下所示:  超密集网络是5G通信系统中的重要技术,是现在通信界的研究热点。系统中......
  • 基于模糊神经网络的异构网络环境下垂直切换算法的matlab仿真与分析
    目录一、理论基础二、核心程序三、测试结果一、理论基础切换是移动通信系统必备的关键功能之一。移动通信网络中发生在同构网络不同基站间的水平切换主要是为了保......
  • P4047 [JSOI2010]部落划分
    地图上标注了n个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。......
  • 经典的排序算法 - 冒泡排序
    冒泡排序算法应该可以说是很经典的一种对数据进行排序的的算法了,甚至在很多的介绍算法的数据中,它可能还是放在最前面开始讲解的。冒泡排序算法到底是咋样的呢?冒泡这个说法又......
  • 3-10
    编写函数求两个整数的最大公约数和最小公倍数。1#include<iostream>2usingnamespacestd;34voidfun(inta,intb){5inttemp;6intm=a*......
  • PAT Basic 1021. 个位数统计
    PATBasic1021.个位数统计1.题目描述:给定一个 \(k\) 位整数 \(N=d_{k−1}10^{k−1}+⋯+d_110^1+d_0 (0≤d_i≤9, i=0,⋯,k−1, d_{k−1}>0)\),请编写程序统计每......
  • PAT Basic 1020. 月饼
    PATBasic1020.月饼1.题目描述:月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量......
  • 数据结构与算法
    释义:数据结构啥指相互之前存在一种或多种特定关系的数据元素的集合算法是规则的有限集合,是为解决特定问题而规定的一系列操作程序=数据结构+算法语句频度:是指该语句......
  • Java算法——字符串
    344.反转字符串编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组s的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)......