前端「N皇后」递归回溯

/*

  • @Author: yang
  • @Date: 2021-09-27 19:40:34
  • @LastEditors: yang
  • @LastEditTime: 2021-10-19 19:57:43
  • @FilePath: \demo\广度优先遍历.js
    */

前端「N皇后」递归回溯经典问题

先来看问题,其实问题不难理解:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],

["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
皇后,是国际象棋中的棋子,意味着国王的妻子。皇后只做一件事,那就是“吃子”。当她遇见可以吃的棋子时,就迅速冲上去吃掉棋子。当然,她横、竖、斜都可走一到七步,可进可退。
思路
乍一看这种选出全部方案的问题有点难找到头绪,但是其实仔细看一下,题目已经限定了皇后之间不能互相攻击,转化成代码思维的语言其实就是说每一行只能有一个皇后,每条对角线上也只能有一个皇后,
也就是说:

在一列上,错。

[
‘Q’, 0
‘Q’, 0
]
复制代码
在左上 -> 右下的对角线上,错。

[
‘Q’, 0
0, ‘Q’
]
复制代码
在左下 -> 右上的对角线上,错。

[
0, ‘Q’
‘Q’, 0
]

那么以这个思路为基准,我们就可以把这个问题转化成一个「逐行放置皇后」的问题,思考一下递归函数应该怎么设计?

对于 n皇后 的求解,我们可以设计一个接受如下参数的函数:

  1. rowIndex 参数 代表当前正在尝试第几行放置皇后
  2. prev 参数 代表之前的行已经放置的皇后位置比如[1,3]就代表第0行(数组下标)的皇后放置在位置1 第一行的皇后放置在3
    当rowIndex === n 即说明这个递归成功的放置了n个皇后 一路畅通无阻的到达终点 每次的放置都顺利的通过了我们的限制条件
    那么就把这次的prev作为一个结果放置到全局res结果数组中

实现
理想总是美好的,虽然目前为止我们的思路很清晰了,但是具体的编码还是会遇到几个头疼的问题的。

当前一行已经落下一个皇后之后,下一行需要判断三个条件:

  1. 在这一行上 之前不能摆放过皇后
    2 在对角线1,也就是[左下 - 右上] 这条对角线上 之前不能摆放过皇后
    3 在对角线2 也就是[右上 -> 左下] 这条对角线上 之前不能摆放过皇后
对角线1

直接通过这个点的纵横坐标rowIndex + columnIndex 相加,相等的话 就在同条对角线1上
对角线1:
2n-1个
i+j
前端「N皇后」递归回溯_第1张图片

对角线2

直接通过这个点的横纵坐标 rowIndex - columnIndex 相减,相等的话 就在对角线2上
对角线2:
2*n - 1个
i - j + n - 1
前端「N皇后」递归回溯_第2张图片

所以:
1 用columns数据记录摆放的列下标 摆放后直接标记为true即可
2 用dia1数组记录摆放过的对角线1下标,摆放后直接把下标rowIndex + columnIndex标记为true即可
3 用dia2 数组记录摆放过的对角线2下标 摆放后直接把下标rowIndex - columnIndex 标记为true即可
4 递归函数 的参数prev代表每一行中皇后放置的列数,比如prev[0] = 3 代表第 0 行皇后放在第3列 以此类推
5 每次进入递归函数前 先把当前项对应的列 对角线1 对角线2 的下标标记为true,带着标记后的状态进入递归函数
并且在推出本次递归后 需要把这些状态重置为false 再进入下一轮循环

有了这几个辅助知识点 就可以开始编写递归函数了,在每一行 我们都不断的尝试一个坐标点,只要它和之前已有的结果不冲突 那么就可以放入数组中作为下一次递归的开始值
这样,如果递归函数顺利的来到了rowIndex === n 的情况 说明之前的条件全部满足了 一个n皇后的解就产生了 把prev这个一维数组通过辅助函数恢复成题目要求的二维数组即可

/**
 * @param {number} n
 * @return {string[][]}
 */

let solveNQueens = function(n){
    let res = []
    // 已经摆放皇后的列下标
    let columns = []
    // 已摆放皇后的对角线1下标 左下 ——> 右上
    // 计算某个坐标是否在这个对角线的方式是 [行下标 + 列下标] 是否相等
    let dia1 = []
    // 已摆放皇后的对角线2下标 左上 -> 右下
    // 计算某个坐标是否在这个对角线的方式是[行下标 - 列下标] 是否相等
    let dia2 = []
    //  在选择当前的格子后 记录状态
    let record = (rowIndex,columnIndex,bool)=>{
        columns[columnIndex] = bool
        dia1[rowIndex + columnIndex] = bool
        dia2[rowIndex - columnIndex] = bool
    }
    // 尝试在一个n皇后问题中  摆放第index行内的皇后位置
    let putQueen = (rowIndex,prev)=>{
        if(rowIndex === n){
            res.push(generateBoard(prev))
            return 
        }

        // 尝试摆放第Index行的皇后 尝试[0,n-1]列
        for(let columnIndex = 0; columnIndex 

你可能感兴趣的:(算法,数据结构)