《数据结构》——邓俊辉版本 读书笔记

今天学习了回溯法,有两道习题,一道N皇后,一道迷宫寻径。今天,先解决N皇后问题。由于笔者

擅长java,所以用java重现了八皇后问题

注意是java实现版本,其实用什么语言,都一样。


写在前面的话

如果想看代码,直接往下拉,注释写的很清楚,凭借注释应

该也能理解八皇后问题。


1. 首先知道什么是八皇后问题,不过,相信看到这里的同学,都应该知道八皇后

问题,简单概括为:每个皇后的势力范围如下图红线标注所示,也就是横纵轴、两条对角线 。在一个皇后的势力范围内,就不能再出现其他皇后了。

2. 这里选用经典的八皇后问题,想让八个皇后,在棋盘上相安无事。用代码怎么实现?首先,明白一件事,电脑就是小萌新啊,只会计算最简单的计算,2+3,它兴许都得差分为(1+1)+(1+1+1)来计算,唯一的优势就是计算的速度比我们快。

3. 刚看到回溯法的时候,想着要用代码,写出来。感觉无从下手,以为很难的,以为电脑不会笨的一个一个去尝试,以为有很神奇的方法存在。学完以后,才发现,很简单,发现回溯法,也是从穷举起手的,只不过带着一定的策略,说白了,优化过的暴力求解。以后,遇到各种听着很高大上的问题别慌,比如,动态规划、深度优先搜索。都是穷举!!只是加了一些策略。I can I believe !你之所能,是因为你想信你能!


解题思路:

 都是文字讲解,主要是笔者没有图!!!诶,希望你们读完,能学会八皇后问题。如果文字看不下去。可以直接去看代码,注释写的很详细。
  1. 一行只能有一个皇后,这个根据游戏规则中的皇后的势力就可以得知。
  2. 首先让皇后从起点开始摆放。因此,第一个皇后的位置先摆放在(0,0)上,第二个皇后根据游戏规则,只能在第二行寻找合适的位置。在第二个皇后的位置确定以后,再去第三行寻找第三个皇后的位置,以此内推。直到所有的皇后的位置都确定了,问题的一组解,就出来了!但是问题并不是这么简单。
  3. 比如现在有三个皇后A、B、C。A在第一行,B在第二行都找到合适的位置了,在第三行所有的位置都是A或者B皇后的势力范围。尴尬了,皇后之间要打架了。怎么办?聪明的你,可能会想到让B皇后,重新找一个合适的位置(这个合适的位置,从B当前的下一个位置开始寻找),然后再来为 C选择合适的位置。如果C还是没有合适的位置,就再次为B寻找合适的位置,循环往复这个动作。
  4. 细心的你,可能会有疑问,每次C皇后,找不到合适的位置,就去让B重新寻找合适的位置难道B皇后就一直会有新的合适的位置吗?答案是否定的,当B皇后在它所处的行,再也找不到合适的位置,就说明A皇后的位置,也需要变动了。A皇后的位置变动,跟其他皇后不一样,因为,当前B、C皇后都因为没有合适的位置,所有,就没有摆放在棋盘上。因此,这里棋盘上就一个皇后A,所有它只需要右移一个位置即可。A皇后位置更新以后,就再去为B皇后找合适位置,如果有合适位置,就再去为C皇后寻找合适的位置;如果B皇后没有合适的位置,就再次右移A皇后,循环上面的过程。
  5. 有的同学,可能会有疑问,照你说的,计算机就是通过穷举解决问题的,上面的解题过程,计算机确实也是笨笨的一个一个位置去寻找的。那么这算法有效率吗?答案是有效率的。不知你们发现没有,我们每个为下一个皇后寻找合适的位置的时候,都与之前存在的皇后做了比较。只有,有合适的位置,才会摆放皇后;这里寻找合适的位置,会排除没有意义的试探,比如,将皇后摆放在之前某个皇后的势力范围内。没有排除没意义的试探,才是正正的傻傻的穷举。我们的算法,是带着策略的穷举。
  6. 上述每次为下一个皇后,寻找合适的位置,就是试探。每次试探不到合理的位置,回头去改变前一个皇后的位置,就是回溯;排除没意义的试探,就是剪枝。上诉的解题过程,就是一个回溯法的思路

用代码求解八皇后问题

    回溯法,如何回溯?需要我们的栈结构,来保存我们每次已经找到合适位置的皇后的列坐标,坐标从0开始。为什么不保存行坐标呢?因为,一行只能有一个皇后,栈中皇后的下标,就是对应皇后的行坐标啦。

首先我们定义一个皇后类

在皇后类中,我们写了一个方法判断,两个皇后之间是否安全
/*** 皇后类*/
class Queen {//    棋盘中的皇后下标,从0开始int x;int y;public Queen(int x, int y) {this.x = x;this.y = y;}public Queen() {}/*判断皇后所处位置是否相互安全,接收一个皇后对象安全返回真,不安全返回假*/public boolean isSafe(Queen q) {return !(this.x == q.x ||this.y == q.y ||this.x + this.y == q.x + q.y ||this.x - this.y == q.x - q.y);}}

测试类中定义一个方法,求新皇后应该放置的合理位置

这个方法中创建一个新皇后对象,与之前已经存在的皇后作比较。比较函数则是皇后类定义的方法。找到合适位置,就返回合适的位置的列坐标;返回-1,代表没有合适位置。
      /*** 为皇后寻找合法位置*/public int setY(int N, ArrayList<Integer> list, int y) {Queen q;Queen q_exist;
//       做个标记,boolean flag ;
//        遍历一行的所有位置for (int z = y; z < N; z++) {
//          为每个格子创建一个新的皇后q = new Queen(list.size(), z);
//            起始标记为真flag = true ;for (int x = 0; x < list.size(); x++) {
//          创建之前存在的皇后q_exist = new Queen(x, list.get(x));
//           安全 就返回真,循环作比较if (!(q_exist.isSafe(q))) {
//          只要和之前存在的的任何一个皇后冲突,
//          并且说明当前皇后不是真的,将标记改为假
//          并跳出当前跳出循环,flag = false;break;}}
//          如果标记还为真,说明皇后是对滴,并返回新皇后的列坐标,结束函数if (flag) {list.add(q.y);return q.y;}}
//          函数没有提前结束,说明没有合适的位置,返回-1;return -1;}

求出所有组解

跟着注释看,相信大家都能学会
/*** 求解N皇后的问题,并给出所有的解,找到一组解,就让最后的皇后换位置,直达第一个皇后的角标越界* 笔者认为 next_y 变量的动态赋值的 需要很好的理解* @param N 表示一共有多少皇后,也是棋盘的规模  N x N*/private void placeQueen(int N) {
//        表示一共有多少种解法int all = 1;
//        代表皇后的y坐标int y = 0;
//        在当前行,下一个位置开始重新寻找合适的位置int next_y = 0;
//        建立一个栈,用于回溯
//        栈中存放的是皇后的列坐标,也就是y坐标ArrayList<Integer> list = new ArrayList();
//         定义一个皇后Queen queen;
//        开始计算每种情况while (true) {
//                先判断之前有没有皇后存在if (list.size() == 0) {
//                创建一个皇后对象,从0开始摆放queen = new Queen(0, y);
//                将皇后的y坐标记录下来,而x坐标就是栈的长度-1list.add(queen.y);
//              下一次,栈再次空了,说明第一行的皇后需要右移y++;} else {
//                 如果已经有皇后存在,既需要为当前皇后寻找合适的位置了
//                就是看setY()方法的返回值//                在寻找当前皇后合适位置之前,我们先判断是否求出一组解
//                如果,求出一组解,我们打印当前的解if (list.size() == N) {System.out.print("第" + all + "组解:  ");all++;for (int num : list) {System.out.print(num + " ");}System.out.println();//                    当前一组解,已经打印完毕
//                    需要重新寻找新的解
//                    我们这里让栈顶的皇后,重新寻找位置
//                    改变next_y的值就好了。next_y = list.get(list.size() - 1) + 1;
//                    记得弹出栈顶的皇后list.remove(list.size() - 1);
//                    }} else {
//  *****************************************************
//                就是这里 我上午写了一个比较臃肿的方法
//                我下午回来,试着重构,结果改了3个小时,才改对了!
//                简直日了狗了,澡都没赶上洗,午觉也没睡成//                    setY()方法中,寻找到合适的位置,就会自动添加到栈中
//                    因此,这里只关注返回-1的情况
//                    返回-1 ,表示需要回溯前一个皇后了if (-1 == setY(N, list, next_y)) {//                    记录下,先开始的位置
//                     这个next_y 很重要的
//                     如果需要在当前行 重新寻找合适的位置
//                      就需要从当前位置的下一个位置开始遍历寻找
//                     因此next_y的值,为当前位置+1next_y = list.get(list.size() - 1) + 1;list.remove(list.size() - 1);//                    这里针对的是求出所有解的情况下,退出
//                        当求出最后一组解的时候,再次将栈顶元素弹出,重新回溯的话
//                        必然会回溯到最初的皇后,即第一个皇后,弹出第一个皇后的时候
//                        栈为空。这里就需要作出判断了
//                        第一个皇后的下一个位置,是否超出了N的限制if (list.size() == 0) {
//                            判断是右移还是退出if (next_y < N) {list.add(next_y);next_y = 0;} else {break;}}} else {
//                        如果setY()没有返回-1,说明找到了合适的位置
//                        就需要为下一个皇后寻找合适的位置了
//                        在新的一行开始寻找,必然需要从0开始寻找位置next_y = 0;
//  ****************************************************}}}}}

测试方法

不了解Junit测试框架的同学,这里将 placeQueen(6);写到main函数里面即可。
    @Testpublic void test() {placeQueen(6);}

运行结果

六皇后问题一共有四组解

得出的解,只是皇后从0开始的 “列坐标” ,它们的行坐标,是0到N-1顺序排列;
比如 : 1 3 5 0 2 4,就是下面的情况
列  行
1   0
3   1
5   2
0   3
2   4

4 5

下面运算一下经典的八皇后,看看一个有多少组解

在图中可以看出,一共有92组解。并且可以看到我们计算所有的解,只花费了0.026s。还是很快的。


八皇后问题,已经被我们解决了,用的就是回溯法,可能你

已经对回溯法,有了一定的了解,就是试探-剪枝-回溯

希望你下次遇到什么新的算法策略。不要紧张,它们只是穷

举,带有策略的穷举,策略还需要你来写呢!


回溯法还有一个好玩的题,迷宫寻径。过几天,我在写一个关于迷宫寻径的博

客,题目有千千万,但是思想是一样的。希望大家和我,都能在解题过程中,对

回溯法有一个很好的掌握,下一次遇到问题的时候,能熟练使用回溯法解决它。

八皇后问题——列出所有的解,可推至N皇后相关推荐

  1. 苏丹的继承者(八皇后问题)之python解

            八皇后问题 早在1848年就已提出,可谓是历史悠久,经久不衰,曾一度难倒了高斯之流的顶级数学大师,但是,在计算机发明之后,这道曾经的世界难题被以无数种方式轻松解决,这也不由得令我们广大 ...

  2. 八皇后问题、N皇后问题回溯法详解

    /* * 回溯法解N皇后问题 * 使用一个一维数组表示皇后的位置 * 其中数组的下标表示皇后所在的行 * 数组元素的值表示皇后所在的列 * 这样设计的棋盘,所有皇后必定不在同一行 * * 假设前n-1 ...

  3. 回溯算法详解之全排列、N皇后问题

    回溯算法详解 回溯算法框架.解决一个回溯问题,实际上就是一个决策树的遍历过程.你只需要思考 3 个问题: 1.路径:也就是已经做出的选择. 2.选择列表:也就是你当前可以做的选择. 3.结束条件:也就 ...

  4. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    linux系统编程之进程(八):守护进程详解及创建,daemon()使用 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等 ...

  5. Linux Shell脚本入门教程系列之(八)Shell printf命令详解

    本文是Linux Shell脚本系列教程的第(八)篇,更多shell教程请看:Linux Shell脚本系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中,已经对She ...

  6. 深度学习之图像分类(二十八)-- Sparse-MLP(MoE)网络详解

    深度学习之图像分类(二十八)Sparse-MLP(MoE)网络详解 目录 深度学习之图像分类(二十八)Sparse-MLP(MoE)网络详解 1. 前言 2. Mixture of Experts 2 ...

  7. (十八)享元模式详解(都市异能版) - 转

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 魔都. 自从越狱风波过去以后,小左的生活便又回到了之前的节奏,依旧是每日徘徊在魔都某天桥,继续着自己的算命之旅. 说起这次越狱风波,着 ...

  8. Android绘图Canvas十八般武器之Shader详解及实战篇(上)

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 前言 Android中绘图离不开的就是Canvas了,Canvas是一个庞大的知识体系,有java层的,也有jni层深入到Frame ...

  9. n皇后问题python_Python:回溯法解N皇后问题

    这个算法采用的是一维数组,皇后个数即数组长度,数组值即对应行皇后所在的列. 按照每行至上而下,每一行从第一列起尝试放置皇后,每次仅需判断对于已经放置的皇后是否产生冲突.如果某个位置可以放置,则放置皇后 ...

最新文章

  1. Android开发笔记——Android 9发送通知
  2. python卷积神经网络cnn的训练算法_【深度学习系列】卷积神经网络CNN原理详解(一)——基本原理...
  3. 编译报错+解决方法:错误: 找不到符号
  4. XP操作系统最优视觉效果
  5. python urllib模块学习笔记
  6. 硬盘整数分区最精确地方法(转载)
  7. 推荐阅读啃饼随笔的《玩聚网的案例分析》
  8. html 好看表格样式,简单但是好看的表格样式
  9. 漏洞补丁:windwos补丁下载(MS17-010)
  10. 嵌入式学习--1线协议(以ds18b20为例)
  11. android谷歌卫星地图,高德地图安卓端实现卫星地图路网功能
  12. Edit plus | ecli pse配色方案
  13. BES(恒玄) 平台 复杂按键 实现
  14. 解决Please make sure you have the correct access rights and the repository exists 问题
  15. 道翰天琼认知智能为您解密:Rust语言杀疯了!前有谷歌高薪争夺 Rust 人才,Facebook再官宣加入Rust基金会
  16. 微信公众号更新缓存问题--批量添加版本号
  17. Java基础知识——Java语言基础
  18. 2020-10-01 交换机通过CRT保存配置-SSH
  19. android 手机多用户探索,如何在主用户删除其他用户
  20. 小米4 第三方re奇兔_【沙发管家】带你了解小米电视的几个使用技巧!|小米电视|沙发管家|机顶盒|智能电视|遥控器...

热门文章

  1. 网络名人点赞重庆“智慧城市”建设
  2. 2020牛客暑期多校训练营(第八场)E.Enigmatic Partition(差分+隔项差分/dp+暴力)
  3. 2020牛客暑期多校训练营Enigmatic Partition(数学,二阶隔项差分)
  4. B.FRIENDit壁虎忍者笔记本支架,铝合金电脑支架,便携折叠、可调节桌面电脑架
  5. 多个label,可变长情况下使用Masonry
  6. 高分考生学口腔和学计算机,川内高校今年录取最高分659分 为川大口腔医学专业录取...
  7. 基于C#弹幕类射击游戏的实现——(六)爆炸效果
  8. HTML标记【图片的使用】!
  9. matlab的无穷大怎样表示_matlab中从一到无穷大怎么表示
  10. 给js添加类名/添加元素标签/