回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并以此慢慢地扩大问题规模,迭代地逼近最终问题的解。这种迭代类似于穷举并且是试探性的,因为当目前的可能答案被测试出不可能可以获得最终解时,则撤销当前的这一步求解过程,回溯到上一步寻找其他求解路径。

为了能够撤销当前的求解过程,必须保存上一步以来的求解路径,这一点相当重要。

光说不做没意思,用学过的算法题来运用一下。

N 皇后问题:在一个 N * N 的国际象棋棋盘中,怎样放置 N 个皇后才能使 N 个皇后之间不会互相有威胁而共同存在于棋局中,即在 N * N 个格子的棋盘中没有任何两个皇后是在同一行、同一列、同一斜线上。

求解思路:最容易想到的方法就是有序地从第 1 列的第 1 行开始,尝试放上一个皇后,然后再尝试第 2 列的第几行能够放上一个皇后,如果第 2 列也放置成功,那么就继续放置第 3 列,如果此时第 3 列没有一行可以放置一个皇后,说明目前为止的尝试是无效的(即不可能得到最终解),那么此时就应该回溯到上一步(即第 2 步),将上一步(第 2 步)所放置的皇后的位置再重新取走放在另一个符合要求的地方…如此尝试性地遍历加上回溯,就可以慢慢地逼近最终解了。

需要解决的问题:如何表示一个 N * N 方格棋盘能够更有效?怎样测试当前所走的试探路径是否符合要求?这两个问题都需要考虑到使用怎样的数据结构,使用恰当的数据结构有利于简化编程求解问题的难度。

为此,我们使用以下的数据结构:

int column[col] = row 表示第 col 列的第 row 行放置一个皇后

boolean rowExists[i] = true 表示第 i 行有皇后

boolean a[i] = true 表示右高左低的第 i 条斜线有皇后(按 →  ↓ 顺序1~ 2*N -1 依次编号)

boolean b[i] = true 表示左高右低的第 i 条斜线有皇后(按 →  ↑ 顺序1~ 2*N -1 依次编号)

对应这个数据结构的算法实现如下:

  1. /**
  2. * 回溯法求解 N 皇后问题
  3. * @author haolloyin
  4. */
  5. public class N_Queens {
  6. // 皇后的个数
  7. private int queensNum = 4;
  8. // column[i] = j 表示第 i 列的第 j 行放置一个皇后
  9. private int[] queens = new int[queensNum + 1];
  10. // rowExists[i] = true 表示第 i 行有皇后
  11. private boolean[] rowExists = new boolean[queensNum + 1];
  12. // a[i] = true 表示右高左低的第 i 条斜线有皇后
  13. private boolean[] a = new boolean[queensNum * 2];
  14. // b[i] = true 表示左高右低的第 i 条斜线有皇后
  15. private boolean[] b = new boolean[queensNum * 2];
  16. // 初始化变量
  17. private void init() {
  18. for (int i = 0; i < queensNum + 1; i++) {
  19. rowExists[i] = false;
  20. }
  21. for(int i = 0; i < queensNum * 2; i++) {
  22. a[i] = b[i] = false;
  23. }
  24. }
  25. // 判断该位置是否已经存在一个皇后,存在则返回 true
  26. private boolean isExists(int row, int col) {
  27. return (rowExists[row] || a[row + col - 1] || b[queensNum + col - row]);
  28. }
  29. // 主方法:测试放置皇后
  30. public void testing(int column) {
  31. // 遍历每一行
  32. for (int row = 1; row < queensNum + 1; row++) {
  33. // 如果第 row 行第 column 列可以放置皇后
  34. if (!isExists(row, column)) {
  35. // 设置第 row 行第 column 列有皇后
  36. queens[column] = row;
  37. // 设置以第 row 行第 column 列为交叉点的斜线不可放置皇后
  38. rowExists[row] = a[row + column - 1] = b[queensNum + column - row] = true;
  39. // 全部尝试过,打印
  40. if(column == queensNum) {
  41. for(int col = 1; col <= queensNum; col++) {
  42. System.out.print("("+col + "," + queens[col] + ")  ");
  43. }
  44. System.out.println();
  45. }else {
  46. // 放置下一列的皇后
  47. testing(column + 1);
  48. }
  49. // 撤销上一步所放置的皇后,即回溯
  50. rowExists[row] = a[row + column - 1] = b[queensNum + column - row] = false;
  51. }
  52. }
  53. }
  54. // 测试
  55. public static void main(String[] args) {
  56. N_Queens queen = new N_Queens();
  57. queen.init();
  58. // 从第 1 列开始求解
  59. queen.testing(1);
  60. }
  61. }

当 N = 8 时,求解结果如下(注:横坐标为 列数, 纵坐标为 行数):

  1. (1,1)  (2,5)  (3,8)  (4,6)  (5,3)  (6,7)  (7,2)  (8,4)    
  2. (1,1)  (2,6)  (3,8)  (4,3)  (5,7)  (6,4)  (7,2)  (8,5)    
  3. (1,1)  (2,7)  (3,4)  (4,6)  (5,8)  (6,2)  (7,5)  (8,3)    
  4. ... ...  
  5. ... ...  
  6. (1,8)  (2,2)  (3,4)  (4,1)  (5,7)  (6,5)  (7,3)  (8,6)    
  7. (1,8)  (2,2)  (3,5)  (4,3)  (5,1)  (6,7)  (7,4)  (8,6)    
  8. (1,8)  (2,3)  (3,1)  (4,6)  (5,2)  (6,5)  (7,7)  (8,4)    
  9. (1,8)  (2,4)  (3,1)  (4,3)  (5,6)  (6,2)  (7,7)  (8,5)   
当 N = 4 时,求解结果如下:
  1. (1,2)  (2,4)  (3,1)  (4,3)    
  2. (1,3)  (2,1)  (3,4)  (4,2)  

有时间的话将输出的结果打印为直观一点的符号形式或界面形式更好。
小结:
1、根据问题选择恰当的数据结构非常重要,就像上面 a 、b 标志数组来表示每一条斜线的编号顺序以及方向都相当重要。看书的时候也是费了些时间来理解的,呼…另外,queens [col] = row 数组只是用了一维而不是二维来表示纵横交错的方格棋盘上特定位置是否有皇后也是比较经济而有意思的。
2、正确运用、组织所确定的数据结构到算法的实现逻辑中也是很重要的,就像代码中的 isExists(int row, int col) 方法内的 (rowExists[row] || a[row + col - 1] || b[queensNum + col - row]) 就是很明确的理解了尝试放置皇后的位置的 x ,y 坐标与斜线之间的数值关系,才使得算法得以正确执行。当然,对于斜线的编号、顺序也是相当重要的。
本文转自 xxxx66yyyy 51CTO博客,原文链接:http://blog.51cto.com/haolloyin/353105,如需转载请自行联系原作者

回溯法求解N皇后问题(Java实现)相关推荐

  1. 回溯法求解N皇后问题及其时间复杂度分析

    回溯法求解N皇后问题及其时间复杂度分析 一.回溯法简介 1. 什么是回溯法? 2. 回溯法的时间复杂度分析 蒙特卡罗方法 蒙特卡罗方法在回溯法求解时间复杂度中的应用 二.回溯法求解N皇后问题 1. 回 ...

  2. 回溯法求解N皇后问题

    问题描述 给定一个n*n的棋盘,棋盘中有一些位置不能放皇后.现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行.同一列或同一条对角线上,任意的两个白皇后都不在同一行.同一列或同一 ...

  3. 回溯法——求解N皇后问题

    问题描写叙述 八皇后问题是十九世纪著名数学家高斯于1850年提出的.问题是:在8*8的棋盘上摆放8个皇后.使其不能互相攻击,即随意的两个皇后不能处在允许行.同一列,或允许斜线上. 能够把八皇后问题拓展 ...

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

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

  5. n皇后问题-回溯法求解

    n皇后问题-回溯法求解 1.算法描述 在n×n格的国际象棋上摆放n个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. n皇后是由八皇后问题演变而来的.该问题 ...

  6. java背包算法回溯法_【算法分析】实验 4. 回溯法求解0-1背包等问题

    [TOC] 实验内容 本实验要求基于算法设计与分析的一般过程(即待求解问题的描述.算法设计.算法描述.算法正确性证明.算法分析.算法实现与测试),通过回溯法的在实际问题求解实践中,加深理解其基本原理和 ...

  7. 回溯法求解K图染色问题(java版)

    回溯法:K图着色问题 问题描述: 问题分析: 伪代码: 局限性 具体实现 问题描述: 对如图 1 所示的图,采用局部搜索算法,求其对应的 3 着色方案. 问题分析: 题目的目标是用3种颜色,将图1中的 ...

  8. 【算法分析】实验 4. 回溯法求解0-1背包等问题

    目录 实验内容 实验目的 实验结果 步骤1:描述与分析 步骤2:策略以及数据结构 步骤3 步骤4 步骤5 步骤6 实验总结 实验内容 本实验要求基于算法设计与分析的一般过程(即待求解问题的描述.算法设 ...

  9. 【回溯法】八皇后问题

    问题描述 在国际象棋棋盘(8×8)(8\times8)(8×8)上放置八个皇后,要求每两个皇后之间不能直接吃掉对方. 皇后可以攻击处于同一行.同一列和同一对角线上的棋子. 思路分析 八皇后问题可以使用 ...

最新文章

  1. 探索 20 年,依然难落地,是谁阻止了教授们的「学术休假」?
  2. 学习前端你必须看过这几本书!
  3. boost::type_index::type_id相关的测试程序
  4. rest模式get,post,put,delete简单讲解
  5. 射灯安装方法图解_射灯更换安装方法图解
  6. visual studio 解决方案项目结构部署和配置
  7. unity3d模拟树叶飘动_Unity3D独立游戏开发日记(一):动态生成树木
  8. Windows 11 小技巧- 安装
  9. 弹弹堂sf发布网_私服冒险岛,新开私服冒险岛,心动sf冒险岛发布网,最新开的私服冒险岛应该如何快速的获取魅力呢?...
  10. Notepad++快速选中多行
  11. 【elasticsearch】 Elasticsearch集群规模和容量规划的底层逻辑
  12. C++高级教程之多线程
  13. 【转】灵活运用 SQL SERVER FOR XML PATH
  14. 读书笔记之文件和注册表操作
  15. linux防火墙应用,Linux防火墙iptables基本应用
  16. windows8无法播放优酷土豆视频解决方法
  17. java.lang.ClassNotFoundException: org.jaxen.JaxenException 解决方法
  18. Android用MediaRecorder实现MPEG4视频监控
  19. SS LSTM全文翻译
  20. 模仿努比亚网站二(广告区域)

热门文章

  1. Java中UDP协议的基本原理和简单用法
  2. Ubuntu设置环境变量
  3. 2.6.21相比2.4.18内核机制变更
  4. 在JavaScript中实现yield,实用简洁实现方式。
  5. 可视化图形(一):散点图-scatter()
  6. 收集的伪静态中经常使用的一些参数解释,如[NC,L,QSA]等
  7. AIX命令参考大全,卷 4,n - r
  8. 大型互联网公司分布式ID方案总结
  9. Android使用 SO 库时要注意的一些问题
  10. 锐捷EG易网关远程命令执行漏洞-1