回溯

  • 回溯(Back Tracking)
  • 提出八皇后问题(Eight Queens)
    • 初步思路一:暴力出奇迹
    • 初步思路二:根据题意减少暴力程度
    • 初步思路三:回溯法(回溯+剪枝)
  • 四皇后 - 回溯法图示
  • 八皇后 - 回溯法图示
  • n皇后实现
    • 合法性检查
    • 从某一行开始摆放皇后
    • 摆放所有皇后
    • 打印
    • n皇后 - 完整实现
  • n皇后优化 - 合法性检查优化
    • 合法性检查优化 O(n) -> O(1)
    • 完整实现
  • n皇后优化 - 位运算
  • LeetCode 51.N皇后
  • LeetCode 52.N皇后 II
    • 无敌解法

数据结构与算法笔记:恋上数据结构笔记目录

回溯(Back Tracking)

回溯可以理解为:通过选择不同的岔路口来通往目的地(找到想要的结果)

  • 每一步都选择一条路出发,能进则进,不能进则退回上一步(回溯),换一条路再试

树的先序遍历图的深度优先搜索(DFS)八皇后走迷宫都是典型的回溯应用

下图中红色代表实际路线绿色代表回溯

不难看出来,回溯很适合使用递归

提出八皇后问题(Eight Queens)

初步思路一:暴力出奇迹

从 64 个格子中选出任意 8 个格子摆放皇后,检查每一种摆法的可行性
一共 C648C_{64}^{8}C648​ 种摆法(大约4.4 * 109 种摆法)

初步思路二:根据题意减少暴力程度

很显然,每一行只能放一个皇后,所以共有 88 种摆法(16777216 种),检查每一种摆法的可行性

初步思路三:回溯法(回溯+剪枝)

在解决八皇后问题之前,可以先缩小数据规模,看看如何解决四皇后问题

四皇后 - 回溯法图示


每次走到死路:

  • 回溯到上次路口,走另一条路;
    如果上次路口的全部路都是死路:

    • 回溯到上上次路口…

回溯途中夹杂着剪枝操作:即不走确定是死路的路,走到每个路口的时候,先判断一下这条路口的路中哪些确定是死路,就可以直接跳过这些路。

八皇后 - 回溯法图示



n皇后实现

合法性检查

// 存放每一个皇后的列号(在第几列)
// cols[row] = col 表示第col行第row列摆放了皇后
int[] cols;
// 一共有多少种合理的摆法
int ways = 0;
/*** 判断第row行第col列是否可以摆放皇后*/boolean isValid(int row, int col) {for (int i = 0; i < row; i++) {// 第col行第row列已经摆放了皇后if (cols[i] == col) return false;// 第i行的皇后根第row行第col列格子处在同一斜线上// 45度角斜线: y-y0 = (x-x0), 则 (y-y0)/(x-x0) = 1, 表示为45度角的斜线if (Math.abs(col - cols[i]) == row -i) return false;}return true;
}

从某一行开始摆放皇后

/*** 从第 row 行开始摆放皇后*/
void place(int row) {// 如果已经放到了第n行,说明找到了一种n皇后的解法if (row == cols.length) {ways++;return;}for (int col = 0; col < cols.length; col++) {if (isValid(row, col)) {// 将row行col列摆放上皇后cols[row] = col; place(row + 1);}}
}

摆放所有皇后

/** * n皇后, 摆放所有皇后*/
void placeQueens(int n) {if (n < 1) return;// 初始化cols = new int[n];place(0); // 从第0行开始放置皇后System.out.println(n + "皇后一共有" + ways + "种摆法");
}

打印

void show() {for (int row = 0; row < cols.length; row++) {for (int col = 0; col < cols.length; col++) {if (cols[row] == col) { // 摆放了皇后System.out.print("1 ");} else {System.out.print("0 ");}}System.out.println();}System.out.println("--------------------------");
}

n皇后 - 完整实现

public class Queens {public static void main(String[] args) {new Queens().placeQueens(8);}// cols[row] = col 表示第col行第row列摆放了皇后int[] cols;// 一共有多少种合理的摆法int ways = 0;/** * n皇后, 摆放所有皇后*/void placeQueens(int n) {if (n < 1) return;// 初始化cols = new int[n];place(0); // 从第0行开始放置皇后System.out.println(n + "皇后一共有" + ways + "种摆法");}/*** 从第 row 行开始摆放皇后*/void place(int row) {// 如果已经放到了第n行,说明找到了一种n皇后的解法if (row == cols.length) {ways++;return;}for (int col = 0; col < cols.length; col++) {if (isValid(row, col)) {// 将row行col列摆放上皇后cols[row] = col; place(row + 1);}}}/*** 判断第row行第col列是否可以摆放皇后*/boolean isValid(int row, int col) {for (int i = 0; i < row; i++) {// 第col行第row列已经摆放了皇后if (cols[i] == col) return false;// 第i行的皇后根第row行第col列格子处在同一斜线上// 45度角斜线: y-y0 = (x-x0), 则 (y-y0)/(x-x0) = 1, 表示为45度角的斜线if (Math.abs(col - cols[i]) == row -i) return false;}return true;}
}

n皇后优化 - 合法性检查优化

合法性检查优化 O(n) -> O(1)

之前的合法性检查需要通过遍历数组来实现,现在使用3个boolean数组分别表示:

  • 某一是否有皇后:boolean[] cols;
  • 某一对角线是否有皇后(左上角->右下角):boolean[] leftTop;
  • 某一对角线是否有皇后 (右上角->左下角):boolean[] rightTop;

用这个进行合法性检查只需要 O(1) 的时间复杂度。

需要知道一个小技巧:根据对角线索引(左上、右上情况不同)

for (int col = 0; col < cols.length; col++) {// 第col列已经有皇后, 继续下一轮if (cols[col]) continue;int ltIndexl = row - col + cols.length - 1; // 左上角->右下角的对角线索引// 左上->右下已经有皇后, 继续下轮if (leftTop[ltIndexl]) continue;int rtIndex = row + col;  // 右上角->左下角的对角线索引// 右上->左下已经有皇后, 继续下轮if (rightTop[rtIndex]) continue;// 给该列摆上皇后cols[col] = leftTop[ltIndexl] = rightTop[rtIndex] = true;place(row + 1); // 该列摆已经摆好了皇后,继续下一行// 这一步很关键, 列、对角线都是牵一发而动全身的影响, 需要重置cols[col] = leftTop[ltIndexl] = rightTop[rtIndex] = false;
}

完整实现

public class Queens2 {public static void main(String[] args) {new Queens2().placeQueens(8);}// 该变量不是必须, 仅仅是为了打印int[] queens;// 标记着某一列是否有皇后了boolean[] cols;// 标记着某一对角线是否有皇后了(左上角->右下角)boolean[] leftTop;// 标记着某一对角线是否有皇后了(右上角->左下角)boolean[] rightTop;// 一共有多少种合理的摆法int ways = 0;/*** n皇后*/void placeQueens(int n) {if (n < 1) return;// 初始化queens = new int[n];cols = new boolean[n]; // 总共有n列leftTop = new boolean[(n << 1) - 1]; // n条对角线rightTop = new boolean[leftTop.length]; // 上面已经做过一次运算,无需再做place(0); // 从第0行开始摆放皇后System.out.println(n + "皇后一共有" + ways + "种摆法");}/*** 从第 row 行开始摆放皇后*/void place(int row) {// 如果已经放到第n行,说明找到了一种n皇后的摆法if (row == cols.length) {ways++;show();return;}for (int col = 0; col < cols.length; col++) {if (cols[col]) continue; // 第col列已经有皇后, 继续下一轮int ltIndexl = row - col + cols.length - 1;if (leftTop[ltIndexl]) continue;int rtIndex = row + col;if (rightTop[rtIndex]) continue;queens[row] = col;cols[col] = leftTop[ltIndexl] = rightTop[rtIndex] = true;place(row + 1); // 这一列摆了皇后,继续下一列cols[col] = leftTop[ltIndexl] = rightTop[rtIndex] = false;}}void show() {for (int row = 0; row < queens.length; row++) {for (int col = 0; col < queens.length; col++) {if (queens[row] == col) { // 摆放了皇后System.out.print("1 ");} else {System.out.print("0 ");}}System.out.println();}System.out.println("--------------------------");}}

n皇后优化 - 位运算

可以利用位运算进一步压缩八皇后的空间复杂度

/*** 八皇后优化 - 位运算*/
public class Queens {public static void main(String[] args) {new Queens().place8Queens();}// 该变量不是必须, 仅仅是为了打印int[] queens;// 标记着某一列是否有皇后了// 比如 00100111 代表0、1、2、5列已经有皇后byte cols; // byte是8位// 标记着某一对角线是否有皇后了(左上角->右下角)short leftTop; // short是16位// 标记着某一对角线是否有皇后了(右上角->左下角)short rightTop; // short是16位// 一共有多少种合理的摆法int ways = 0;/*** n皇后*/void place8Queens() {queens = new int[8];place(0); // 从第0行开始摆放皇后System.out.println("八皇后一共有" + ways + "种摆法");}/*** 从第 row 行开始摆放皇后*/void place(int row) {// 如果已经放到第8行,说明找到了一种8皇后的摆法if (row == 8) {ways++;show();return;}for (int col = 0; col < 8; col++) {int colV = 1 << col; // 00000001if((cols & colV) != 0) continue; // col列已经有皇后int ltV = 1 << (row - col + 7);if ((leftTop & ltV) != 0) continue;int rtV = 1 << (row + col);if ((rightTop & rtV) != 0) continue;queens[row] = col;cols |= colV;leftTop |= ltV;rightTop |= rtV;place(row + 1); // 这一列摆了皇后,继续下一列cols &= ~colV;leftTop &= ~ltV;rightTop &= ~rtV;}}void show() {for (int row = 0; row < 8; row++) {for (int col = 0; col < 8; col++) {if (queens[row] == col) { // 摆放了皇后System.out.print("1 ");} else {System.out.print("0 ");}}System.out.println();}System.out.println("--------------------------");}}

LeetCode 51.N皇后

leetcode_51_N皇后:https://leetcode-cn.com/problems/n-queens/

class Solution {public List<List<String>> solveNQueens(int n) {return placeQueens(n);}int[] cols; // cols[row] = col; 表示row行col列摆放了皇后List<List<String>> queens;List<List<String>> placeQueens(int n) {if (n < 1) return null;cols = new int[n];queens = new ArrayList<>();place(0); // 从第0行开始摆return queens;}// 在第row行摆放皇后void place(int row) {if (row == cols.length) {queens.add(put());return;}for (int col = 0; col < cols.length; col++) {if (isValid(row, col)) {cols[row] = col; // 摆放皇后place(row + 1); // 去row+1行摆放皇后}}}boolean isValid(int row, int col) {for (int i = 0; i < row; i ++) {if (cols[i] == col) return false;// 看作两个点: (row, col)、(i, cols[i]), 斜率为1则在斜对角 if (row - i == Math.abs(cols[i] - col)) return false;}return true;}    // 将结果放入List中List<String> put() {List<String> list = new ArrayList<>();StringBuilder sb;for (int row = 0; row < cols.length; row++) {sb = new StringBuilder();for (int col = 0; col < cols.length; col++) {if (col == cols[row]) {sb.append("Q");} else {sb.append(".");}}list.add(sb.toString());}return list;}
}

leetcode 的排名看看就好,有点玄学

LeetCode 52.N皇后 II

leetcode_52_N皇后 II: https://leetcode-cn.com/problems/n-queens-ii/

class Solution {public int totalNQueens(int n) {return placeQueens(n);}boolean[] cols;   // 列上是否有皇后boolean[] leftTop;    // 对角线左上角->右下角是否有皇后boolean[] rightTop; // 对角线右上角->左下角是否有皇后int ways = 0;      // 摆列次数int placeQueens(int n) {if (n < 1) return 0;cols = new boolean[n];leftTop = new boolean[(n << 1) - 1];rightTop = new boolean[leftTop.length];place(0);return ways;}void place(int row) {if (row == cols.length) {ways++;return;}for (int col = 0; col < cols.length; col++) {if (cols[col]) continue;int ltIndex = row - col + cols.length - 1 ;if (leftTop[ltIndex]) continue;int rtIndex = row + col;if (rightTop[rtIndex]) continue;cols[col] = leftTop[ltIndex] = rightTop[rtIndex] = true;place(row + 1);cols[col] = leftTop[ltIndex] = rightTop[rtIndex] = false;}}
}

无敌解法

评论区看到这个代码,把我给整笑了,

【恋上数据结构】回溯、剪枝(八皇后、n皇后)、LeetCode51.N皇后、LeetCode52.N皇后 II相关推荐

  1. MJ恋上数据结构(第1季 + 第2季)笔记

    文章转载自:https://blog.csdn.net/weixin_43734095/article/details/104847976 恋上数据结构完整笔记(第1季 + 第2季) 前言 数据结构 ...

  2. 【恋上数据结构】串匹配算法(蛮力匹配、KMP【重点】、Boyer-Moore、Karp-Rabin、Sunday)

    串(Sequence) 串(前缀.后缀) 串匹配算法 蛮力(Brute Force) 蛮力1 – 执行过程 + 实现 蛮力1 – 优化 蛮力2 – 执行过程 + 实现 蛮力 – 性能分析 KMP 蛮力 ...

  3. 基数排序及其思想 C++代码实现及分析 恋上数据结构笔记

    文章目录 复习梗概 算法思想 时间及空间复杂度 基数排序基础版代码 及输出结果 计数排序函数 基数排序函数 可视化输出 另一种思路 完整版代码 复习梗概 思想 如何取数字各个位位数 计数排序保证稳定性 ...

  4. 计数排序及其改进 C++代码实现与分析 恋上数据结构笔记

    文章目录 复习梗概 算法思想 基础思想 改进空间复杂度,改进不能对负数进行排序问题 改进稳定性 计数排序时间空间复杂度 计数排序基础版 代码及输出 计数排序第一次改进版 代码及输出 计数排序终极版 代 ...

  5. 快速排序 C++代码实现及其算法思想及时间复杂度分析及优化 恋上数据结构笔记

    文章目录 复习梗概 算法思想 算法复杂度分析及稳定性 如何优化? 快速排序改进版代码C++ 快速排序个人青春版代码 完整代码 复习梗概 算法思想,别的排序名字直接就能让人联想到它的算法思想,唯独快速排 ...

  6. 归并排序算法 C++实现与时间复杂度(考过)恋上数据结构笔记

    复习梗概 画图,自己整个数组,看代码写步骤,这个对理解归并排序还是很有必要的 合并两个有序数组的merge函数写法 时间复杂度的分析方法!!! 其实我觉得去b站找个动态的步骤分解视频也是不错的复习方法 ...

  7. 插入排序算法 及其二分搜索优化版 C++代码实现 恋上数据结构笔记

    复习梗概 文章目录 复习梗概 插入排序算法思想 插入排序时间复杂度与特性(多少,与什么有关?) 插入排序基础版 插入排序2nd优化版(优化了哪里?) !!!插入排序二分搜索优化版(优化了哪里?如何优化 ...

  8. 堆排序 C++代码实现及思想 排序过程输出 恋上数据结构笔记

    复习梗概 文章目录 复习梗概 什么是堆思想? 堆排序算法怎么来的? 什么是下滤?代码 什么是建堆?代码 堆排序本体 代码及排序过程输出 和时间复杂度 完整代码 什么是堆思想? 最大堆:树形结构,每一个 ...

  9. 2021-10-15 红黑树 概念和平衡操作理解以及与AVL对比分析 恋上数据结构笔记

    文章目录 红黑树的由来 红黑树需要遵守的五大规则 红黑树与4阶B树的相互转换!! 红黑树的插入(12种情况) 红黑树的删除(3大类情况) 红黑树的平衡 以及与AVL树的性能比较 红黑树的由来 红黑树: ...

最新文章

  1. 炒冷饭系列:设计模式 装饰模式
  2. python3爬虫实例代码-Python3爬虫带上cookie的实例代码
  3. 10款WordPress的插件让你的网站的移动体验
  4. 2020-12-11 Python yield 使用浅析
  5. 接口测试工具postman(六)添加变量(参数化)
  6. Python3 爬虫学习笔记 C03 【Ajax 数据爬取】
  7. w10电脑c盘满了怎么清理_Win10专业版电脑c盘满了怎么清理?教你一招快速清理C盘...
  8. NET在后置代码中输入JS提示语句(背景不会变白)
  9. 列注释_技术贴 | 宏基因组分箱 (Binning)第四课——COG EC RNA注释统计
  10. MYSQL GDB 崩溃调试
  11. 《CSDN实训》工时统计(未完)
  12. 2016PHP开发者大会
  13. JAVA学习——GUI鼠标画图交互练习
  14. 基于排序变换混沌置乱算法的图像加密系统
  15. 【金融大脑-一支优秀的队伍】比赛经验分享
  16. 短期突击面试攻略,收offer如砍瓜切菜!!!
  17. Altium designer AD原理图导入word文档、pdf,生成矢量图的方法;
  18. unraid教程贴备忘
  19. 2020蓝桥杯模拟赛题目解析(上)
  20. HBase(1):简介

热门文章

  1. 1.2.3 TCP/PI参考模型(应用层、传输层、网际层、网络接口层)、五层参考模型(应用层、传输层、网络层、数据链路层、物理层)、OSI与TCP/IP参考模型比较(转载)
  2. Java中Spring报错org.springframework.core.annotation.AnnotationUtils.clearCache()V
  3. Js判断是否在微信浏览器中打开和微信版本号
  4. ssms .net3.5_SSMS 18.4中SQL Server查询存储选项
  5. java jdk1.8 API
  6. node.js下npm安装太慢,如何解决
  7. python 魔法方法
  8. PostgreSQL高可用性、负载均衡、复制与集群方案介绍
  9. POJ1273 裸裸的网络流
  10. C++静态库与动态库(转)