递归与回溯

先来看问题,其实问题不难理解:

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

 

上图为 8 皇后问题的一种解法。

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

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

示例:

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

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

思路

乍一看这种选出全部方案的问题有点难找到头绪,但是其实仔细看一下,题目已经限定了皇后之间不能互相攻击,转化成代码思维的语言其实就是说每一行只能有一个皇后,每条对角线上也只能有一个皇后

也就是说:在一列上,错。


[ 'Q', 0 'Q', 0 ]
  1. 在左上 -> 右下的对角线上,错。
[ 'Q', 0 0, 'Q' ]

  1. 在左下 -> 右上的对角线上,错。
[ 0, 'Q' 'Q', 0 ]

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

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

  1. rowIndex 参数,代表当前正在尝试第几行放置皇后。
  2. prev 参数,代表之前的行已经放置的皇后位置,比如 [1, 3] 就代表第 0 行(数组下标)的皇后放置在位置 1,第 1 行的皇后放置在位置 3。

rowIndex === n 即说明这个递归成功的放置了 n 个皇后,一路畅通无阻的到达了终点,每次的放置都顺利的通过了我们的限制条件,那么就把这次的 prev 做为一个结果放置到一个全局的 res 结果数组中。

树状图

这里我尝试用工具画出了 4皇后 的其中的一个解递归的树状图,第一行我直接选择了以把皇后放在2为起点,省略了以 放在1放在3放在4 为起点的树状图,否则递归树太大了图片根本放不下。

注意这里的 放在x,为了方便理解,这个 x 并不是数组下标,而是从 1 开始的计数。

 

在这次递归之后,就求出了一个结果:[1, 3, 0, 2]

 

你可以在纸上按照我的这种方式继续画一画尝试以其他起点开始的解法,来看看这个算法的具体流程。

实现

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

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

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

难点在于判断对角线上是否摆放过皇后了,其实找到规律后也不难了,看图:

对角线1

直接通过这个点的横纵坐标 rowIndex + columnIndex 相加,相等的话就在同在对角线 1 上:

image

 

对角线2

直接通过这个点的横纵坐标 rowIndex - columnIndex 相减,相等的话就在同在对角线 2 上:

image

 

所以:

  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 < n; columnIndex++) {
      // 在列上不冲突
      let columnNotConflict = !columns[columnIndex]
      // 在对角线1上不冲突
      let dia1NotConflict = !dia1[rowIndex + columnIndex]
      // 在对角线2上不冲突
      let dia2NotConflict = !dia2[rowIndex - columnIndex]

      if (columnNotConflict && dia1NotConflict && dia2NotConflict) {
        // 都不冲突的话,先记录当前已选位置,进入下一轮递归
        record(rowIndex, columnIndex, true)
        putQueen(rowIndex + 1, prev.concat(columnIndex))
        // 递归出栈后,在状态中清除这个位置的记录,下一轮循环应该是一个全新的开始。
        record(rowIndex, columnIndex, false)
      }
    }
  }

  putQueen(0, [])

  return res
}

// 生成二维数组的辅助函数
function generateBoard(row) {
  let n = row.length
  let res = []
  for (let y = 0; y < n; y++) {
    let cur = ""
    for (let x = 0; x < n; x++) {
      if (x === row[y]) {
        cur += "Q"
      } else {
        cur += "."
      }
    }
    res.push(cur)
  }
  return res
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/476059.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot的在线装修管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的在线装修管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

elk7.11.2离线安装

elk与jdk自行搜索下载 package(离线安装包)目录&#xff1a;/home/elk-pak /home/jdk8-pak 安装目录&#xff1a;/usr/local/ jdk8安装 最好查看一下是否存在原服务器自带openJDK rpm -qa|grep java # 有就执行下边卸载命令 无拉倒 rpm -e --nodeps jdk-xxx# 解压jdk8安装包…

你的电脑打不开摄像头问题

我一直以为我电脑上的摄像头老是打不开是因为硬件不匹配的问题。知道我发现了我的拯救者Y7000的机身盘边的“摄像头开关”按钮。。。 我去&#xff0c;你的摄像头开关按钮怎么设置在机身旁边啊。。。。 —————————————————————— 2024年3月21日更新记录&a…

zookeeper快速入门二:zookeeper基本概念

本文是zookeeper系列之快速入门中的第二篇&#xff0c;欢迎大家观看与指出不足。 目录 一、zookeeper的存储结构 二、什么是znode 三、znode节点的四种类型 四、权限控制ACL&#xff08;Access Control List&#xff09; 五、事件监听watcher 一、zookeeper的存储结构 z…

【C++】---string的模拟

【C】---string的模拟 一、string类实现1.string类的构造函数2.swap&#xff08;&#xff09;函数3.拷贝构造函数4.赋值运算符重载5.析构6.迭代器7.operator[ ]8.size9.c_str&#xff08;&#xff09;10.reserve&#xff08;&#xff09;11.resize&#xff08;&#xff09;12.p…

利用python进行接口测试及类型介绍

前言 其实我觉得接口测试很简单&#xff0c;比一般的功能测试还简单&#xff08;这话我先这样说&#xff0c;以后可能会删O(∩_∩)O哈&#xff01;&#xff09;&#xff0c;现在找工作好多公司都要求有接口测试经验&#xff0c;也有好多人问我&#xff08;也就两三个人&#x…

搜维尔科技:利用增强现实改进检验和质量控制流程

直接在工作环境中显示数字指令 DELMIA 质量检查增强体验通过工作环境中的数字指令指导操作员完成检查过程&#xff0c;从而有效地识别、鉴定和报告不合格项。使用增强现实可以显着提高质量控制和检查过程的准确性和速度。由于技术人员可以访问实时信息&#xff0c;因此他们会减…

揭秘爆红AI图像增强神器:Magnific AI如何做到1亿像素放大?

最近有个很火的AI图像增强应用&#xff0c;叫Magnific AI。 你知道吗&#xff0c;它发布一个多月就有40万人注册了&#xff01; 这个应用确实非常实用&#xff0c;它不仅利用AI技术放大了图像&#xff0c;还能提升分辨率&#xff0c;从而使图片呈现得更加清晰。 值得一提的是…

EpiCypher欣博盛生物:染色质研究有望推进精准医疗

精准医疗&#xff0c;也被称为个性化医学&#xff0c;是指使用病人特定的分子特征来指导疾病诊断、监测病情发展与治疗应答、或用于确定药物开发新靶点的医疗模式。 精准医疗原则的应用使癌症治疗领域取得了长足的进步&#xff0c;并有望显著改变多种病症的治疗&#xff0c;包括…

关于OceanBase中旁路导入的应用分享

背景 前段时间&#xff0c;在用户现场协助进行OceanBase的性能测试时&#xff0c;我注意到用户常常需要运用 insert into select 将上亿行的数据插入到一张大宽表里&#xff0c;这样的批量数据插入操作每次都需要耗时半个小时左右。对这一情况&#xff0c;我提议用户尝试采用旁…

基于springboot+vue的失物招领平台

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【软考】生成树

目录 1. 概念2. 图解3. 例题3.1 例题1 1. 概念 1.对于有n个顶点的连通图&#xff0c;至少有n-1条边&#xff0c;而生成树中恰好有n-1条边2.连通图的生成树是该图的极小连通子图3.若在图的生成树中任意加一条边&#xff0c;则必然形成回路4.图的生成树不是唯一的5.从不同的顶点…

python3+requests+unittest:接口自动化测试(一)

简单介绍框架的实现逻辑&#xff0c;参考代码的git地址&#xff1a; GitHub - zhangying123456/python_unittest_interface: pythonunittest接口自动化测试脚本 1.环境准备 python3 pycharm编辑器 2.框架目录展示 &#xff08;该套代码只是简单入门&#xff0c;有兴趣的可…

UE4 面试题整理

1、new与malloc的区别 new&#xff1a; new首先会去调用operator new函数&#xff0c;申请足够的内存&#xff08;大多数底层用malloc实现&#xff09;&#xff0c;然后调用类型的构造函数来初始化变量&#xff0c;最后返回自定义类型的指针&#xff0c;delete先调用析构函数&…

HCIP—BGP路由聚合

在大型网络中&#xff0c;路由条目通常多达成千上万条&#xff0c;甚至几十万条&#xff0c;这给路由设备带来的挑战是&#xff1a;如何存储并有效管理如此众多的路由信息&#xff1f; BGP是一种无类路由协议&#xff0c;支持CIDR、VLSM和路由聚合。路由聚合技术的使用…

【微服务】Nacos配置管理

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;微服务 ⛺️稳中求进&#xff0c;晒太阳 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&am…

JVM垃圾回收之内存分配,死亡对象判断方法

Java 堆是垃圾收集器管理的主要区域&#xff0c;因此也被称作 GC 堆。 堆划分为新生代 老生代 永久代。 下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代&#xff0c;中间一层属于老年代&#xff0c;最下面一层属于永久代。 内存分配原则 对象优先在Eden区域分…

jupyter notebook设置代码提示方法

在命令行运行以下代码&#xff1a; pip install jupyter_contrib_nbextensionsjupyter contrib nbextension install --userpip install jupyter_nbextensions_configuratorjupyter nbextensions_configurator enable --user &#xff08;有时安装第一行后会自动执行第二行&a…

Redis监控工具

Redis 是一种 NoSQL 数据库系统&#xff0c;以其速度、性能和灵活的数据结构而闻名。Redis 在许多领域都表现出色&#xff0c;包括缓存、会话管理、游戏、排行榜、实时分析、地理空间、叫车、聊天/消息、媒体流和发布/订阅应用程序。Redis 数据集完全存储在内存中&#xff0c;这…

八部门联合打击涉税违法犯罪活动,企业合规经营刻不容缓

近期&#xff0c;国家税务总局、公安部、最高人民法院、最高人民检察院、中国人民银行、海关总署、市场监管总局、国家外汇管理局在北京召开全国八部门联合打击涉税违法犯罪工作推进会议。 会议强调八部门将持续深化完善联合打击工作机制&#xff0c;依法严厉打击涉税违法犯罪行…