数据结构课设----迷宫问题求解

问题描述:

        

 

函数流程图:

        

 

源代码:

#include<iostream>
#include<fstream>
#include <time.h>
#include <stdlib.h>  
#include <iomanip>
#include <vector> 
using namespace std;
struct Coor       //定义描当前位置的结构类型
{
	int row;
	int column;
};


struct PointNode
{
	int row;
	int column; 
	int direction; 
	PointNode(int r, int c, int dir)
	{
		row = r; 
		column = c; 
		direction = dir; 
	}
};

struct LinkNode      //链表结点 , 栈中存储的元素的类型就是LinkNode 的类型。 
{
	Coor data;
	LinkNode* next;
};
//定义栈    虽然说stl库中有我们所需要的相应的数据结构,但我们还是直接再利用链表去封装一个栈的类型吧。 
class stack
{
private:
	LinkNode* top;
public:
	stack();              //构造函数,置空栈
	~stack();             //析构函数 对象的生命周期结束的时候,就会自动执行析构函数,从而实现我们的类对象的及时的删除,避免浪费空间。 
	void Push(Coor data);           //把元素data压入栈中
	Coor Pop();                  //使栈顶元素出栈
	Coor GetPop();               //只获得栈顶元素的data,栈顶元素并不会出栈。 
	void Clear();                   //把栈清空
	bool IsEmpty();        //判断栈是否为空
};

// 栈类的一系列的经典操作******** 
stack::stack()          //构造函数,置空栈
{
	top = NULL;
}
stack::~stack() {}
void stack::Push(Coor x)          //把元素data压入栈中 
{
	LinkNode* TempNode;
	TempNode = new LinkNode;  // 注意这里要用我们的new去申请空间,因为后面我们要用delete 去删除我们的出栈的节点。 
	TempNode->data = x;
	TempNode->next = top;
	top = TempNode;
}
Coor stack::Pop()                 //使栈顶元素出栈
{
	Coor Temp;
	LinkNode* TempNode;
	TempNode = top;
	top = top->next;
	Temp = TempNode->data;
	delete TempNode;
	return Temp;
}
Coor stack::GetPop()               //直接获取栈顶元素的data值,并不删除栈顶元素。 
{
	return top->data;
}
void stack::Clear()                    //把栈清空 , 在出栈的同时我们要释放相应的节点的空间。 
{
	while (top)
	{
		LinkNode* Temp;
		Temp = top;
		delete Temp;
		top = top->next;
	}
	top = NULL; // 最后将栈指针置空 。 
}
bool stack::IsEmpty()
{
	if (top == NULL) return true;
	return false;
}
// *********************** \\  
   //定义移动的偏移量 
int dx[4] = { 0 , 1 , 0 , -1 }; // x 方向的偏移量。 
int dy[4] = { 1 , 0 , -1 , 0 }; // y 方向的偏移量。 
vector<PointNode> Point;  // 存储点的容器

int x_present, y_present; // 标记我们当前走到的位置。 

// ***********函数的声明 *********** //


int** GetMaze(int& m, int& n); // 生成迷宫 
bool bfs_Mazepath(int** maze, int m, int n); // 深搜求路径 

bool dfs_Mazepath(int** maze, int m, int n); // 宽搜求路径。 

void PrintPath(stack p, int** maze, int m, int n);        //输出宽搜路径。 
void PrintPath(int** maze, int m, int n);          //输出深搜路径。 
void print(int* row, int n, int m, int p);       // 是输出在GetMaze()函数中生成的初始迷宫的辅助函数。 

void backUp(int** maze, int** backup, int m, int n); // 恢复迷宫,便于不同搜索方式的执行。 

void menu(); // 对我们的主菜单进行简单的渲染; 

void Search_present(int** maze , int m , int n ); // 深搜算法的辅助函数。 

int** getInvaildMaze(int m, int n);

int main() ; // 主函数,程序执行的开始 
//*************函数的声明***********//  

// 各类函数的定义(按照声明的顺序来)

int** GetMaze(int& m, int& n) // 注意这里是引用,我们还要根据引用去修改m , n 的值。 
{
	int** maze;
	int i = 0, j = 0;
	char Choose;

	cout << "请输入你想生成的迷宫的行数和列数: ";
	cin >> m >> n;
	while (m <= 0 || n <= 0)
	{
		system("color E4");
		cout << "(wc)哥哥莫不是在消遣我? , 再让你输入一次: ";
		cin >> m >> n;
	}
	system("color E0");
	//定义一个标志,选择读取文件或直接输入,获取迷宫
	cout << "请选择:随机生成(1)或  (如果不嫌累的话)手动键盘输入(2):\n";
	cin >> Choose;
	while ((Choose != '1') && (Choose != '2'))
	{
		cout << "请睁大你的眼睛仔细看看我让你输入的什么东西!,再让你输一次嗷:";
		cin >> Choose;
	}
	if (Choose == '1')         //当标志Choose为‘1’时,读取文件
	{
		while ((m % 2 == 0 ) || (n % 2 == 0 ))
		{
			cout << "如果你想要生成随机的迷宫就请你输入的行和列都是奇数:";
			cin >> m >> n;
		}
		//cout << "hello world " << endl ; 
		maze = getInvaildMaze(m, n);
	}

	else  if (Choose == '2')        //Choose=2  ,手动输入迷宫
	{
		cout << "请输入迷宫内容:(0表示通路,1表示不连通。中间用空格键分开)\n";
		maze = new int* [m + 2];  //申请长度等于行数加2的二级指针
		for (i = 0; i < m + 2; i++)  //申请每个二维指针的空间
		{
			maze[i] = new int[n + 2];
		}
		for (i = 1; i <= m; i++)
		{
			for (j = 1; j <= n; j++)
			{
				cin >> maze[i][j];
			}
		}
	}
	//给迷宫的四周加一堵墙,即把迷宫四周定义为1
	for (i = 0; i < m + 2; i++) // 给左右加上墙。              
		maze[i][0] = maze[i][n + 1] = 1;
	for (i = 0; i < n + 2; i++) maze[0][i] = maze[m + 1][i] = 1; // 给上下加上墙。
	cout << endl;
	cout << "我们生成了如下图的迷宫:\n";
	for (int p = 0; p < m + 2; ++p) // 根据相应位置的是0 或是 1 去实现这个迷宫的可视化。  
	{
		print(maze[p], n, m, p);

	}
	return maze;              //返回存贮迷宫的二维指针maze
}


bool bfs_Mazepath(int** maze, int m, int n) // 寻找路径的函数。, 宽搜版本 ,  
{
	stack q, p;        //定义栈p、q,分别存探索迷宫的存储和路径过程
	Coor Temp1, Temp2;
	int row, column, loop;
	Temp1.row = 1;
	Temp1.column = 1;
	q.Push(Temp1);           //将入口位置入栈
	p.Push(Temp1);       // 第一个节点进入我们的存储栈中。 
	maze[1][1] = -1;          //标志入口位置已到达过 , 防止在搜索的过程中搜回已经搜过的点,从而造成栈的死循环。 
	while (!q.IsEmpty())       //  路径栈不为空的话,我们就继续输出。 
	{
		Temp2 = q.GetPop();      //获取栈顶元素的 data的值 。 
		if (!((p.GetPop().row == q.GetPop().row) && (p.GetPop().column == q.GetPop().column))) // 如果路径栈的栈顶元素跟存储栈的栈顶元素不是同一个元素
		// 我们就将路径栈上的元素在存储栈上入栈。 
			p.Push(Temp2);
		for (loop = 0; loop < 4; loop++)   //探索当前位置的4个相邻位置
		{
			row = Temp2.row + dx[loop];     //计算出新位置 x位置值 
			column = Temp2.column + dy[loop];      //计算出新位置 y 位置值
			if (maze[row][column] == 0)         //判断新位置是否可达 , 同时也防止了我们“回头”。 
			{
				Temp1.row = row;
				Temp1.column = column;
				maze[row][column] = -1;          //标志新位置已到达过,防止我们再反过来重新搜到从而造成死循环, 
				q.Push(Temp1);         // 将这个节点加入到我们的路径栈中, 
			}
			if ((row == m) && (column == n))    //说明路径栈的当前顶点的一个邻点是我们的终点,从而说明到达了出口。 
			{

				//cout << "hello world" << endl ; 
				Temp1.row = m;

				Temp1.column = n;


				p.Push(Temp1);       //把最后一个位置入栈

				PrintPath(p, maze, m, n);   //矩阵显示输出    

				return true;            //表示成功找到路径
			}
		}
		if (p.GetPop().row == q.GetPop().row && p.GetPop().column == q.GetPop().column) // 这个判断是当我们走到一个死路的时候存储栈的回退。 
			//如果没有新位置入栈,则返回到上一个位置 
		{
			p.Pop();
			q.Pop();
			//			maze[data.row][data.column] = 9 ; // 标志这个位置是一个无效位置,(这里不重新赋值为零是为了防止入栈的死循环)。   
		}
	}
	return false;           //表示查找失败,即迷宫无路经
}

bool dfs_Mazepath(int** maze, int row, int column, int m, int n) //  
{
	if (row == m && column == n)
	{
		maze[m][n] = 9;
		return true;
	}

	maze[row][column] = 9;
	for (int i = 0; i < 4; i++)
	{
		int a, b;
		a = row + dx[i];
		b = column + dy[i];
		if (maze[a][b] == 0)
		{
			if (dfs_Mazepath(maze, a, b, m, n))
				return true;
		}
	}

	// 恢复现场

	maze[row][column] = 0;
	return false;
}

void PrintPath(stack p, int** maze, int m, int n)        //输出路径
{
	cout << "迷宫的路径为下图中'㊣' 所标记的路径\n";
	int a, b;
	Coor data;
	LinkNode* Temp;

	while (!p.IsEmpty())
	{
		data = p.Pop();
		maze[data.row][data.column] = 10;
	}
	//************调试代码*********** 
	//	for(int i = 1 ; i <= m ; i ++ ) 
	//	{
	//		for(int j = 1 ; j  <= m ; j ++ ) 
	//		{
	//			cout << maze[i][j] << ' ' ;
	//		}
	//		cout << endl ; 
	//	}
	//******************************** 
	for (int i = 0; i <= m + 1; i++)
	{
		cout << "                           ";
		for (int j = 0; j <= n + 1; j++)
		{
			if (i == 0 || i == m + 1 || j == 0 || j == n + 1)
			{
				cout << "■";
			}
			else if (maze[i][j] == 0 || maze[i][j] == -1)
			{
				cout << "  ";
			}
			else if (maze[i][j] == 1)
			{
				cout << "■";
			}
			else if (maze[i][j] == 10)
			{
				cout << "㊣";
			}
		}
		cout << endl;
	}
	return;
}

void PrintPath(int** maze, int m, int n)
{
	for (int i = 0; i <= m + 1; i++)
	{
		cout << "                           ";
		for (int j = 0; j <= n + 1; j++)
		{
			if (i == 0 || i == m + 1 || j == 0 || j == n + 1)
			{
				cout << "■";
			}
			else if (maze[i][j] == 0 || maze[i][j] == -1)
			{
				cout << "  ";
			}
			else if (maze[i][j] == 1)
			{
				cout << "■";
			}
			else if (maze[i][j] == 9)
			{
				cout << "㊣";
			}
		}
		cout << endl;
	}
	return;
}

void print(int* row, int n, int m, int p)
{
	cout << "                           ";
	for (int i = 0; i <= n + 1; i++)
	{

		if (p == 0 || p == m + 1)
			cout << "■";
		else if (i == 0 || i == n + 1)
			cout << "■"; // ★
		else if (row[i] == 0)
			cout << "  ";    // ㊣ 
		else if (row[i] == 1)
			cout << "■";
	}
	cout << endl;
}

void backUp(int** maze, int** backup, int m, int n)
{
	for (int i = 0; i < m + 2; i++)
	{
		for (int j = 0; j < n + 2; j++)
		{
			backup[i][j] = maze[i][j];
		}
	}
}

void menu()
{
	system("color E0");
	cout << setiosflags(ios::right | ios::showpoint);
	cout.width(100); // 70
	cout << "欢迎来到迷宫小程序" << endl;
	cout << endl << endl;
	cout.width(95);
	cout << "迷宫说明" << endl;
	cout.width(85);
	cout << "障碍物: ■" << "         " << "无障碍物的小格子:空格" << endl;
	cout << endl;
	cout.width(85);
	cout << "  外围墙体:■" << "         " << "描述一条路径 " << "㊣" << endl;
}

void search_present(int** maze , int m , int n )
{
	// 右  , 下 , 左 , 上 ; 
	int row, column;
	for (int i = 0; i < 4; i++)  // 0 , 1 , 2 , 3 分别代表的方向是: 右 下 左 上 
	{
		row = x_present + dx[i];
		column = y_present + dy[i];
		if (row >= 1 && row <= m && column >= 1 && column <= n && maze[row][column] == 1)
		{
			PointNode t(row, column, i);
			Point.push_back( t ); 
		}
	}
}

int** getInvaildMaze(int m, int n) // 随机生成一个m行n列的最少具有一条通路的迷宫, 
//  并且返回这个迷宫矩阵的首地址。(利用普利姆算法实现迷宫的生成) 
{
	//随机生成的迷宫的行和列必须是奇数,

		//开辟迷宫空间。

		// 1 为墙 0 为可以走的通路。 
	int** maze;
	maze = new int* [m + 2];
	for (int i = 0; i < m + 2; i++)
	{
		maze[i] = new int[n + 2];
	}
	srand(time(0)); // 随机数种子。 
	// 将所有位置上的都设置为墙。 
	for (int i = 0; i <= m + 1; i++)
	{
		for (int j = 0; j <= n + 1; j++)
		{
			maze[i][j] = 1; // 将所有的位置上的空格都设置为障碍物。 
		}
	}

	Point.clear();
	// 下面对我们的迷宫进行实现。
	x_present = y_present = 1; // 入口点。
	
	maze[x_present][y_present] = 0 ; 

	search_present(maze , m , n );
	// 0 , 1 , 2 , 3 分别代表的方向是: 右 下 左 上 
//	cout  << "hello world " << endl ; 
	while (Point.size())
	{
		int wall_sum = Point.size();
		int Randnum;
		Randnum = rand() % wall_sum;

		PointNode new_wall = Point[Randnum];

		x_present = new_wall.row;

		y_present = new_wall.column;

		int t = new_wall.direction;
		if (t == 0) // 向右走 , x 不变, y ++ ;  
		{
			y_present++;
		}
		else if (t == 1)
		{
			x_present++;
		}
		else if (t == 2)
		{
			y_present--;
		}
		else {
			x_present--;
		}

		// 现在走到墙的后面的一格。

		// Point数组中的墙一定是迷宫范围内的墙,不会是外围的墙。 
		int& x = x_present;
		int& y = y_present;
		if (x >= 1 && y >= 1 && x <= m && y <= n && maze[x][y] == 1)
		{
			maze[new_wall.row][new_wall.column] = maze[x][y] = 0;
			search_present(maze , m , n ) ;  // 搜索当前位置四周的墙加入我们的容器中。 
		}
		Point.erase(Point.begin() + Randnum);
	}
//	for(int i = 1 ; i <= m ; i ++ )
//	{
//		for(int j = 1 ; j <= n ; j ++ ) 
//		{
//			cout << maze[i][j] << ' ' ;
//		}
//		cout << endl; 
//	}
	return maze ; 
}

int main()
{
	menu();
	int m = 0, n = 0;
	int** maze;  // 定义二级指针,从而存储我们的迷宫矩阵的首地址。 
	int** backup; // 备份一下,一会便于恢复。 
	maze = GetMaze(m, n);  //调用GetMaze(int &m,int &n)函数,得到迷宫矩阵的首地址 , 同时注意参数的调用是引用。, m , n 的值被修改了! 


	backup = new int* [m + 2];
	for (int i = 0; i < m + 2; i++)
	{
		backup[i] = new int[n + 2];
	}
	backUp(maze, backup, m, n); // 将第一个参数的copy给第二个参数 

	cout << "请问你想使用深搜( D/d )来走迷宫;还是想用宽搜来走迷宫( B/b ) (tips:输入0退出我们的选择): ";
	char choose;
	cin >> choose;
	while (choose != '0')
	{
		while (choose != 'D' && choose != 'd' && choose != 'B' && choose != 'b' && choose != '0')
		{
			cout << "请再仔细看看我让你输入的东西是什么,再让你输一次: ";
			cin >> choose;
		}
		if (choose == '0') return 0;
		if (choose == 'D' || choose == 'd')
		{

			if (dfs_Mazepath(maze, 1, 1, m, n))
			{
				PrintPath(maze, m, n);
				cout << "迷宫路径探索成功!\n";
			}
			else cout << "NO Path \n";
			backUp(backup, maze, m, n);
		}
		else if (choose == 'B' || choose == 'b')
		{
			if (bfs_Mazepath(maze, m, n)) //调用Mazepath(int **maze,int m,int n)函数获取路径
				cout << "迷宫路径探索成功!\n";
			else cout << "NO Path \n";
			backUp(backup, maze, m, n);
		}
		cout << "请问你想使用深搜( D/d )来走迷宫;还是想用宽搜来走迷宫( B/b ) (tips:输入0退出我们的选择): ";
		cin >> choose;
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值