对于DFS算法和BFS算法来说,虽然算法的实现比较简单,但是对于n^2-1数码问题,n较大时,会非常占据内存。因为DFS和BFS从本质上来讲可以看出时枚举,举有盲目性,如果枚举到了,问题就得到了解决,对于较大的n来讲,枚举是非常不方便的(15数码问题BFS算法运行了42s),所以得用新的方法,即启发式搜索。
启发式搜索包括A算法与A* 算法,这两个算法得共同点是都引入了估价函数,对最优得结点,将其结点放入队列中,其中估价函数F(n)=G(n)+H(n),H(n)是当前不在位得将牌数,G(n)是结点的深度。经过这样的优化之后,效率得到了极大的提升,对于相对于目标结点很远的结点,不对其进行盲目扩展进入我们的Open表队列,这就是启发式搜索。
下面是A算法代码***
//采用启发式搜索 有序搜索算法(A算法)
//开始状态
/*
[2,8,3,
1,0,4,
5,7,6]
X=15
*/
//目标状态
/*
[1,2,3,
8,0,4,
7,6,5]
X=11
*/
//初始状态的逆序数与目标状态的逆序数都是奇数,八数码问题有解
/*
初始结点入队列,计算f的值,如果Open表和Close表中都没有出现目标结点
对Open表进行重新排序,按f的值从小到大,从队首到队尾进行排序。
f=g+h
g:深度
h:不在位的将牌数
*/
#include<stdio.h>
#include<stdlib.h>
#define NUM 9
//结点的数据结构
typedef struct Node{
int a[9];
int value; //估价函数值
int deep; //当前结点的深度,父结点的深度为0
struct Node *next;
struct Node *before;
}LinkNode;
//Open表与Close表的数据结构
typedef struct Queue{
LinkNode *front; //队首指针
LinkNode *rear; //队尾指针
int length;
}QueueTable;
//h(n)
int InspireFunction(LinkNode *node1,LinkNode *gnode){
int Count=0;
for(int i=0;i<NUM;i++)
if(node1->a[i]!=gnode->a[i])
if(i!=4)
Count++;
return Count;
}
//创建一个结点
LinkNode* CreateNode(int arr[]){
LinkNode *node=(LinkNode*)malloc(sizeof(LinkNode));
for(int i=0;i<NUM;i++)
node->a[i]=arr[i];
node->next=NULL;
node->before=NULL;
node->deep=0;
node->value=0;
return node;
}
//初始化队列
void Initial(QueueTable *&q){
q=(QueueTable*)malloc(sizeof(QueueTable));
q->front=NULL;
q->rear=NULL;
q->length=0;
}
//判断队列是否为空
bool is_Empty(QueueTable *q){
return q->front==NULL;
}
//复制指针内容
void Copy(LinkNode *&node1,LinkNode *node2){
for(int i=0;i<NUM;i++){
node1->a[i]=node2->a[i];
}
node1->deep=node2->deep;
node1->value=node2->value;
}
//入队列操作,尾进头出
void EnQueue(QueueTable *&q,LinkNode *tempnode){
LinkNode *tnode=(LinkNode*)malloc(sizeof(LinkNode));
Copy(tnode,tempnode);
tnode->before=NULL;
tnode->next=NULL;
if(is_Empty(q)){
q->front=tnode;
q->rear=tnode;
q->rear->next=NULL;
}
else{
tnode->next=q->rear->next;
q->rear->next=tnode;
q->rear=tnode;
}
q->length+=1;
}
//出队列操作
void DeQueue(QueueTable *&q){
if(q->front!=NULL){
q->front=q->front->next;
q->length-=1;
}
}
//判断队列中是否出现目标状态或者判断是否已经出现了状态,有就返回真
bool Equal(QueueTable *q,LinkNode*node){
LinkNode *t=(LinkNode *)malloc(sizeof(LinkNode));
t=q->front;
while(t!=NULL){
for(int i=0;i<9;i++){
if(node->a[i]!=t->a[i])
break;
else if(i==8)
return true;
}
t=t->next;
}
return false;
}
//扩展结点,加入Open表
void ExtendTable(QueueTable *&open,LinkNode *node,QueueTable *&close,LinkNode *gnode){
//注意连接父结点
//对于当前结点node,进行扩展,子结点加入Open表,当前结点进入Close表
int arr[9]={0};
int spacePosition=0;
//记录空格的下标
for(int i=0;i<9;i++)
if(node->a[i]==0){
spacePosition=i;
break;
}
//可向上移动
if(spacePosition-3>=0){
for(int i=0;i<9;i++)
arr[i]=node->a[i];
LinkNode *unode;
int t=arr[spacePosition];
arr[spacePosition]=arr[spacePosition-3];
arr[spacePosition-3]=t;
unode=CreateNode(arr);
unode->deep=node->deep+1;
unode->value=InspireFunction(unode,gnode)+unode->deep;
//如果没有再Open表或者Close表中出现,就加入Open表
if(Equal(open,unode)==0 && Equal(close,unode)==0){
EnQueue(open,unode);
open->rear->before=node;
}
}
//可向下移动
if(spacePosition+3<=8){
for(int i=0;i<9;i++)
arr[i]=node->a[i];
LinkNode *dnode;
int t=arr[spacePosition];
arr[spacePosition]=arr[spacePosition+3];
arr[spacePosition+3]=t;
dnode=CreateNode(arr);
dnode->deep=node->deep+1;
dnode->value=InspireFunction(dnode,gnode)+dnode->deep;
//如果没有再Open表或者Close表中出现,就加入Open表
if(Equal(open,dnode)==0 && Equal(close,dnode)==0){
EnQueue(open,dnode);
open->rear->before=node;
}
}
//可向左移动
if(spacePosition%3!=0){
for(int i=0;i<9;i++)
arr[i]=node->a[i];
LinkNode *lnode;
int t=arr[spacePosition];
arr[spacePosition]=arr[spacePosition-1];
arr[spacePosition-1]=t;
lnode=CreateNode(arr);
lnode->deep=node->deep+1;
lnode->value=InspireFunction(lnode,gnode)+lnode->deep;
//如果没有再Open表或者Close表中出现,就加入Open表
if(Equal(open,lnode)==0 && Equal(close,lnode)==0){
EnQueue(open,lnode);
open->rear->before=node;
}
}
//可向右移动
if( (spacePosition+1)%3!=0){
for(int i=0;i<9;i++)
arr[i]=node->a[i];
LinkNode *rnode;
int t=arr[spacePosition];
arr[spacePosition]=arr[spacePosition+1];
arr[spacePosition+1]=t;
rnode=CreateNode(arr);
rnode->deep=node->deep+1;
rnode->value=InspireFunction(rnode,gnode)+rnode->deep;
//如果没有再Open表或者Close表中出现,就加入Open表
if(Equal(open,rnode)==0 && Equal(close,rnode)==0){
EnQueue(open,rnode);
open->rear->before=node;
}
}
//将当前结点从open表出队,进入close表入队
EnQueue(close,open->front);
DeQueue(open);
}
//按照评估函数的评估值重新对Open表排序
void ReSort(QueueTable *&q){
LinkNode *tnode=(LinkNode*)malloc(sizeof(LinkNode));
LinkNode *pnode=(LinkNode*)malloc(sizeof(LinkNode));
LinkNode *qnode=(LinkNode*)malloc(sizeof(LinkNode));
//对于有n个元素的列表,每排序依次就确定一个最大值
for(int i=0;i<q->length-1;i++){
//确定对首指针的位置
if(q->front->value > q->front->next->value){
tnode=q->front->next;
q->front->next=tnode->next;
tnode->next=q->front;
q->front=tnode;
}
//三个指针进行遍历
for(tnode=q->front,pnode=tnode->next,qnode=pnode->next;pnode->next!=NULL;tnode=tnode->next,pnode=tnode->next,qnode=pnode->next){
if(pnode->value > qnode->value){
//确定队尾指针
if(qnode->next==NULL){
pnode->next=qnode->next;
qnode->next=pnode;
tnode->next=qnode;
q->rear=pnode;
}
else{
pnode->next=qnode->next;
qnode->next=pnode;
tnode->next=qnode;
}
}
}
}
}
//判断8数码问题是否有解
int ReverseNum(int a[],int size){
int Count=0;
for(int i=1;i<size;i++){
for(int j=0;j<i;j++)
if (a[j]>a[i])
Count++;
}
return Count;
}
//打印表中的数据
void Print(QueueTable *q){
LinkNode *t=(LinkNode *)malloc(sizeof(LinkNode));
t=q->front;
while(t!=NULL){
for(int i=0;i<9;i++){
printf("%4d",t->a[i]);
if(i==2 || i==5 || i==8)
printf("\n");
}
printf("\n");
t=t->next;
}
}
//找出出现的结点,求出解路径
LinkNode* LookforNode(QueueTable *q,LinkNode *node){
while(q->front!=NULL){
for(int i=0;i<9;i++)
if(node->a[i]!=q->front->a[i])
break;
//找到当前结点,寻找解路径
else if(i==8){
return q->front;
}
q->front=q->front->next;
}
return NULL;
}
//打印结点的信息
void PrintData(LinkNode *node){
if(node->before!=NULL)
PrintData(node->before);
for(int i=0;i<9;i++){
printf("%4d",node->a[i]);
if(i==2 || i==5 || i==8)
printf("\n");
}
printf("\n");
}
int main(){
int begin[9]={2,8,3,1,0,4,5,7,6};
int goal[9]={1,2,3,8,0,4,7,6,5};
int temp[9]={0};
LinkNode *bnode,*gnode,*tnode;
QueueTable *Open,*Close;
Initial(Open);
Initial(Close);
bnode=CreateNode(begin);
gnode=CreateNode(goal);
tnode=CreateNode(temp);
EnQueue(Open,bnode);
if(ReverseNum(begin,9)%2 == ReverseNum(goal,9)%2){
//当Open表不为空
while(is_Empty(Open)==0){
if(Equal(Open,gnode))
break;
//扩展Open表
ExtendTable(Open,Open->front,Close,gnode);
//重排Open表
ReSort(Open);
}
printf("Close表当前的数据:\n");
Print(Close);
printf("******************************************\n");
printf("Open表当前的数据:\n");
Print(Open);
printf("*********************************\n");
printf("解路径:\n");
tnode=LookforNode(Open,gnode);
PrintData(tnode);
}
else
printf("当前问题无解!");
return 0;
}
运行结果截图
如果有帮助就点个赞吧。