用C语言解决迷宫问题

迷宫问题是栈这一块很经典的问题。
迷宫大致可分为三种,简单迷宫、多通路迷宫:通路间不带环、多通路迷宫:通路间带环,其中带环多通路迷宫是最复杂的,解决它,要把栈与递归结合起来,下来我们来一个一个分析吧,先从简单迷宫开始。

简单迷宫
这里写图片描述
要解决这个问题并不难,我们只要从入口进入,当然要先检测这个入口是不是合法并且能不能走的通,如果走的通,把当前这一步的位置入栈,并且把它标记为2,然后继续向当前这一步的其他三个方向走,如果其他方向都走不了,说明上一步走错了,我们要回退,把此时的栈顶元素出栈,并把这一步标为3。
这里写图片描述
这里写图片描述
看了上边两幅图,我相信大家因该很清楚它的过程了吧,下边我们就来看看代码吧。
Maze.h //头文件

#ifndef __MAZE_H__
#define __MAZE_H__

#include<assert.h>
#include<stdio.h>

#define MaxSize 20
#define Row 6
#define Col 6

typedef struct Position
{
	int _x;
	int _y;
}Position;

typedef Position SDataType;

typedef struct Stack
{
	int top;
	SDataType _arry[MaxSize];
}Stack;

typedef struct Maze
{
	int _map[Row][Col];
}Maze;

void PassMaze(Maze* m, Position enter);
void InitMaze(Maze* m, int map[Row][Col]);
void PrintMaze(Maze* m);
int IsValidEnter(Maze* m, Position enter);//是否为入口
void StackPush(Stack* ps, SDataType data);
void StackInit(Stack* ps);
void StackPop(Stack* ps);
int IsExit(Position pos, Position enter);//是否为出口
int IsPass(Maze* m, Position pos);
int StackEmpty(Stack* ps);
SDataType StackTop(Stack* ps);//返回栈顶元素

#endif //__MAZE_H__

Maze.c //源文件

#include"Maze.h"

void InitMaze(Maze* m, int map[Row][Col])
{
	assert(m != NULL);
	int i = 0;
	int j = 0;
	for (i = 0; i < Row; i++)
	{
		for (j = 0; j < Col; j++)
		{
			m->_map[i][j] = map[i][j];
		}
	}
}

void PrintMaze(Maze* m)
{
	assert(m != NULL);
	int i = 0;
	int j = 0;
	for (i = 0; i < Row; i++)
	{
		for (j = 0; j < Col; j++)
		{
			printf("%d  ", m->_map[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

int IsValidEnter(Maze* m, Position enter)
{
	assert(m != NULL);
	if ((enter._x >= 0 || enter._x < Row) && (enter._y >= 0 || enter._y < Col))
	{
		return 1;
	}
	return 0;
}

void StackPush(Stack* ps, SDataType data)
{
	assert(ps != NULL);
	if (ps->top == MaxSize)
		return;
	else
	{
		ps->_arry[ps->top] = data;
		ps->top++;
	}
}

void StackInit(Stack* ps)
{
	assert(ps != NULL);
	ps->top = 0;
}

void StackPop(Stack* ps)
{
	assert(ps != NULL);
	if (ps->top)
		ps->top--;
}

int IsExit(Position pos, Position enter)
{
	if ((pos._x == 0 || pos._y == Row - 1) || (pos._y == 0 || pos._x == Col - 1) && (pos._x !=enter._x || pos._y !=enter._y))
	{
		return 1;
	}
	return 0;
}

int IsPass(Maze* m, Position pos)
{
	assert(m != NULL);
	if (1 == m->_map[pos._x][pos._y])
	{
		return 1;
	}
	return 0;
}

int StackEmpty(Stack* ps)
{
	assert(ps != NULL);
	if (0 == ps->top)
		return 1;//栈空
	return 0;
}

SDataType StackTop(Stack* ps)
{
	assert(ps != NULL);
	return ps->_arry[ps->top - 1];
}

void PassMaze(Maze* m, Position enter)
{
	assert(m != NULL);
	if (!IsValidEnter(m, enter))//如果入口不合法
	{
		printf("入口不合法!!!\n");
		return;
	}
	else
	{
		Stack s;
		StackInit(&s);
		StackPush(&s, enter);
		while (!StackEmpty(&s))//当栈不空时
		{
			Position pos;
			Position next;
			pos = StackTop(&s);
			m->_map[pos._x][pos._y] = 2;//将走过的路标记为2
			if (IsExit(pos, enter))//判断是不是出口
				return;
			else
			{
				//上
				next = pos;
				next._x -= 1;
				if (IsPass(m, next))//判断是否可以走通,只有为1 时才可以走通
				{
					StackPush(&s, next);
					continue;
				}
				//左
				next = pos;
				next._y -= 1;
				if (IsPass(m, next))
				{
					StackPush(&s, next);
					continue;
				}
				//右
				next = pos;
				next._y += 1;
				if (IsPass(m, next))
				{
					StackPush(&s, next);
					continue;
				}
				//下
				next = pos;
				next._x += 1;
				if (IsPass(m, next))
				{
					StackPush(&s, next);
					continue;
				}
				m->_map[pos._x][pos._y] = 3;
				StackPop(&s);
			}
		}
	}
}

test.c //测试文件

#include"Maze.h"

void test()
{
	Maze m;
	Position enter;
	enter._x = 5;
	enter._y = 2;
	int map[Row][Col] = { { 0, 0, 0, 0, 0, 0 },
	{ 0, 0, 1, 0, 0, 0 },
	{ 0, 0, 1, 0, 0, 0 },
	{ 0, 0, 1, 1, 1, 0 },
	{ 0, 0, 1, 0, 1, 1 },
	{ 0, 0, 1, 0, 0, 0 } };
	InitMaze(&m, map);
	PrintMaze(&m);
	PassMaze(&m, enter);
	PrintMaze(&m);
}

int main()
{
	test();

	return 0;
}

运行结果正确:
这里写图片描述
多通路迷宫:通路间不带环
这里写图片描述
其实解决这个问题也挺简单的,我们只要在找到一个出口后,把出口位置置为0,再让它向回退,就可以找到第二条路径了。
这里写图片描述
我们也来看看它的代码吧:
Maze.h //头文件

#ifndef __MAZE_H__
#define __MAZE_H__

#include<assert.h>
#include<stdio.h>

#define MaxSize 20
#define Row 6
#define Col 6

typedef struct Position
{
	int _x;
	int _y;
}Position;

typedef Position SDataType;

typedef struct Stack
{
	int top;
	SDataType _arry[MaxSize];
}Stack;

typedef struct Maze
{
	int _map[Row][Col];
}Maze;

void PassMaze(Maze* m, Position enter);
void InitMaze(Maze* m, int map[Row][Col]);
void PrintMaze(Maze* m);
int IsValidEnter(Maze* m, Position enter);//是否为入口
void StackPush(Stack* ps, SDataType data);
void StackInit(Stack* ps);
void StackPop(Stack* ps);
int IsExit(Position pos, Position enter);//是否为出口
int IsPass(Maze* m, Position pos);
int StackEmpty(Stack* ps);
SDataType StackTop(Stack* ps);//返回栈顶元素

#endif //__MAZE_H__

Maze.c //源文件

#include"Maze.h"

void InitMaze(Maze* m, int map[Row][Col])
{
	assert(m != NULL);
	int i = 0;
	int j = 0;
	for (i = 0; i < Row; i++)
	{
		for (j = 0; j < Col; j++)
		{
			m->_map[i][j] = map[i][j];
		}
	}
}

void PrintMaze(Maze* m)
{
	assert(m != NULL);
	int i = 0;
	int j = 0;
	for (i = 0; i < Row; i++)
	{
		for (j = 0; j < Col; j++)
		{
			printf("%d  ", m->_map[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

int IsValidEnter(Maze* m, Position enter)
{
	assert(m != NULL);
	if ((enter._x >= 0 || enter._x < Row) && (enter._y >= 0 || enter._y < Col))
	{
		return 1;
	}
	return 0;
}

void StackPush(Stack* ps, SDataType data)
{
	assert(ps != NULL);
	if (ps->top == MaxSize)
		return;
	else
	{
		ps->_arry[ps->top] = data;
		ps->top++;
	}
}

void StackInit(Stack* ps)
{
	assert(ps != NULL);
	ps->top = 0;
}

void StackPop(Stack* ps)
{
	assert(ps != NULL);
	if (ps->top)
		ps->top--;
}

int IsExit(Position pos, Position enter)
{
	if ((pos._x == 0 || pos._y == Row - 1) || (pos._y == 0 || pos._y == Col - 1) && (pos._x != enter._x || pos._y != enter._y))
	{
		return 1;
	}
	return 0;
}

int IsPass(Maze* m, Position pos)
{
	assert(m != NULL);
	if (1 == m->_map[pos._x][pos._y])
	{
		return 1;
	}
	return 0;
}

int StackEmpty(Stack* ps)
{
	assert(ps != NULL);
	if (0 == ps->top)
		return 1;//栈空
	return 0;
}

SDataType StackTop(Stack* ps)
{
	assert(ps != NULL);
	return ps->_arry[ps->top - 1];
}

void PassMaze(Maze* m, Position enter)
{
	assert(m != NULL);
	if (!IsValidEnter(m, enter))//如果入口不合法
	{
		printf("入口不合法!!!\n");
		return;
	}
	else
	{
		Stack s;
		StackInit(&s);
		StackPush(&s, enter);
		while (!StackEmpty(&s))
		{
			Position pos;
			Position next;
			pos = StackTop(&s);//取当前栈顶元素
			m->_map[pos._x][pos._y] = 2;//将走过的路标记为2
			if (IsExit(pos, enter))
			{
				m->_map[pos._x][pos._y] = 0;//将出口位置置为0
				StackPop(&s);
				continue;
			}
			else
			{
				//上
				next = pos;
				next._x -= 1;
				if (IsPass(m, next))
				{
					StackPush(&s, next);
					continue;
				}
				//左
				next = pos;
				next._y -= 1;
				if (IsPass(m, next))
				{
					StackPush(&s, next);
					continue;
				}
				//右
				next = pos;
				next._y += 1;
				if (IsPass(m, next))
				{
					StackPush(&s, next);
					continue;
				}
				//下
				next = pos;
				next._x += 1;
				if (IsPass(m, next))
				{
					StackPush(&s, next);
					continue;
				}
				m->_map[pos._x][pos._y] = 3;//将回退的路标为3
				StackPop(&s);
			}
		}
	}
}

test.c //测试文件

#include"Maze.h"

void test()
{
	Maze m;
	Position enter;
	enter._x = 5;
	enter._y = 2;
	int map[Row][Col] = { { 0, 0, 0, 0, 0, 0 },
	{ 0, 0, 1, 1, 1, 1 },
	{ 0, 0, 1, 0, 0, 0 },
	{ 0, 0, 1, 0, 0, 0 },
	{ 0, 0, 1, 1, 1, 1 },
	{ 0, 0, 1, 0, 0, 0 } };
	InitMaze(&m, map);
	PrintMaze(&m);
	PassMaze(&m, enter);
	PrintMaze(&m);
}

int main()
{
	test();

	return 0;
}

运行结果正确
这里写图片描述
最后,我们就来看看最复杂的迷宫:多通路迷宫(通路间带环)
这里写图片描述
在这里我们就不能再让走过的路直接标记为2了,因为如果再按上边那种方法,我们只能找到一条路径,但并不是最短路径,在(4,3)的时候,可以向左走但是走到左边(4,2),(4,2)位置标记为2,它的四边都走不通,只能回退,然后超(4,3)的右走,走的通,标记为2,再向(4,4)的上(不通)、左(不通)、右(通),标记为2,是出口了,出来,虽然出口是找到了,但我们发现这并不是一条最短路径,我们要找到它的最短路径,判断如果是出口,我们就让它出栈,回退,一直找到可以走第二条路为止,我们发现,在(4,1)的右边本来可以走通,找到最短路径,但刚刚我们已经走过它,并吧它标记为2了,所以不能再走了,这样我们就找不到最短路径了,所以像上边两种做法一样,把走过的路标记为2是没有办法解决复杂迷宫的。
这里写图片描述
那我们可以想另外一种办法来解决这个问题,我们可以把入口点标记为2,让每次把它的下一步标记为它的上一步加1,如果它的下一步为1或者大于它,就可以走的通,到出口让它出栈往回退。
在这次和上次不变的是如果走的通,入栈;如果走错了出栈,回退也是出栈,回退的时候,我们也要判断回退的这一步它的四个方向能否走通,因为在刚刚有可能它的其他三个方向可以走通,但我们先检测了它的上方,发现可以走通,我们就超上走了,其他可以走通的方向并没有走,所以在回退的时候我们要检测,这样就可以找到最短路径了。
这里写图片描述
我们就先来分析一下这个代码的框架吧。
这里写图片描述
在代码里我们还有很多细节,我们再来细分一下吧。
在判断是出口的时候,我们要出口先入栈,因为这时只是判断了,还并没有入栈,然后再保存最短路径,这就出现了一个问题,我们什么时候保存最短路径呢?在走迷宫的时候,我们每走一步,我们都把它的坐标保存到栈里了,所以我们保存最短路径的时候,就要比较当前栈里的元素个数与保存最短路径的栈里元素个数,如果size(Pah())<size(shortPath()),就更新shortPath()栈里的元素,还有就是,在第一次走的时候,也就是shortPath()为空时,我们要把Path()中的元素更新到shortPath()中,再出栈栈顶元素,寻找其他路径,具体代码如下:

	//判断是否为出口,若为出口,保存最短路径,Pop
	if (IsExit(m, cur, enter))
	{
		StackPush(Path, cur);
		if (StackSize(Path) < StackSize(shortPath) || StackEmpty(shortPath))
		{
			SaveshortPath(shortPath, Path);
		}
		StackPop(Path);
		//return;
	}

void SaveshortPath(Stack* shortPath, Stack* Path)
{
	assert(Path);
	assert(shortPath);
	int i = 0;
	int size = 0;
	size = StackSize(Path);
	for (i = 0; i < size; i++)
	{
		shortPath->_arry[i] = Path->_arry[i];
	}
	shortPath->top = size;
}

判断下一步能否走通(我们在前边也分析了,只要下一步比上一步的值大也可以走通):

int IsPass(Maze* m, Position cur, Position next)
{
	assert(m != NULL);
	if (m->_map[next._x][next._y] > m->_map[cur._x][cur._y] || m->_map[next._x][next._y] == 1)
		return 1;

	return 0;
}

下来,我们看一下完整的代码吧:
Maze.h //头文件
我们要定义一个栈的结构体,每次入栈的元素类型是坐标形式的,所以还要定义一个Position的结构体。

#ifndef __MAZE_H__
#define __MAZE_H__

#include<stdio.h>
#include<assert.h>

#define MaxSize 20
#define Row 6
#define Col 6

typedef struct Position
{
	int _x;
	int _y;
}Position;

typedef Position SDataType;

typedef struct Stack
{
	SDataType _arry[MaxSize];
	int top;
}Stack;

typedef struct Maze
{
	int _map[Row][Col];
}Maze;

void PrintMaze(Maze* m);
void InitMaze(Maze* m, int map[Row][Col]);
void PassMaze(Maze* m, Position enter, Stack* shortPath);
void StackInit(Stack* ps);
void StackPush(Stack* ps, SDataType data);
void StackPop(Stack* ps);
int IsExit(Maze* m, Position pos, Position enter);
int StackEmpty(Stack* ps);
SDataType StackTop(Stack* ps);
int IsValidEnter(Maze* m, Position enter);
int StackSize(Stack* ps);
int IsPass(Maze* m, Position cur, Position next);
void _GetMazeshortPath(Maze* m, Position cur, Position enter, Stack* Path, Stack* shortPath);
void SaveshortPath(Stack* Path, Stack* shortPath);

#endif //__MAZE_H__

Maze.c //源文件
还有很多细节,比如我们在定义一个栈之后我们要把它初始化,不然就会出错。
在void PassMaze(Maze* m, Position enter, Stack* shortPath)函数中,我们只需要把地图,入口,和保存最短路径的栈传进去就行,因为我们左后需要的就是最短路径。

void PassMaze(Maze* m, Position enter, Stack* shortPath)
{
	Stack Path;
	StackInit(&Path);
	assert(m != NULL);
	if (!IsValidEnter(m, enter))//如果入口不合法
	{
		printf("入口不合法!!!\n");
		return;
	}
	else
		_GetMazeshortPath(m, enter, enter, &Path, shortPath);
}

在void _GetMazeshortPath(Maze* m, Position cur, Position enter, Stack* Path, Stack* shortPath)函数中,我们还要把最开始的入口点传入,因为我们要判断当前位置是否为出口,判断出口的方法就是在判断是入口的基础上加上不能为入口这一条,我们还要传入Path()这个栈的指针,因为我们要保存当前路径。

void _GetMazeshortPath(Maze* m, Position cur, Position enter, Stack* Path, Stack* shortPath)
{
	Position next;
	//判断是否为出口,若为出口,保存最短路径,Pop
	if (IsExit(m, cur, enter))
	{
		StackPush(Path, cur);
		if (StackSize(Path) < StackSize(shortPath) || StackEmpty(shortPath))
		{
			SaveshortPath(shortPath, Path);
		}
		StackPop(Path);
		//return;
	}
	if (StackEmpty(Path))
		m->_map[enter._x][enter._y] = 2;
	StackPush(Path, cur);

	//上
	next = cur;
	next._x -= 1;
	if (IsPass(m, cur, next))
	{
		m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;//标记
		_GetMazeshortPath(m, next, enter, Path, shortPath);
	}

	//左
	next = cur;
	next._y -= 1;
	if (IsPass(m, cur, next))
	{
		m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;
		_GetMazeshortPath(m, next, enter, Path, shortPath);
	}


	//右
	next = cur;
	next._y += 1;
	if (IsPass(m, cur, next))
	{
		m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;
		_GetMazeshortPath(m, next, enter, Path, shortPath);
	}

	//下
	next = cur;
	next._x += 1;
	if (IsPass(m, cur, next))
	{
		m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;
		_GetMazeshortPath(m, next, enter, Path, shortPath);
	}

	//说明上步走错了
	StackPop(Path);
}

test.c //测试文件

#include"Maze.h"

void test()
{
	Position enter;
	enter._x = 5;
	enter._y = 1;
	Maze m;
	Stack shortPath;
	StackInit(&shortPath);
	int map[Row][Col] = {
	{ 0, 0, 0, 0, 0, 0 },
	{ 0, 1, 1, 1, 0, 0 },
	{ 0, 1, 0, 1, 0, 0 },
	{ 0, 1, 0, 1, 0, 0 },
	{ 0, 1, 1, 1, 1, 1 },
	{ 0, 1, 0, 0, 0, 0 } };
	InitMaze(&m, map);
	PrintMaze(&m);
	PassMaze(&m, enter, &shortPath);
	PrintMaze(&m);
}

int main()
{
	test();

	return 0;
}

运行结果正确:
这里写图片描述

  • 119
    点赞
  • 564
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值