《数据结构课程设计》——隐式图的搜索问题(代码实现)

《数据结构课程设计》——隐式图的搜索问题(代码实现)


前言

此篇文章为隐式图搜索问题的代码实现。


一、定义结构体

#include <iostream>
#include <time.h>
using namespace std;

#define MAXLISTSIZE 10000
#define MAXSTEPSIZE 100

/*
定义结构体
*/
struct EightNum
{    //存储八数码
	int status[9];
	//存储走的是第几步(层数)
	int G;
	//存储不在位的格数(作为我们的启发式函数)
	int H;
	//存储估价函数的值
	int F;
	//存储0数码的位置
	int Zero;
	//存储操作符(1左2右3上4下)
	int step;
	//父指针
	EightNum* Parent;
};

二、声明

//声明最终状态
int FinalStatus[9];
//定义OPEN表和CLOSE表,open和close是表中最后一个内容的下一位序号
EightNum OPEN[MAXLISTSIZE];
EightNum CLOSE[MAXLISTSIZE];
int open = 0;
int close = 0;

EightNum* Node;

三、A*算法实现

/*
计算不在位的字格数H
返回 H
*/
int CountH(int* status)
{
	int H = 0;
	int i;
	for (i = 0; i <= 8; i++)
	{
		if (FinalStatus[i] != status[i])
		{
			H++;
		}
	}
	return H;
}

/*
判断新生成的节点是否已经存在于OPEN表或CLOSE表中
返回 表征是否存在于OPEN或CLOSE的值,值为0 均不在,值>0 只在OPEN表,值<0 只在CLOSE表,|值|-1表示所在列表中的位置
*/
int Exist(EightNum* N)
{
	int i, j;
	//计算不在位的字格数,如果为0,则证明给函数的节点在表中已存在
	int H = 0;                    
	int status[9];

	Node = new EightNum;
	Node = N;

	for (i = 0; i <= 8; i++)
	{
		status[i] = Node->status[i];
	}
	//判断是否在OPEN表
	for (i = 0; i <= open - 1; i++)    
	{
		for (j = 0; j <= 8; j++)
		{
			if (status[j] != OPEN[i].status[j])
			{
				H++;
			}
		}
		//H=0证明在表中找到该节点
		if (H == 0)                 
		{
			//如果在OPEN表中,返回i(节点在OPEN的位置)+ 1(在OPEN找到该节点)
			return i + 1;          
		}
		//扫描完一个节点后重置H
		H = 0;                     
	}
	//判断是否在CLOSE表
	for (i = 0; i <= close - 1; i++)     
	{
		for (j = 0; j <= 8; j++)
		{
			if (status[j] != CLOSE[i].status[j])
			{
				H++;
			}
		}
		//H=0证明在表中找到该节点
		if (H == 0)                 
		{
			//如果在CLOSE表中,返回-i(i为节点在CLOSE的位置)- 1(在CLOSE找到该节点)
			return (-i) - 1;        
		}
		//扫描完一个节点后重置H
		H = 0;                     
	}

	return 0;
}

/*
初始化节点
返回 初始化后的节点Node
*/
EightNum* EightNumInit(int status[10], int zero, int g, EightNum* parent, int step)
{
	int i;
	Node = new EightNum;
	for (i = 0; i <= 8; i++)
	{
		Node->status[i] = status[i];
	}
	Node->Zero = zero;
	Node->G = g;
	Node->H = CountH(Node->status);
	Node->F = Node->G + Node->H;
	Node->Parent = parent;
	Node->step = step;
	return Node;
}

/*
左移后的变化
返回 左移后的状态
*/
int* Left(int* s, int z)
{
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
		status[i] = s[i];
	}
	//左移则是下标减1,需要与前一个位置进行值的交换
	temp = status[z - 1];
	status[z - 1] = 0;
	status[z] = temp;
	return status;
}

/*
右移后的变化
返回 右移后的状态
*/
int* Right(int* s, int z)
{
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
		status[i] = s[i];
	}
	temp = status[z + 1];
	status[z + 1] = 0;
	status[z] = temp;
	return status;
}

/*
上移后的变化
返回 上移后的状态
*/
int* Up(int* s, int z)
{
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
		status[i] = s[i];
	}
	temp = status[z - 3];
	status[z - 3] = 0;
	status[z] = temp;
	return status;
}

/*
下移后的变化
返回 下移后的状态
*/
int* Down(int* s, int z)
{
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
		status[i] = s[i];
	}
	temp = status[z + 3];
	status[z + 3] = 0;
	status[z] = temp;
	return status;
}

/*
判断子节点是否在OPEN或CLOSE中,并进行对应的操作
返回值 NULL
*/
void ExistAndOperate(EightNum* N)
{
	int i;
	//定义表示新生成节点是否在OPEN表或CLOSE表中, 值为0 均不在,值>0 只在OPEN表,值<0 只在CLOSE表
	int inList;                 
	Node = new EightNum;
	Node = N;
	//如果是第一步的节点,直接加入OPEN中,返回
	if (Node->G == 1)
	{
		OPEN[open] = *Node;
		open++;
		return;
	}
	//判断新节点是否在OPEN或CLOSE中
	inList = Exist(Node);       
	//如果均不在两个表中,将节点加入OPEN表中
	if (inList == 0)           
	{
		//将拓展出的新结点加入到OPEN表中
		OPEN[open] = *Node;     
		open++;
	}
	//如果在OPEN中,说明从初始节点到该节点找到了不同路径,保留耗散值短的那条路径
	else if (inList > 0)             
	{//如果表内节点F值大于新节点F值,用新节点代替表内节点
		if (OPEN[inList - 1].F > Node->F)    
		{
			OPEN[inList - 1] = *Node;
		}
	}
	//如果在CLOSE中,说明初始节点到该节点有两条路径,如果新找到的路径耗散值大,什么都不做,如果较小,将其从CLOSE中取出放入OPEN中    
	else if (inList < 0)           
	{
		inList = -inList;
		//如果较小
		if (CLOSE[inList - 1].F > Node->F)       
		{//将其取出放入OPEN
			OPEN[open] = *Node;       
			open++;
		}
		//将其在CLOSE中释放
		for (i = inList - 1; i <= close - 1; i++)     
		{
			CLOSE[i] = CLOSE[i + 1];
		}
		close--;
	}
}

/*
寻找最佳路径函数
返回 最后的节点Node
*/
EightNum* Search()
{
	int* status;
	int i, j;

	EightNum* Temp;
	//一直循环知道找到解结束
	while (1)
	{
		Temp = new EightNum;
		//用冒泡排序给OPEN表里面的节点按耗散值进行排序
		for (i = open - 1; i > 0; i--)
		{
			for (j = 0; j < i; j++)
			{//从小到大进行排序
				if (OPEN[j].F > OPEN[j + 1].F)
				{//交换值
					*Temp = OPEN[j + 1];
					OPEN[j + 1] = OPEN[j];
					OPEN[j] = *Temp;
				}
			}
		}

		Node = new EightNum;
		//从OPEN表中取出第一个元素(F值最小)
		*Node = OPEN[0];
		//判断该节点是否是目标节点,若是,则不在位的格数为0,算法结束,若不是,则将该结点进行扩展
		if (!CountH(Node->status))
		{
			break;
		}

		Temp = Node;
		//将扩展过的节点放入CLOSE 
		CLOSE[close] = *Node;
		close++;
		//将扩展的节点从OPEN中释放
		for (i = 0; i <= open - 1; i++)
		{//相当于是出栈
			OPEN[i] = OPEN[i + 1];
		}
		open--;
		//如果能左移,则进行左移创造新结点,下标为0,3,6则不能进行左移
		if ((Temp->Zero) % 3 >= 1)
		{//创造新结点
			Node = new EightNum;
			//得到新的状态
			status = Left(Temp->status, Temp->Zero);
			//初始化新结点
			Node = EightNumInit(status, Temp->Zero - 1, (Temp->G) + 1, Temp, 1);
			//判断子节点是否在OPEN或CLOSE中,并进行对应的操作
			ExistAndOperate(Node);      
		}
		//如果能右移,则进行右移创造新结点 ,下标为2,5,8则不能
		if ((Temp->Zero) % 3 <= 1)
		{  //创造新结点
			Node = new EightNum;     
			//得到新的状态
			status = Right(Temp->status, Temp->Zero);  
			//初始化新结点
			Node = EightNumInit(status, Temp->Zero + 1, (Temp->G) + 1, Temp, 2);   
			//判断子节点是否在OPEN或CLOSE中,并进行对应的操作
			ExistAndOperate(Node);      
		}
		//如果能上移,则进行上移创造新结点  ,下标为0,1,2则不可以
		if (Temp->Zero >= 3)
		{
			Node = new EightNum;  
			//得到新的状态
			status = Up(Temp->status, Temp->Zero); 
			//初始化新结点
			Node = EightNumInit(status, Temp->Zero - 3, (Temp->G) + 1, Temp, 3);   
			//判断子节点是否在OPEN或CLOSE中,并进行对应的操作
			ExistAndOperate(Node);      
		}
		//如果能下移,则进行下移创造新结点 ,下标为6,7,8则不可以
		if (Temp->Zero <= 5)
		{
			Node = new EightNum;                                           //创造新结点
			status = Down(Temp->status, Temp->Zero);                   //得到新的状态
			Node = EightNumInit(status, Temp->Zero + 3, (Temp->G) + 1, Temp, 4);    //初始化新结点
			ExistAndOperate(Node);      //判断子节点是否在OPEN或CLOSE中,并进行对应的操作
		}
		//如果open=0, 证明算法失败, 没有解
		if (open == 0)                  
			return NULL;
	}
	return Node;
}

/*
展示具体步骤
返回 NULL
*/
void ShowStep(EightNum* Node)
{
	int STEP[MAXSTEPSIZE];
	int STATUS[MAXSTEPSIZE][9];
	int step = 0;
	int i, j;
	int totalStep = Node->G;
	while (Node)
	{
		STEP[step] = Node->step;
		for (i = 0; i <= 8; i++)
		{
			STATUS[step][i] = Node->status[i];
		}
		step++;
		Node = Node->Parent;
	}
	cout << "----------------------" << endl;
	cout << "总步数:" << totalStep << endl;
	cout << "----------------------" << endl;
	for (i = step - 1; i >= 0; i--)
	{
		if (STEP[i] == 1)
			cout << "向左走一步"<<endl;
		else if (STEP[i] == 2)
			cout << "向右走一步"<<endl;
		else if (STEP[i] == 3)
			cout << "向上走一步"<<endl;
		else if (STEP[i] == 4)
			cout << "向下走一步"<<endl;
		else if (STEP[i] == 0)
			cout << "开始:" << endl;
		for (j = 0; j <= 8; j++)
		{
			cout << STATUS[i][j]<<" ";
			//换行输出
			if (j == 2 || j == 5 || j == 8)
				cout << endl;
		}
		cout << "----------------------" << endl;
	}
}

四、主函数

/*
主函数
*/
int main()
{
	int fstatus[9];
	int i, beginTime, endTime;
	EightNum* FNode;
	EightNum* EndNode;
	//输入初始状态
	cout << "请输入初始状态:" << endl;
	for (i = 0; i <= 8; i++)
	{
		cin >> fstatus[i];
	}
	cout << endl;
	//输入最终状态
	cout << "请输入最终状态:" << endl;
	for (i = 0; i <= 8; i++)
	{
		cin >> FinalStatus[i];
	}
	beginTime = clock();
	//判断0数码的位置
	for (i = 0; i <= 8; i++)
	{
		if (fstatus[i] == 0)
			break;
	}
	//获得初始节点
	FNode = EightNumInit(fstatus, i, 0, NULL, 0);
	//将初始节点放入OPEN中
	OPEN[open] = *FNode;
	open++;
	//寻找最佳路径
	EndNode = Search();

	if (!EndNode)
		cout << "无解" << endl;
	else
		ShowStep(EndNode);                      //展示步骤

	endTime = clock();
	cout << "Run Time:" << endTime - beginTime << "ms" << endl;

	return 0;
}

五、运行结果

在这里插入图片描述在这里插入图片描述

六、 总结

在本项目的实现中主要是采用了A* 启发搜索算法,项目从开始到结束主要分为以下几个步骤:1.熟悉并了解A*算法;2.将查询目标状态的搜索树画出;3.整理项目思路,打出项目框架,.进行项目的具体实现;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值