求解迷宫问题的三种方法(第三章)

本文介绍三种迷宫求解算法,包括利用栈求解迷宫(仅输出一个解)、使用递归函数求解迷宫(输出所有解)以及采用广度优先搜索的非循环顺序队列求解迷宫(输出一条路径)。每种方法详细展示了如何通过不同数据结构和搜索策略找到从起点到终点的路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一种:

利用栈求解迷宫问题(只输出一个解,算法3.3)

 

typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */

/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 



/*   利用栈求解迷宫问题(只输出一个解,算法3.3)  */
typedef struct /* 迷宫坐标位置类型 */
{
	int x; /* 行值 */
	int y; /* 列值 */
}PosType;

#define MAXLENGTH 25 /* 设迷宫的最大行列为25 */
typedef int MazeType[MAXLENGTH][MAXLENGTH]; /* 迷宫数组[行][列] */

/* 全局变量 */
MazeType m; /* 迷宫数组 */
int curstep = 1; /* 当前足迹,初值为1 */

typedef struct /* 栈的元素类型 */
{
	int ord; /* 通道块在路径上的"序号" */
	PosType seat; /* 通道块在迷宫中的"坐标位置" */
	int di; /* 从此通道块走向下一通道块的"方向"(0~3表示东~北) */
}SElemType;


/* --------------------------   栈的顺序存储表示   -----------------------------*/

#define STACK_INIT_SIZE 10 /* 存储空间初始分配量 */
#define STACKINCREMENT 2 /* 存储空间分配增量 */
typedef struct SqStack
{
	SElemType *base; /* 在栈构造之前和销毁之后,base的值为NULL */
	SElemType *top; /* 栈顶指针 */
	int stacksize; /* 当前已分配的存储空间,以元素为单位 */
}SqStack; /* 顺序栈 */

/* ----------------------------------------------------------------------------*/



/* -----------------------   需要用到的顺序栈的操作   ---------------------------*/

Status InitStack(SqStack *S)
{ /* 构造一个空栈S */
	(*S).base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if (!(*S).base)
		exit(OVERFLOW); /* 存储分配失败 */
	(*S).top = (*S).base;
	(*S).stacksize = STACK_INIT_SIZE;
	return OK;
}

Status StackEmpty(SqStack S)
{ /* 若栈S为空栈,则返回TRUE,否则返回FALSE */
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}

Status Push(SqStack *S, SElemType e)
{ /* 插入元素e为新的栈顶元素 */
	if ((*S).top - (*S).base >= (*S).stacksize) /* 栈满,追加存储空间 */
	{
		(*S).base = (SElemType *)realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
		if (!(*S).base)
			exit(OVERFLOW); /* 存储分配失败 */
		(*S).top = (*S).base + (*S).stacksize;
		(*S).stacksize += STACKINCREMENT;
	}
	*((*S).top)++ = e;
	return OK;
}

Status Pop(SqStack *S, SElemType *e)
{ /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
	if ((*S).top == (*S).base)
		return ERROR;
	*e = *--(*S).top;
	return OK;
}


/* ----------------------------------------------------------------------------*/


/* 定义墙元素值为0,可通过路径为1,不能通过路径为-1,通过路径为足迹 */

Status Pass(PosType b)
{ /* 当迷宫m的b点的序号为1(可通过路径),return OK; 否则,return ERROR。 */
	if (m[b.x][b.y] == 1)
		return OK;
	else
		return ERROR;
}

void FootPrint(PosType a)
{ /* 使迷宫m的a点的序号变为足迹(curstep) */
	m[a.x][a.y] = curstep;
}

PosType NextPos(PosType c, int di)
{ /* 根据当前位置及移动方向,返回下一位置 */
	PosType direc[4] = { {0,1},{1,0},{0,-1},{-1,0} }; /* {行增量,列增量} */
	/* 移动方向,依次为东南西北 */
	c.x += direc[di].x;
	c.y += direc[di].y;
	return c;
}

void MarkPrint(PosType b)
{ /* 使迷宫m的b点的序号变为-1(不能通过的路径) */
	m[b.x][b.y] = -1;
}

Status MazePath(PosType start, PosType end) /* 算法3.3 */
{ /* 若迷宫maze中存在从入口start到出口end的通道,则求得一条 */
  /* 存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE */
	SqStack S;
	PosType curpos;
	SElemType e;
	InitStack(&S);
	curpos = start;
	do
	{
		if (Pass(curpos))
		{ /* 当前位置可以通过,即是未曾走到过的通道块 */
			FootPrint(curpos); /* 留下足迹 */
			e.ord = curstep;
			e.seat.x = curpos.x;
			e.seat.y = curpos.y;
			e.di = 0;
			Push(&S, e); /* 入栈当前位置及状态 */
			curstep++; /* 足迹加1 */
			if (curpos.x == end.x&&curpos.y == end.y) /* 到达终点(出口) */
				return TRUE;
			curpos = NextPos(curpos, e.di);
		}
		else
		{ /* 当前位置不能通过 */
			if (!StackEmpty(S))
			{
				Pop(&S, &e); /* 退栈到前一位置 */
				curstep--;
				while (e.di == 3 && !StackEmpty(S)) /* 前一位置处于最后一个方向(北) */
				{
					MarkPrint(e.seat); /* 留下不能通过的标记(-1) */
					Pop(&S, &e); /* 退回一步 */
					curstep--;
				}
				if (e.di < 3) /* 没到最后一个方向(北) */
				{
					e.di++; /* 换下一个方向探索 */
					Push(&S, e);
					curstep++;
					curpos = NextPos(e.seat, e.di); /* 设定当前位置是该新方向上的相邻块 */
				}
			}
		}
	} while (!StackEmpty(S));
	return FALSE;
}

void Print(int x, int y)
{ /* 输出迷宫的解 */
	int i, j;
	for (i = 0; i < x; i++)
	{
		for (j = 0; j < y; j++)
			printf("%3d", m[i][j]);
		printf("\n");
	}
}

void main()
{
	PosType begin, end;
	int i, j, x, y, x1, y1;
	printf("请输入迷宫的行数,列数(包括外墙):");
	scanf("%d,%d", &x, &y);
	for (i = 0; i < x; i++) /* 定义周边值为0(同墙) */
	{
		m[0][i] = 0; /* 行周边 */
		m[x - 1][i] = 0;
	}
	for (j = 1; j < y - 1; j++)
	{
		m[j][0] = 0; /* 列周边 */
		m[j][y - 1] = 0;
	}
	for (i = 1; i < x - 1; i++)
		for (j = 1; j < y - 1; j++)
			m[i][j] = 1; /* 定义通道初值为1 */
	printf("请输入迷宫内墙单元数:");
	scanf("%d", &j);
	printf("请依次输入迷宫内墙每个单元的行数,列数:\n");
	for (i = 1; i <= j; i++)
	{
		scanf("%d,%d", &x1, &y1);
		m[x1][y1] = 0; /* 定义墙的值为0 */
	}
	printf("迷宫结构如下:\n");
	Print(x, y);
	printf("请输入起点的行数,列数:");
	scanf("%d,%d", &begin.x, &begin.y);
	printf("请输入终点的行数,列数:");
	scanf("%d,%d", &end.x, &end.y);
	if (MazePath(begin, end)) /* 求得一条通路 */
	{
		printf("此迷宫从入口到出口的一条路径如下:\n");
		Print(x, y); /* 输出此通路 */
	}
	else
		printf("此迷宫没有从入口到出口的路径\n");
}

运行结果:

 

 

第二种:

用递归函数求解迷宫问题(求出所有解)

 

/* 用递归函数求解迷宫问题(求出所有解) */
#include<stdio.h> 
struct PosType /* 迷宫坐标位置类型 */
{
	int x; /* 行值 */
	int y; /* 列值 */
};

#define MAXLENGTH 25 /* 设迷宫的最大行列为25 */
typedef int MazeType[MAXLENGTH][MAXLENGTH]; /* [行][列] */

/* 全局变量 */
struct PosType end; /* 迷宫终点位置 */
MazeType m; /* 迷宫数组 */
int x, y; /* 迷宫行数,列数 */

/* 定义墙元素值为0,可通过路径为-1,通过路径为足迹 */

void Print(int x, int y)
{ /* 输出解 */
	int i, j;
	for (i = 0; i < x; i++)
	{
		for (j = 0; j < y; j++)
			printf("%3d", m[i][j]);
		printf("\n");
	}
	printf("\n");
}

void Try(struct PosType cur, int curstep)
{ /* 由当前位置cur、当前步骤curstep试探下一点 */
	int i;
	struct PosType next; /* 下一个位置 */
	struct PosType direc[4] = { {0,1},{1,0},{0,-1},{-1,0} }; /* {行增量,列增量} */
	/* 移动方向,依次为东南西北 */
	for (i = 0; i <= 3; i++) /* 依次试探东南西北四个方向 */
	{
		next.x = cur.x + direc[i].x;
		next.y = cur.y + direc[i].y;
		if (m[next.x][next.y] == -1) /* 是通路 */
		{
			m[next.x][next.y] = ++curstep;
			if (next.x != end.x || next.y != end.y) /* 没到终点 */
				Try(next, curstep); /* 试探下一点(递归调用) */
			else
				Print(x, y); /* 输出结果 */
			m[next.x][next.y] = -1; /* 恢复为通路,试探下一条路 */
			curstep--;
		}
	}
}

void main()
{
	struct PosType begin;
	int i, j, x1, y1;
	printf("请输入迷宫的行数,列数(包括外墙):");
	scanf_s("%d,%d", &x, &y);
	for (i = 0; i < x; i++) /* 定义周边值为0(同墙) */
	{
		m[0][i] = 0; /* 行周边 */
		m[x - 1][i] = 0;
	}
	for (j = 1; j < y - 1; j++)
	{
		m[j][0] = 0; /* 列周边 */
		m[j][y - 1] = 0;
	}
	for (i = 1; i < x - 1; i++)
		for (j = 1; j < y - 1; j++)
			m[i][j] = -1; /* 定义通道初值为-1 */
	printf("请输入迷宫内墙单元数:");
	scanf_s("%d", &j);
	if (j)
		printf("请依次输入迷宫内墙每个单元的行数,列数:\n");
	for (i = 1; i <= j; i++)
	{
		scanf_s("%d,%d", &x1, &y1);
		m[x1][y1] = 0;
	}
	printf("迷宫结构如下:\n");
	Print(x, y);
	printf("请输入起点的行数,列数:");
	scanf_s("%d,%d", &begin.x, &begin.y);
	printf("请输入终点的行数,列数:");
	scanf_s("%d,%d", &end.x, &end.y);
	m[begin.x][begin.y] = 1;
	Try(begin, 1); /* 由第一步起点试探起 */
}

运行结果:

 

第三种:

利用非循环顺序队列采用广度搜索法求解迷宫问题(一条路径) 

 

/* 利用非循环顺序队列采用广度搜索法求解迷宫问题(一条路径) */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */

/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 


#define M 5 /* 迷宫行数(包括外墙) */
#define N 5 /* 迷宫列数(包括外墙) */
#define D 8 /* 移动方向数,只能取4和8。(8个,可斜行;4个,只可直走) */

typedef struct /* 定义队列元素和栈元素为同类型的结构体 */
{
	int x, y; /* 当前点的行值,列值 */
	int pre; /* 前一点在队列中的序号 */
}QElemType, SElemType; /* 定义栈元素和队列元素 */


/* -----------------------      栈的顺序存储表示   ----------------------------*/

#define STACK_INIT_SIZE 10 /* 存储空间初始分配量 */
#define STACKINCREMENT 2 /* 存储空间分配增量 */
typedef struct SqStack
{
	SElemType *base; /* 在栈构造之前和销毁之后,base的值为NULL */
	SElemType *top; /* 栈顶指针 */
	int stacksize; /* 当前已分配的存储空间,以元素为单位 */
}SqStack; /* 顺序栈 */


/* -----------------------     队列的顺序存储结构    ----------------------------*/

#define MAXQSIZE 10 /* 最大队列长度(对于循环队列,最大队列长度要减1) */
typedef struct
{
	QElemType *base; /* 初始化的动态分配存储空间 */
	int front; /* 头指针,若队列不空,指向队列头元素 */
	int rear; /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}SqQueue;


/* ------------------------       需要用到的顺序栈操作      ------------------------------*/

Status InitStack(SqStack *S)
{ /* 构造一个空栈S */
	(*S).base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if (!(*S).base)
		exit(OVERFLOW); /* 存储分配失败 */
	(*S).top = (*S).base;
	(*S).stacksize = STACK_INIT_SIZE;
	return OK;
}

Status StackEmpty(SqStack S)
{ /* 若栈S为空栈,则返回TRUE,否则返回FALSE */
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}

Status Push(SqStack *S, SElemType e)
{ /* 插入元素e为新的栈顶元素 */
	if ((*S).top - (*S).base >= (*S).stacksize) /* 栈满,追加存储空间 */
	{
		(*S).base = (SElemType *)realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
		if (!(*S).base)
			exit(OVERFLOW); /* 存储分配失败 */
		(*S).top = (*S).base + (*S).stacksize;
		(*S).stacksize += STACKINCREMENT;
	}
	*((*S).top)++ = e;
	return OK;
}

Status Pop(SqStack *S, SElemType *e)
{ /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
	if ((*S).top == (*S).base)
		return ERROR;
	*e = *--(*S).top;
	return OK;
}


/* -----------------------------------------------------------------------------------------*/


/* ------------------------       需要用到的顺序队列(非循环)操作      ------------------------------*/

Status InitQueue(SqQueue *Q)
{ /* 构造一个空队列Q */
	(*Q).base = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType));
	if (!(*Q).base) /* 存储分配失败 */
		exit(OVERFLOW);
	(*Q).front = (*Q).rear = 0;
	return OK;
}

Status QueueEmpty(SqQueue Q)
{ /* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
	if (Q.front == Q.rear) /* 队列空的标志 */
		return TRUE;
	else
		return FALSE;
}

Status EnQueue(SqQueue *Q, QElemType e)
{ /* 插入元素e为Q的新的队尾元素 */
	if ((*Q).rear >= MAXQSIZE)
	{ /* 队列满,增加1个存储单元 */
		(*Q).base = (QElemType *)realloc((*Q).base, ((*Q).rear + 1) * sizeof(QElemType));
		if (!(*Q).base) /* 增加单元失败 */
			return ERROR;
	}
	*((*Q).base + (*Q).rear) = e;
	(*Q).rear++;
	return OK;
}

Status DeQueue(SqQueue *Q, QElemType *e)
{ /* 若队列不空,则删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */
	if ((*Q).front == (*Q).rear) /* 队列空 */
		return ERROR;
	*e = (*Q).base[(*Q).front];
	(*Q).front = (*Q).front + 1;
	return OK;
}


/* --------------------------------------------------------------------------------------------*/


struct /* 移动数组,移动方向由正东起顺时针转 */
{
	int x, y;
#if D==8
}move[D] = { {0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1} };
#endif
#if D==4        //如果#if 后面的是0,那么执行#endif 后面的代码。反之为1的话,相反。
 }move[D] = { {0,1},{1,0},{0,-1},{-1,0} };
#endif

 Status Path(int maze[M][N]) /* 广度搜索法求一条迷宫路径 */
 {
	 SqQueue q; /* 采用非循环顺序队列 */
	 QElemType qf, qt; /* 当前点和下一点 */
	 SqStack s; /* 采用顺序栈 */
	 int i, j, flag = 1; /* 当找到出口,flag=0 */
	 int x1, y1; /* 终点的坐标 */
	 printf("请输入入口的行,列(左上角为1,1)\n");
	 scanf_s("%d,%d", &qf.x, &qf.y);
	 printf("请输入出口的行,列(右下角为%d,%d)\n", M - 2, N - 2);
	 scanf_s("%d,%d", &x1, &y1);
	 qf.pre = -1; /* 设入口(第一点)的上一点的序号=-1 */
	 maze[qf.x][qf.y] = -1; /* 初始点设为-1(已访问过) */
	 InitQueue(&q);
	 EnQueue(&q, qf); /* 起点入队 */
	 while (!QueueEmpty(q) && flag)
	 { /* 队列中还有没被广度搜索过的点且还没找到出口 */
		 DeQueue(&q, &qf); /* 出队qf为当前点 */
		 for (i = 0; i < D; i++) /* 向各个方向尝试 */
		 {
			 qt.x = qf.x + move[i].x; /* 下一点的坐标 */
			 qt.y = qf.y + move[i].y;
			 if (maze[qt.x][qt.y] == 1)
			 { /* 此点是通道且不曾被访问过 */
				 maze[qt.x][qt.y] = -1; /* 已访问过 */
				 qt.pre = q.front - 1; /* 上一点处于队列中现队头减一的位置(没删除) */
				 EnQueue(&q, qt); /* 入队 */
				 if (qt.x == x1 && qt.y == y1) /* 到达终点 */
				 {
					 flag = 0;
					 break;
				 }
			 }
		 }
	 }
	 if (flag) /* 搜索完整个队列还没到达终点 */
	 {
		 printf("没有路径可到达终点!\n");
		 return ERROR;
	 }
	 else
	 {
		 InitStack(&s); /* 初始化s栈 */
		 i = q.rear - 1; /* i为待入栈元素在队列中的位置 */
		 while (i >= 0) /* 没到入口 */
		 {
			 Push(&s, *(q.base + i));
			 i = (*(q.base + i)).pre; /* i为前一元素在队列中的位置 */
		 }
		 i = 0; /* i为走出迷宫的步骤 */
		 while (!StackEmpty(s))
		 {
			 Pop(&s, &qf);
			 i++;
			 maze[qf.x][qf.y] = i;
		 }
		 printf("走出迷宫的一个方案:\n");
		 for (i = 1; i < M - 1; i++) /* 输出maze[][],其值是走出迷宫的步骤 */
		 {
			 for (j = 1; j < N - 1; j++)
				 printf("%3d", maze[i][j]);
			 printf("\n");
		 }
		 return OK;
	 }
 }

 void main()
 {
	 int i, j;
	 int maze[M][N]; /* 迷宫数组 */
	 printf("%d行%d列迷宫(不包括外墙)\n", M - 2, N - 2);
	 for (i = 0; i < N; i++)
	 { /* 0为墙,1为通道 */
		 maze[0][i] = 0; /* 北墙 */
		 maze[M - 1][i] = 0; /* 南墙 */
	 }
	 for (i = 1; i < M - 1; i++)
	 {
		 maze[i][0] = 0; /* 西墙 */
		 maze[i][N - 1] = 0; /* 东墙 */
	 }
	 printf("请按行输入迷宫结构(不包括周边,0为墙,1为通道),如1 0 0 1\n");
	 for (i = 1; i < M - 1; i++)
		 for (j = 1; j < N - 1; j++)
			 scanf_s("%d", &maze[i][j]);
	 printf("迷宫结构(包括外墙):\n");
	 for (i = 0; i < M; i++)
	 {
		 for (j = 0; j < N; j++)
			 printf("%3d", maze[i][j]);
		 printf("\n");
	 }
	 Path(maze);
 }

运行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值