- 问题描述
在3×3的方格上分别放置1,2,3,4,5,6,7,8,9的八张排,初始状态为S0,目标状态为Sg,计算出从S0到Sg的方法。
- 代码及说明 /******************************************************************************
* 使用深度优先、广度优先和A*算法解决九宫问题
******************************************************************************/
#include < stdio.h >
#include < iostream.h >
#include < string .h >
#include < stdlib.h >
#include “ data.h “
#include “ queue.h “
#include “ stack.h “
#include “ link.h “
#define DEEPSEARCH 1
#define WIDESEARCH 2
#define ASTART 3
#define SEARCHTYPE WIDESEARCH // 定义使用广度优先
// #define SHOWPROCESS // 定义是否显示中间过程
class SearchTree {
private:
/***********************定义open表的数据类型************************/
#if SEARCHTYPE==WIDESEARCH
Queue open;
#elif SEARCHTYPE==DEEPSEARCH
Stack open;
#else
Link open;
#endif
Stack close;
public:
void init(); //初始化数据
void extend(); //扩展close表尾节点并添加进open表 void moveToClose(); //将open表的头节点移动到close表中
bool success(); //判断搜索是否成功
bool openEmpty(); //判断open表是否为空
void showAnswer(); //显示最终结果
} ;
void SearchTree::showAnswer() {
close.show();
}
bool SearchTree::openEmpty() {
return open.empty();
}
void SearchTree::extend() {
DATATYPE temp[LINE][ROW],buf[LINE][ROW];
Data *pid;
int n,m;
pid=close.getTop(*buf); //将close表的最后一项记录复制到buf中
for(n=0;n<LINE;n++)
for(m=0;m<ROW;m++)
if(buf[n][m]==0)//寻找buf中0所在的位置,0表示空格
goto L1;
L1:
memcpy(temp,buf,DATASIZE*sizeof(DATATYPE));
if(n!=0){ //空格上移
temp[n][m]=temp[n–1][m];
temp[n–1][m]=0;
if(close.exist(*temp)==false) open.push(*temp,&pid);
#ifdef SHOWPROCESS //宏定义,决定时候输出中间过程
cout<<“move below data to open table:“<<endl;
showElement(*temp);
getchar();
#endif
}
memcpy(temp,buf,DATASIZE*sizeof(DATATYPE));
if(n!=2){ //空格下移
temp[n][m]=temp[n+1][m];
temp[n+1][m]=0;
if(close.exist(*temp)==false) open.push(*temp,&pid);
#ifdef SHOWPROCESS
cout<<“move below data to open table:“<<endl;
showElement(*temp);
getchar();
#endif
}
memcpy(temp,buf,DATASIZE*sizeof(DATATYPE));
if(m!=0){ //空格左移
temp[n][m]=temp[n][m–1];
temp[n][m–1]=0;
if(close.exist(*temp)==false) open.push(*temp,&pid);
#ifdef SHOWPROCESS
cout<<“move below data to open table:“<<endl;
showElement(*temp);
getchar();
#endif
}
memcpy(temp,buf,DATASIZE*sizeof(DATATYPE));
if(m!=2){ //空格右移
temp[n][m]=temp[n][m+1];
temp[n][m+1]=0;
if(close.exist(*temp)==false) open.push(*temp,&pid);
#ifdef SHOWPROCESS
cout<<“move below data to open table:“<<endl;
showElement(*temp);
getchar();
#endif
}
}
void SearchTree::moveToClose() {
DATATYPE dt[DATASIZE];
Data *pid;
open.pop(dt,&pid);
close.push(dt,&pid);
}
bool SearchTree::success() {
DATATYPE dt[DATASIZE];
close.getTop(dt);
return memcmp(dt,*sg,DATASIZE*sizeof(DATATYPE))? false:true;
}
void SearchTree::init() {
open.init();
close.init();
//初始节点S0
s0[0][0]=2;s0[0][1]=8;s0[0][2]=3;
s0[1][0]=1;s0[1][1]=0;s0[1][2]=4;
s0[2][0]=7;s0[2][1]=6;s0[2][2]=5;
//目标节点Sg
sg[0][0]=1;sg[0][1]=2;sg[0][2]=3;
sg[1][0]=8;sg[1][1]=0;sg[1][2]=4;
sg[2][0]=7;sg[2][1]=6;sg[2][2]=5;
//显示信息
cout<<“s0:“<<endl;
showElement(*s0);
cout<<“sg:“<<endl;
showElement(*sg);
cout<<“any key to continue“<<endl;
getchar();
open.push(*s0,NULL);
#ifdef SHOWPROCESS
cout<<“below data move to open table:“<<endl;
showElement(*s0);
#endif
}
void main() {
#if SEARCHTYPE==WIDESEARCH
puts(“wide search“);
#elif SEARCHTYPE==DEEPSEARCH
puts(“deep search“);
#else
puts(“astart search“);
#endif
SearchTree st;
st.init();
while(1){
//open表为空,问题无解
if(st.openEmpty()==true){
cout<<“there is no answer for this question, open table is empty“<<endl;
exit(0);
}
//将open表的表头移动到close表中
st.moveToClose();
//判断是否搜索成功
if(st.success()==true){
cout<<“get answer:“<<endl;
st.showAnswer();
exit(0);
}
//扩展close表
st.extend();
}
}
/*********************************************************
* data.h
* 定义基本的数据类型
**********************************************************/
#define
DATATYPE int
//
定义数据类型
#define
LINE 3
//
矩阵的行
#define
ROW 3
//
矩阵的列
#define
DATASIZE LINE*ROW
//
矩阵的大小
class
Data
{ //存储在open和close表中的数据元素
public:
DATATYPE element[DATASIZE]; //九宫图以矩阵的方式存放
Data *pid; //记录close表中父亲节点的位置
Data *next,*pre; //双向链表
int gx,hx;
}
;
class
F
{
public:
int gx,hx;
Data *addr;
F *next;
}
;
void
showElement(
const
DATATYPE
*
dt)
{ //打印数据元素的信息
int i;
for(i=0;i<DATASIZE;i++){
if(i%LINE==0) cout<<endl;
cout<<dt[i]<<“ “;
}
cout<<endl;
}
DATATYPE s0[LINE][ROW],sg[LINE][ROW];
/*********************************************************************************
* link.h
* 定义链表类,用于A*算法中的open表
*********************************************************************************/
class
Link:
public
Queue
{ //链表类
private:
Data head;
F fhead; //估价函数临时表
int len;
public:
void init(); //链表初始化
void show(); //显示链表
void push(DATATYPE *,Data **); //数据入队
void pop(DATATYPE *,Data **); //数据出队
}
;
void
Link::show()
{
if(empty()==true) cout<<“nothing to print, Link is empty“<<endl;
else cout<<“there are “<<len<<“ members in the Link: “<<endl;
Data *temp=head.next;
while(temp){
cout<<“f(x)=“<<temp->gx+temp->hx<<endl;
showElement(temp->element);
temp=temp->next;
}
}
void
Link::init()
{
head.next=head.pid=head.pre=NULL;
fhead.next=NULL;
len=0;
}
void
Link::pop(DATATYPE
*
dt,Data
**
pid)
{
if(empty()==true) {
cout<<“warning: pop Link error beacuse of can not pop a empty Link anykey to exit“<<endl;
getchar();
exit(1);
}
Data *temp=head.next;
F *ftemp;
ftemp=new F;
ftemp->gx=temp->gx;
ftemp->hx=temp->hx;
ftemp->addr=temp->pid;
ftemp->next=fhead.next;
fhead.next=ftemp;
memcpy(dt,temp->element,DATASIZE*sizeof(DATATYPE));
*pid=temp->pid;
head.next=temp->next;
if(head.next) head.next->pre=&head;
delete temp;
len—;
}
void
Link::push(DATATYPE
*
dt,Data
**
pid)
{
int gx,hx;
int n,m,k;
Data *temp,*loc;
F *ftemp;
hx=k=0;
/************************* 计算启发函数 h(x) **************************/
for(n=0;n<LINE;n++)
for(m=0;m<ROW;m++)
if(dt[k++]!=sg[n][m]) hx++;
/************************* 计算 g(x) ************************************/
if(fhead.next!=NULL){ //fhead表中存在数据
for(ftemp=fhead.next;ftemp;ftemp=ftemp->next)
if(ftemp->addr=*pid){
gx=ftemp->gx+1;
break;
}
if(ftemp==NULL) {
puts(“can not caculate function g(x), program will exit“);
exit(1);
}
}
else{ //fhead表为空
gx=0; //根节点
}
/******************************创建新数据********************************/
temp=new Data;
memcpy(temp->element,dt,DATASIZE*sizeof(DATATYPE)); //将dt复制给temp
temp->gx=gx;
temp->hx=hx;
if(pid!=NULL) temp->pid=*pid;
else temp->pid=NULL;
temp->pre=temp->next=NULL;
/******************************将数据添加到链表中**********************/
if(head.next==NULL){ //链表为空
head.next=temp;
temp->pre=&head;
temp->next=NULL;
++len;
return ;
}
else{ //链表不空
loc=head.next;
while(1){
if((temp->gx+temp->hx)<=(loc->gx+loc->hx)){
loc->pre->next=temp;
temp->pre=loc->pre;
temp->next=loc;
loc->pre=temp;
++len;
return;
}
else if(loc->next==NULL){
loc->next=temp;
temp->next=NULL;
temp->pre=loc;
++len;
return;
}
else{
loc=loc->next;
}
}
}
}
/**************************************************************************
* queue.h
* 定义队列类,用广度优先的open表
**************************************************************************/
class
Queue:
private
Data
{ //队列类
private:
Data head,*loc;
int len;
public:
int length(); //返回队列的长度
void init(); //队列初始化
bool empty(); //判断队列是否为空
void show(); //显示队列
bool exist(DATATYPE *); //判断数据是否之前已经在队列中出现过
void push(DATATYPE *,Data **); //数据入队
void pop(DATATYPE *,Data **); //数据出队
Data * getTop(DATATYPE *); //返回队头元素
}
;
void
Queue::show()
{
if(empty()==true) cout<<“nothing to print, queue is empty“<<endl;
else cout<<“there are “<<len<<“ members in the queue: “<<endl;
Data *temp=head.next;
while(temp){
showElement(temp->element);
temp=temp->next;
}
}
bool
Queue::exist(DATATYPE
*
dt)
{
Data *temp=head.next;
while(temp){
if(memcmp(dt,temp->element,DATASIZE*sizeof(DATATYPE))==0) return true;
temp=temp->next;
}
return false;
}
Data
*
Queue::getTop(DATATYPE
*
dt)
{
memcpy(dt,head.next->element,DATASIZE*sizeof(DATATYPE));
return head.next;
}
void
Queue::pop(DATATYPE
*
dt,Data
**
pid)
{
if(empty()==true) {
cout<<“warning: pop queue error beacuse of can not pop a empty queue anykey to exit“<<endl;
getchar();
exit(1);
}
Data *temp=head.next;
memcpy(dt,temp->element,DATASIZE*sizeof(DATATYPE));
*pid=temp->pid;
head.next=temp->next;
if(head.next) head.next->pre=&head;
delete temp;
len—;
if(len==0) loc=&head;
}
void
Queue::push(DATATYPE
*
dt,Data
**
pid)
{
Data *temp=loc;
loc=loc->next=new Data;
memcpy(loc->element,dt,DATASIZE*sizeof(DATATYPE));
if(pid!=NULL) loc->pid=*pid;
else loc->pid=NULL;
loc->next=NULL;
loc->pre=temp;
len++;
}
void
Queue::init()
{
head.next=head.pid=head.pre=NULL;
loc=&head;
len=0;
}
bool
Queue::empty()
{
return len==0? true:false;
}
int
Queue::length()
{
return len;
}
/*********************************************************************************
* stack.h
* 定义栈类,用于三种算法的close表以及深度优先搜索的open表
***********************************************************************************/
class
Stack
{ //栈类
private:
Data head,*loc;
int len;
public:
void init(); //初始化栈
int length(); //返回栈的长度
bool empty(); //判断栈是否为空
void show(); //显示栈
bool exist(DATATYPE *dt); //判断元素是否在栈中已经出现过
void push(DATATYPE *,Data **); //数据入栈
void pop(DATATYPE *,Data **); //数据出栈
Data * getTop(DATATYPE *); //返回栈顶元素
}
;
void
Stack::show()
{
if(empty()==true) cout<<“stack is emtpy “<<endl;
else cout<<“there are “<<len<<“ members in the stack“<<endl;
Data *temp=loc;
int i=0;
while(temp){
cout<<“step “<<i++<<“ :“<<endl;
showElement(temp->element);
temp=temp->pid;
}
}
bool
Stack::exist(DATATYPE
*
dt)
{
Data *temp=head.next;
while(temp){
if(memcmp(temp->element,dt,DATASIZE*sizeof(DATATYPE))==0) return true;
temp=temp->next;
}
return false;
}
Data
*
Stack::getTop(DATATYPE
*
dt)
{
memcpy(dt,loc->element,DATASIZE*sizeof(DATATYPE));
return loc;
}
void
Stack::pop(DATATYPE
*
dt,Data
**
pid)
{
if(empty()==true){
cout<<“warning: pop stack error beacuse of stack is empty anykey to exit“<<endl;
getchar();
exit(1);
}
Data *temp=loc;
loc=loc->pre;
loc->next=NULL;
memcpy(dt,temp->element,DATASIZE*sizeof(DATATYPE));
*pid=temp->pid;
delete temp;
len—;
}
void
Stack::push(DATATYPE
*
dt,Data
**
pid)
{
Data *temp=loc;
loc=loc->next=new Data;
memcpy(loc->element,dt,DATASIZE*sizeof(DATATYPE));
if(pid!=NULL) loc->pid=*pid;
else loc->pid=NULL;
loc->next=NULL;
loc->pre=temp;
len++;
}
void
Stack::init()
{
head.next=head.pid=head.pre=NULL;
loc=&head;
len=0;
}
int
Stack::length()
{
return len;
}
bool
Stack::empty()
{
return len==0? true:false;
}