8数码问题,启发式搜索。C++

对于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;
}

运行结果截图
在这里插入图片描述
在这里插入图片描述

如果有帮助就点个赞吧。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值