数据结构4:线性表的应用(3)——队列求解迷宫问题的最短路径BFS

191 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,[点击查看活动详情]

相关文章

数据结构3:队列 - 掘金 (juejin.cn)

基础知识

  • 队列

    • 定义:队列(Queue)是限定仅在表头进行删除操作、在表尾进行插入操作的线性表(先进先出)。进行插入操作的端称为队尾,进行删除操作的端称为队头。
    • 存储形式:顺序存储(数组实现)、链式存储(链表实现) 在这里插入图片描述
  • BFS算法

    • 宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一
    • 与队列的关系:BFS算法是一种盲目搜索算法,需要按结点邻接关系遍历所有展开节点,而得到的子节点都会被加进一个先进先出的队列中。这就是队列在BFS算法中的应用。

题目

用队列求解迷宫问题的最短路径

思路

①实现链栈的常规操作:入队、出队、队列判空、获取队头元素等操作

②按照BFS的遍历顺序思想,依次访问邻接结点,将他们放入队列中

③遍历下去直到访问到目标结点为止

代码

//=====================测试3 BFS最短路径======================

class QueueNode  //队列结点
{
public:
	QueueNode() :x(-1), y(-1), next(NULL) {};
	QueueNode(int m_x, int m_y, QueueNode* m_next = NULL) :x(m_x), y(m_y), next(m_next) {};
	~QueueNode() { next = NULL; }
public:
	int x, y;  //储存当前结点距离起止位置的最短路线的距离
	QueueNode* next;  //储存下一个结点的指针
};

class LinkQueue  //队列双向链表
{
public:
	LinkQueue() { font = rear = new QueueNode(); }
	void record(int m_x, int m_y);
	void pop();
	void print();

public:
	int x, y;  //记录结点代表的坐标
	QueueNode* font;
	QueueNode* rear;
};
void LinkQueue::record(int m_x, int m_y)
{
	QueueNode* p = new QueueNode(m_x, m_y);
	p->next = NULL;
	rear->next = p; //注意
	rear = p;
	cout << '(' << m_x << ',' << m_y << ')' << "结点入队成功!" << endl;
}

void LinkQueue::pop()
{
	if (font == rear) {  //队空
		cout << "链队为空,无法执行出队操作!" << endl;
		return;
	}
	QueueNode* tmp = font->next;
	font->next = tmp->next;
	if (rear == tmp)  //只剩下一个元素
	{
		rear = font;
	}
	cout << "(" << tmp->x << ',' << tmp->y << ')' << "出队成功!" << endl;
	delete tmp;


}
void LinkQueue::print()
{
	QueueNode* p = font;
	while (p->next != NULL)
	{
		cout << '(' << p->next->x << ',' << p->next->y << ')' << "->";
		p = p->next;
	}
	cout << "end" << endl;
}

struct Road
{
	int x, y;
};


void test3()  //BFS走迷宫-最短路径
{
	cout << "=====================测试3 BFS之迷宫最短路径问题======================\n";
	int m, n;  //m行n列
	//m = n = 5;
	cout << "请输入要生成的迷宫的长和宽:";
	cin >> m >> n;
	//创建标记地图,记录是否走过
	bool** sign_map = new bool*[m];  //存储迷宫是否走过
	for (int i = 0; i < m; ++i)
	{
		sign_map[i] = new bool[n];
		for (int t = 0; t < n; ++t)
		{
			sign_map[i][t] = true;
		}
	}
	//创建地图
	bool** map = new bool*[m];  //存储迷宫是否走过
	for (int i = 0; i < m; ++i)
	{
		map[i] = new bool[n];
	}
	time_t tt;
	srand((unsigned)time(&tt));
	for (int i = 0; i < m; i++)  //生成10个随机整数存入链表
	{
		if (i == 0 )
		{
			for (int j = 0; j < m + 2; ++j)
			{
				cout << "**";
			}
			cout << endl;
		}
		for (int t = 0; t < n; ++t)
		{
			if (i != 0 || i != m - 1)
			{
				if (t == 0) cout << "* ";
			}
			if ((i == 0 && t == 0) || (i == m - 1 && t == n - 1))
			{
				map[i][t] = true;
				if (i == 0 && t == 0) cout << "起";
				else cout << "终";
			}
			else
			{
				int is_wall = rand() % 10;  //插入到位置i100-999随机整数
				if (is_wall < 3) map[i][t] = false;  //修改is_wall的值可以改变迷宫中墙壁出现的概率
				else map[i][t] = true;
				if (map[i][t] == true) cout << "□";  //⭕
				else if (map[i][t] == false) cout << "×";
			}
			if (i != 0 || i != m - 1)
			{
				if (t == n-1) cout << " *";
			}
		}
		cout << endl;
		if (i == m-1)
		{
			for (int j = 0; j < m + 2; ++j)
			{
				cout << "**";
			}
			cout << endl;
		}
	}
	//标记地图——复制地图用于初始化
	for (int i = 0; i < m; ++i)
	{
		for (int t = 0; t < n; ++t)
		{
			sign_map[i][t] = map[i][t];    //true为未走,表示可以经过这个点,false表示已走或者墙壁
		}
	}
	Road** road = new Road*[m];  //记录上个结点
	for (int i = 0; i < m; ++i)
	{
		road[i] = new Road[n];
		for (int t = 0; t < n; ++t)
		{
			road[i][t].x = -1;
			road[i][t].y = -1;
		}
	}
	LinkQueue* path = new LinkQueue();
	path->record(0, 0);  //记录起始位置
	path->print();
	sign_map[0][0] = false;  //起始位置标记为走过
	int xx = 0, yy = 0;
	road[0][0].x = 0;
	road[0][0].y = 0;
	while (1)
	{
		if (path->font == path->rear)  //头尾结点相等,表示到达终点
		{
			//输出路径,结束搜索
			if (xx != m - 1 || yy != n - 1)
			{
				cout << "此迷宫找不到出路!" << endl;
				break;
			}
		}
		//搜索4个方向=====================================================
		if (xx + 1 < m&&sign_map[xx + 1][yy] == true)  //向下可走
		{
			path->record(xx + 1, yy);
			sign_map[xx + 1][yy] = false;  //走过的点标为false
			road[xx + 1][yy].x = xx;
			road[xx + 1][yy].y = yy;
		}
		if (yy + 1 < n&&sign_map[xx][yy + 1] == true)  //向右可走
		{
			path->record(xx, yy + 1);
			sign_map[xx][yy + 1] = false;
			road[xx][yy + 1].x = xx;
			road[xx][yy + 1].y = yy;
		}
		if (xx - 1 > 0 && sign_map[xx - 1][yy] == true)  //向上可走
		{
			path->record(xx - 1, yy);
			sign_map[xx - 1][yy] = false;
			road[xx - 1][yy].x = xx;
			road[xx - 1][yy].y = yy;

		}
		if (yy - 1 > 0 && sign_map[xx][yy - 1] == true)  //向左可走
		{
			path->record(xx, yy - 1);
			sign_map[xx][yy - 1] = false;
			road[xx][yy - 1].x = xx;
			road[xx][yy - 1].y = yy;
		}
		//搜索4个方向=====================================================finished!

		path->pop();
		if (path->font->next != NULL)
		{
			xx = path->font->next->x;
			yy = path->font->next->y;
			cout << "走到(" << xx << ',' << yy << ')' << endl;
		}
		path->print();
		if (xx == m - 1 && yy == n - 1)
		{
			cout << "到达终点!" << endl;

			Road r; //反向查找路径
			int xx = m - 1;
			int yy = n - 1;
			Road* road_list = nullptr;
			road_list = new Road[m*n];
			road_list[0].x = m - 1;
			road_list[0].y = n - 1;
			int step = 1;
			while (xx != 0 || yy != 0)
			{
				xx = road[xx][yy].x;
				yy= road[xx][yy].y;
				road_list[step].x = xx;
				road_list[step].y = yy;
				//cout << road_list[step].x << ',' << road_list[step].y << endl;
				step++;
			}
			cout << "最短路径长为: " << step-1<<endl;
			cout << "最短路径为:" << endl;
			cout << "Start->";
			for (int i = step - 1; i >= 0; --i)
			{
				cout << '(' << road_list[i].x << "," << road_list[i].y << ')' << "->";
			}
			cout << "End";
			cout << endl;
			break;
			delete[]road_list;
		}
	}


	//二维动态数组的删除
	for (int i = 0; i < m; ++i)
	{
		delete[]sign_map[i];
	}
	delete[]sign_map;

	for (int i = 0; i < m; ++i)
	{
		delete[]map[i];
	}
	delete[]map;

	for (int i = 0; i < m; ++i)
	{
		delete[]road[i];
	}
	delete[]road;
}

int main()
{
	test1();
	cout << '\n';
	test2();
	cout << '\n';
	test3();
	cout << '\n';
}


结果

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