Java编程思想—八皇后问题(数组法、堆栈法)
Java编程思想—八皇后问题(数组法、堆栈法)
实验题目:回溯法实验(八皇后问题)
实验目的:
(1) 掌握回溯法求解问题的思想
(2) 学会利用其原理求解相关问题
实验要求:
使用贪心法求出给定图各点的最短路径,并计算算法的执行时间,分析算法的有效性。利用回溯法解决八皇后问题,检验结果,并计算算法的执行时间,分析算法的有效性。
测试数据可以通过手工寻找三组满足需要的值,测试数组(M,N),其中 M 代表皇后所在的行,N 代表皇后所在的列。
例如,第一组测试数据:
(1,4)、(2,7)、(3,3)、(4、8)、(5,2)、(6,5)、(7,1)、 (8,6);
第二组测试数据
(1,5)、(2,2)、(3,4)、(4,7)、(5,3)、(6,8)、(7,6)、 (8,1);
第三组测试数据
(1,4)、(2,2)、(3,7)、(4,3)、(5,6)、(6,8)、(7,5)、 (8,1)。
最后与编程求得的结果进行比较。如果这三组数据在最后编程求得的结果中,说明程序的编写基本没有什么问题。
实验内容:
(1)问题描述
八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后。为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。
(2)实验步骤:
数组法:
① 根据 8 皇后问题,可以把其设想为一个数组;
② 根据 8 皇后的规则,可以设想为数组上同一直线,横线,斜线的数字都不能相同,由此可以得出判断条件;
③ 根据判断条件之后,建立回溯点,即可解决问题。
堆栈法:
① 检索当前行是否可以放置一个皇后;
② 利用检索过程,通过递归的方式,来确定每个皇后的位置———回溯的思想。
算法伪代码:
实验结果:
实验代码:
public class EightQueensOfBacktracking {
int count = 0;
//棋盘初始化 清空操作
void initialChessBoard(int chessBoard[][]){
for(int i = 0; i < 8 ; i++){
for(int j = 0; j < 8; j++){
chessBoard[i][j] = 0;
}
}
}
//打印皇后位置
void showLocation(int chessBoard[][]){
System.out.println("————————————");
System.out.println("皇后的坐标为:");
for(int i = 0; i < 8 ; i++){
for(int j = 0; j < 8; j++){
if(chessBoard[i][j] != 0){
System.out.print(" ( " + (i+1) + " , " + (j+1) + " ) ");
}
}
}
System.out.println();
System.out.println("棋盘如下:");
for(int i = 0; i < 8 ; i++){
for(int j = 0; j < 8; j++){
System.out.print(chessBoard[i][j] + " ");
}
System.out.println();
}
}
//行列检查 斜线检查
boolean checkAll(int i, int j, int chessBoard[][]){
int tempI = i;
int tempJ = j;
if((i>7)||(j>7)) return false;
//check column
for(int k = 0; k <= j; k++){
if(chessBoard[i][k] != 0){
return false;
}
}
//check row
for(int k = 0; k <= i; k++){
if(chessBoard[k][j] != 0){
return false;
}
}
//左上斜线检查
while(true){
if(chessBoard[i][j] != 0)
return false;
if((i == 0)||(j == 0)) break;
i--;
j--;
}
//右上斜线检查
i = tempI;
j = tempJ;
while(true){
if(chessBoard[i][j] != 0)
return false;
if((i == 0)||(j == 7)) break;
i--;
j++;
}
return true;
}
//堆栈方法
public void findQueen(int i, int chessBoard[][], EightQueensOfBacktracking eightQueens){
if(i>7){
eightQueens.count++;
eightQueens.showLocation(chessBoard);
}
//回溯法
boolean judge;
for(int m = 0; m<8; m++){
judge = eightQueens.checkAll(i, m, chessBoard);
if(judge){
chessBoard[i][m] = 1;
eightQueens.findQueen(i+1, chessBoard, eightQueens);
chessBoard[i][m] = 0;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//函数调用
EightQueensOfBacktracking eightQueens = new EightQueensOfBacktracking();
//摆法的数量
int count = 0;
int k = 0;//临时变量
int i = 0, j= 0;//i 行 ;j 列
boolean judge = true;//检查结果
//8个皇后,1-8表示
int queens = 1;
//棋盘 8*8
int chessBoard[][] = new int [8][8];
//每次找到解后清盘,即初始化
eightQueens.initialChessBoard(chessBoard);
long startTime2 = System.nanoTime();
//堆栈方法:
eightQueens.findQueen(0, chessBoard, eightQueens);
long endTime2 = System.nanoTime();
//数组方法
//失败1:在当前行非末尾:列+1;
//失败2:在当前行的末尾:判断行,如果是0行,则所有情况已遍历,结束;
// 如果不是,回到上一行,遍历,查找到上一行的棋子,记录位置,找到下一位置,则结束查找上一个位置;
// 如果上一行也遍历完,列的位置为7,上一个位置处没有下一位置,则再找上上一行的位置,进行上面的循环;
// 如果上一行是0行,则也结束遍历,flag为0,结束上一位置的查找;
//成功:标记位置,如果位置标记是8,则为一种方案,输出,方法计数加一,标记减1,queens表示下一个要放的棋子;
// 行加1,列归0,通过失败1和失败2再找到上一个棋子的的位置
long startTime1 = System.nanoTime();
int flag = 1;
while(true){
judge = eightQueens.checkAll(i, j, chessBoard);
//这一行未检查完,检查这一行下一个位置
if((judge == false)&&(j != 7)){
j++;
continue;
}
else if((judge == false)&&(j == 7)){
if(i == 0) break;//表示所有的情况已经遍历,结束循环
i--;//回到上一行
k = 0;//查找,用来遍历
while(true){
if(chessBoard[i][k] != 0){//找到上一行棋子的位置
queens = chessBoard[i][k];//表示这个棋子放到棋盒中
chessBoard[i][k] = 0;//把棋子取走
j = k + 1;//找到下一个位置,准备下一次循环
if(j > 7){//如果上一行也遍历完,找到上上一行
if(i == 0){
flag = 0;
break;//如果上一行是0,那么没有上上一行,结束
}
i--;
k =0;
continue;//重新开始查找棋子
}
break;
}
k++;
}
if(flag == 0){
break;
}
continue;
}
//检查通过,放下棋子,到下一行
chessBoard[i][j] = queens;
if(queens == 8){//找到一种方法
eightQueens.showLocation(chessBoard);
count++;
queens--;
//输出后,假装这个摆法不行,继续查找
}
queens++;//queens的值表示下一个要放的棋子
i++;
j = 0;
}
long endTime1 = System.nanoTime();
System.out.println("数组方法共有" + count + "种摆法");
System.out.println("堆栈递归方法共有" + eightQueens.count + "种摆法");
System.out.println("数组程序运行时间:" + (endTime1 - startTime1) + "ns");
System.out.println("堆栈递归程序运行时间:" + (endTime2 - startTime2) + "ns");
}
}
出现的问题:
问题一:条件检查
实验时,排序的结果出现问题,斜线的情况不能检查出来。
于是我仔细检查了判断部分的代码,发现是变量的重复使用,导致无法正常判断。i和j的值被重复使用。我通过临时变量进行存储,在上一次使用后进行重新赋值,解决了问题,如红圈。
问题二:数组法 跳出循环情况分析
一开始不知道递归方法,想用情况分析,循环查找,但是发现在查找时,只能查找到第一个棋子在1,1位置的情况。
我猜测是跳出循环的判断出了问题,于是在我的检查下,发现下图第一个圈是要跳出循环,结束整个查找,由于是双重循环,所以我直接在第二个圈设置如果i= =0,则结束循环,共两次跳出。
但是我忽略了限制条件,即在i = = 0的时候,并不是都是要结束的,只有圈1的那一种情况才跳出,所以我设置flag变量进行传递,纠正了程序的错误。 更正的代码 见 实验代码 部分。
实验心得:
本次实验体现的是回溯法。经过本次实验,发现自己对回溯的理解并不全面,不会应用。初次做这个题,想的只是遍历,在循环中,进行人工的回溯。后来发现回溯时的情况分析十分复杂,并不能很好的发现并且处理所有情况,只求出4种结果。经过学习后了解到,对于回溯,本题不需要自己考虑情况,只需给出限制条件进行筛选,在满足条件的情况下进行重复调用自身函数,在完成函数后,进行自身位置的值的清空,为之后回溯进行准备即可。让我明白递归是回溯的一种很好的实现方式。
在使用java的过程中,为了解决遇到的问题还进行了调试,让我对debug和调试有了进一步的掌握。
说明:递归法是调用系统堆栈进行操作,所以属于堆栈法。
递归堆栈方法可以参考链接:八皇后递归堆栈方法
Java编程思想—八皇后问题(数组法、堆栈法)的更多相关文章
- Java编程思想学习(十二) 数组和容器
一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的 ...
- Java编程思想里的泛型实现一个堆栈类
觉得作者写得太好了,不得不收藏一下. 对这个例子的理解: //类型参数不能用基本类型,T和U其实是同一类型. //每次放新数据都成为新的top,把原来的top往下压一级,通过指针建立链接. //末端哨 ...
- Java编程思想学习(八) 内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿 ...
- Java编程思想(11~17)
[注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第十一章 持有对象 11.1 泛型和类型安全的容器>eg: List<St ...
- Java编程思想 (1~10)
[注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第一章 对象导论 1.万物皆对象2.程序就是对象的集合3.每个对象都是由其它对象所构成 ...
- 《Java编程思想第四版》附录 B 对比 C++和 Java
<Java编程思想第四版完整中文高清版.pdf>-笔记 附录 B 对比 C++和 Java “作为一名 C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且 Java 的语法无疑是 ...
- Java编程思想总结笔记Chapter 5
初始化和清理是涉及安全的两个问题.本章简单的介绍“垃圾回收器”及初始化知识. 第五章 初始化与清理 目录:5.1 用构造器确保初始化5.2 方法重载5.3 默认构造器5.4 this关键字5.5 清 ...
- Java编程思想——第17章 容器深入研究(two)
六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add ...
- JAVA编程思想——分析阅读
需要源码.JDK1.6 .编码风格参考阿里java规约 7/12开始 有点意识到自己喜欢理论大而泛的模糊知识的学习,而不喜欢实践和细节的打磨,是因为粗心浮躁导致的么? cron表达式使用 设计能力.领 ...
随机推荐
- 【BZOJ3534】【Luogu P3317】 [SDOI2014]重建 变元矩阵树,高斯消元
题解看这里,主要想说一下以前没见过的变元矩阵树还有前几个题见到的几个小细节. 邻接矩阵是可以带权值的.求所有生成树边权和的时候我们有一个基尔霍夫矩阵,是度数矩阵减去邻接矩阵.而所谓变元矩阵树实际上就是 ...
- C#制作的屏幕取色器
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System ...
- nginx第二天
nginx配置文件 配置文件结构 全局配置(user.worker_processes.error_log.pid) events(网络连接相关,worker_connections) http(最重 ...
- IView 使用Table组件时实现给某一列添加click事件
通过给 columns 数据的项,设置一个函数 render,可以自定义渲染当前列,包括渲染自定义组件,它基于 Vue 的 Render 函数. render 函数传入两个参数,第一个是 h,第二个是 ...
- qt5--文件操作
文本文件的读写操作: #include "win.h" #include "ui_win.h" #include <QDebug> #include ...
- Python---函数2---王伟
#### 作用域 ```python#1.作用域:变量生效的区域#2.作用域的分类a.全局作用域(全局变量)- 全局作用域在程序执行时创建,在程序执行结束时销毁- 所有函数以外的区域都是全局作用域- ...
- spark读取hbase(NewHadoopAPI 例子)
package cn.piesat.controller import java.text.{DecimalFormat, SimpleDateFormat}import java.utilimpor ...
- C#抽象类怎么调试
本文出自:https://www.cnblogs.com/2186009311CFF/p/11919030.html C#抽象类怎么调试 按F11调试 参考链接: 快捷键:https://www.cn ...
- web前端_css02
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- CAP拾遗
概念 在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性.可用性.分区容错性三者中的两个,另外一个必须被牺牲 Consistency 对某个指定的客户端来说,读操 ...