BFS实现8数码问题,思考与总结


今天中午学习了二叉树的线索化与线索化遍历,突然有一种想实现八数码问题的冲动,因为它的初级解决方式是BFS(广度优先搜索算法)。于是我开始编程。

没想到一编就是一个下午,一直编到了晚上8点。期间出现了很多问题。

1.拷贝函数拷贝完之后,对目标对象进行的操作,会影响源对象。

原来的代码:

             ints(ints obj){//拷贝构造函数
int i,j;
data=new myInt[3][3];
for(i=0;i<3;i++) for(j=0;j<3;j++){//拷贝data
data[i][j]=obj.data[i][j];//问题点
}
this.parent=obj.parent;}//拷贝父编号

修改后:

         ints(ints obj){//拷贝构造函数
int i,j;
data=new myInt[3][3];
for(i=0;i<3;i++) for(j=0;j<3;j++){//拷贝data
data[i][j]=new myInt();
data[i][j].set(obj.data[i][j].get());
}
this.parent=obj.parent;//拷贝父编号
}

这个问题涉及到了java的对象引用方式。在出现问题的语句上,我直接使用了“=”进行赋值,传给等号左边的相当于只是右边的指针,而不是值,因为没有用“new”新建对象。修改后,新建了对象,并且进行了传值。

2.程序能跑起来,但是一直处于循环状态,无法跑出正确的答案。

开始我按照脑海里的“BFS是树的按层遍历”来编制代码。写完后发现出现了问题。

原先的代码:

         while(front<rear){
int lastRear=rear; //临时队尾
for(int i=front;i<lastRear;i++){ //树的按层遍历。对当前层全部执行出队操作。
ints tmp=queue.get(front++); //出队
//对这个出队节点进行四个方向上的变换,构造出新的叶子节点,让他们入队
for(int j=0;j<4;j++){ //进行4个方向上的变换
ints node=new ints(tmp); //拷贝出新的结点
if(node.Move(j) && (!FindInQueue(node)) ){ //如果变换成功 并且这个结点在队列中是新的
node.parent=front-1;
if(node.FindTarget(target)){
System.out.println("找到");
System.out.println(node);
return; //找到目标,跳出循环
}
queue.add(node);
rear++; //入队
System.out.println(node);
}
}
}
}

按层遍历是试用一个队列,首先根节点入队,然后由根节点派生出叶子节点入队,遍历每一层,使树一层一层增加,进行遍历。

java源码

 import java.util.*;

 public class demo {

     public demo() {
// TODO Auto-generated constructor stub
} public static void main(String[] args) {
EightNumProblem proble=new EightNumProblem();
proble.solve();
} } class EightNumProblem{
int source[][] ={{1,2,3},{4,5,0},{6,7,8}};//构造一个二维 3 * 3 数组【源数据】
int target[][] ={{1,2,3},{4,5,6},{7,8,0}};//构造一个二维 3 * 3 数组【目标数据】
class myInt{
int i=0;
int get(){return i;}
void set(int in){i=in;}
myInt(){}
myInt(int in){i=in;}
}
class ints{ //解决问题的数据结构。
protected myInt[][] data=new myInt[3][3];//内部数据
protected int parent;//父编号,用于回溯
ints(){ }
ints(ints obj){//拷贝构造函数
int i,j;
data=new myInt[3][3];
for(i=0;i<3;i++) for(j=0;j<3;j++){//拷贝data
data[i][j]=new myInt();
data[i][j].set(obj.data[i][j].get());
}
this.parent=obj.parent;//拷贝父编号
}
ints(int[][] in){
int i,j;
for(i=0;i<3;i++) for(j=0;j<3;j++) data[i][j]=new myInt(in[i][j]);
}
int get(int i,int j){
return data[i][j].get();
}
void set(int i,int j,int in){
data[i][j].set(in);
}
public String toString(){//重写用于打印
int i,j;
String re=new String("");
for(i=0;i<3;i++){
for(j=0;j<3;j++){
re+=Integer.toString(data[i][j].get());
if(j!=2) re+=",";}
re+="\n";
}
return re;
}
void FindPos(int target,myInt X,myInt Y){//寻找目标元素,并传指 X、Y 返回。
int i,j;
for(i=0;i<3;i++) for(j=0;j<3;j++) if(target==data[i][j].get()){//循环,找到目标元素
X.set(i);//传指
Y.set(j);
return;
}
}
boolean Move(int mod){
myInt X=new myInt();
myInt Y=new myInt();
FindPos(0,X,Y);
int x=X.get();
int y=Y.get();//找到0的位置
// System.out.println("x="+x+"y="+y);
switch(mod){
case 0://上
if(y>0) swap(x,y,x,y-1);
else return false;
break;
case 1://下
if(y<2) swap(x,y,x,y+1);
else return false;
break;
case 2://左
if(x>0) swap(x,y,x-1,y);
else return false;
break;
default://右
if(x<2) swap(x,y,x+1,y);
else return false;
break;
}
return true;
}
void swap(int x1,int y1,int x2,int y2){//(x1,y1)与(x2,y2)交换
int tmp=data[x1][y1].get(); //tmp=x1
data[x1][y1].set(data[x2][y2].get()); //x1=x2
data[x2][y2].set(tmp); //x2=tmp
}
boolean FindTarget(int[][] obj){//找到目标
int i,j;
for(i=0;i<3;i++) for(j=0;j<3;j++) if(obj[i][j]!=data[i][j].get()) return false;//只要有一个不符,错误
return true;
}
boolean equalWith(ints obj){//与目标相同
int i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
if(obj.data[i][j].get()!=data[i][j].get()) return false;//只要有一个不符,错误
return true;
}
} List<ints> queue=new ArrayList<ints>();//设置队列 EightNumProblem(){} boolean FindInQueue(ints elem){//查看队列中是否有该元素
for(int i=0;i<queue.size();i++){
if(elem.equalWith(queue.get(i))){
return true;
}
} return false;
} void solve(){
ints root=new ints(source);//用源数据构造根节点
root.parent=-1;//父编号设置特殊值: -1
int front=0;
int rear=0;
queue.add(root);//根节点入队
rear++;
int flag=0;
while(front<rear && flag<100000){
for(int j=0;j<4;j++){
flag++;
ints tmp=queue.get(front); //出队
ints node=new ints(tmp); //拷贝出新的结点
if(node.Move(j) && (!FindInQueue(node)) ){ //如果变换成功 并且这个结点在队列中是新的
node.parent=front;
if(node.FindTarget(target)){
System.out.println("找到");
PrintSource(node);
return; //找到目标,跳出循环
}
queue.add(node);
rear++; //入队
}
}
front++;
}
}
void PrintSource(ints obj){
while(obj.parent!=-1){
System.out.println(obj);
obj=queue.get(obj.parent);
}
}
}

BFS实现8数码问题,思考与总结的更多相关文章

  1. HDU 1043 Eight (BFS&#183;八数码&#183;康托展开)

    题意  输出八数码问题从给定状态到12345678x的路径 用康托展开将排列相应为整数  即这个排列在全部排列中的字典序  然后就是基础的BFS了 #include <bits/stdc++.h ...

  2. BFS:八数码问题

    #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> ...

  3. BFS(八数码) POJ 1077 || HDOJ 1043 Eight

    题目传送门1 2 题意:从无序到有序移动的方案,即最后成1 2 3 4 5 6 7 8 0 分析:八数码经典问题.POJ是一次,HDOJ是多次.因为康托展开还不会,也写不了什么,HDOJ需要从最后的状 ...

  4. UVALive 6665 Dragon’s Cruller --BFS,类八数码问题

    题意大概就是八数码问题,只不过把空格的移动方式改变了:空格能够向前或向后移动一格或三格(循环的). 分析:其实跟八数码问题差不多,用康托展开记录状态,bfs即可. 代码: #include <i ...

  5. [cdoj1380] Xiper的奇妙历险(3) (八数码问题 bfs + 预处理)

    快要NOIP 2016 了,现在已经停课集训了.计划用10天来复习以前学习过的所有内容.首先就是搜索. 八数码是一道很经典的搜索题,普通的bfs就可求出.为了优化效率,我曾经用过康托展开来优化空间,甚 ...

  6. 八数码问题+路径寻找问题+bfs(隐式图的判重操作)

    Δ路径寻找问题可以归结为隐式图的遍历,它的任务是找到一条凑够初始状态到终止问题的最优路径, 而不是像回溯法那样找到一个符合某些要求的解. 八数码问题就是路径查找问题背景下的经典训练题目. 程序框架 p ...

  7. 【算法】BFS+哈希解决八数码问题

    15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖“X”; 拼图的目的是安排 ...

  8. hdu-1043(八数码+bfs打表+康托展开)

    参考文章:https://www.cnblogs.com/Inkblots/p/4846948.html 康托展开:https://blog.csdn.net/wbin233/article/deta ...

  9. 【基础练习】【BFS+A*】codevs1225八数码难题题解

    题目描写叙述 Description Yours和zero在研究A*启示式算法.拿到一道经典的A*问题,可是他们不会做,请你帮他们. 问题描写叙述 在3×3的棋盘上,摆有八个棋子,每一个棋子上标有1至 ...

随机推荐

  1. 书籍推荐-An introduction to Data Science

    为什么要读这本书? 该书是由我们老师推荐的,通过学习此数,可以了解R语言的使用,也可以知道基本的数据分析方法. 看到Creating a Data Set in R -- 24页面

  2. minikube配置CRI-O作为runtime并指定flannel插件

    使用crio作为runtime后,容器的启动将不依赖docker相关的组件,容器进程更加简洁.如下使用crio作为runtime启动一个nginx的进程信息如下:根进程(1)->conmon-& ...

  3. HMAC算法原理

    HMAC算法是一种基于密钥的报文完整性的验证方法 ,其安全性是建立在Hash加密算法基础上的.它要求通信双方共享密钥.约定算法.对报文进行Hash运算,形成固定长度的认证码.通信双方通过认证码的校验来 ...

  4. 一个简单的 ValueTask 的示例

    Task 确实有潜在的缺点,特别是对于实例创建很多  并且高吞吐量和性能是关键问题的场景  :  Task 是一个类.作为一个类,这意味着任何需要创建一个对象的操作都需要分配一个对象,分配的对象越多, ...

  5. Web应急:搜索引擎劫持

    当你直接打开网址访问网站,是正常的,可是当你在搜索引擎结果页中打开网站时,会跳转到一些其他网站,比如博彩,虚假广告,淘宝搜索页面等.是的,你可能了遇到搜索引擎劫持. 现象描述 从搜索引擎来的流量自动跳 ...

  6. Python的设计哲学--zen of Python

               Python的设计哲学--zen of Python Beautiful is better than ugly. 优美胜于丑陋 Explicit is better than ...

  7. 基于.NET平台常用的框架整理 转自 http://www.cnblogs.com/zhuyongblogs/p/5353751.html

    常用的一些开源组件整理: 导出Excel报表的插件:NOPI.dll(基于微软OpenXml实现)开源的作业调度和自动任务框架:Quartz.NET用于大数据搜索引擎的全文检索框架:Lucene.ne ...

  8. windows电脑ssh连接安卓termux

    最近跟风一个优秀的同事玩起了termux,明明一个简单的ssh,搞了我两天,差点崩溃 一怒之下,觉得很有必要写一篇博客警醒自己 初期,在某某荚下载了高级终端,然后跟着教程配置(https://www. ...

  9. python 数据结构之图的储存方式

    参考链接:https://blog.csdn.net/u014281392/article/details/79120406 所描述的图的结构为: 下面介绍不同的储存方式,我想不必详细分别是每个名称都 ...

  10. set容器与map容器的简单应用

    set容器中一些函数,取自百度其他大佬已总结好的,如有侵权,请联系删除! set的各成员函数列表如下: c++ stl容器set成员函数:begin()--返回指向第一个元素的迭代器 c++ stl容 ...