栈的应用:迷宫问题(DFS,非递归)

目录

问题描述:

问题分析:

(1)当前状态表示

(2)方向试探表示

(3)路径标记问题

(4)栈中元素

(5)基本思路

完整代码:

(1)栈的操作.h

 (2)迷宫.c

(3)输出测试


问题描述:

从入口出发,按某一方向向前探索,若能走通(未走过的),即某处可以到达,则到达新的点,则试探下一方向;若该点所有的方向均没有通路,则沿原路返回到前一点,换下一个方向再继续试探,直到所有可能的通路都探索到,或找到一条通路,或无路可走又退回到入口点。退回到的“前一点”正是刚刚才被访问过的,具有“后进先出”的特性,需要用栈保存所能够到达的每一点的下标及从该点前进的方向。

(以下是基于懒猫老师的《跟懒猫老师快乐数据结构》有关内容的一些自己的实现~有错误欢迎指正^-^)

问题分析:

(1)当前状态表示

typedef struct {
	int x, y; //当前访问的迷宫格子的横纵坐标
	int di;//当前方向;  0--向右 1--向下 2--向左 3--向上
} Box

(2)方向试探表示

typedef struct {
	int incX;//x方向增量,可取值1,0,-1
	int incY;//y方向增量,可取值1,0,-1
} Direction;

可以通过下图来进行理解:

创建一个 Direction类型的数组,数组中每个元素的值为下表:

 左表对应右表,相应的可以得到,direct[0]是向右走,direct[1]是向下走,direct[2]是向左走,direct[3]是向上走

(3)路径标记问题

因为走迷宫要将走过的路进行标记,防止最后陷入死循环,这里有两种思路:

思路一: 另外设置一标志数组flag[m][n],其所有元素都初始化为0,一旦到达了某点之后,将对应
的flag[i][j]设置1,下次再试探该位買时,因为己经置1了,就不能再选它了
思路二:当到达某点(i,j)后将对应maze[i][j]置-1,其他末到达过的点其值只能是1或0,可与未到达过的点区别开来。

下面代码的实现主要采用的是思路二

(4)栈中元素

栈中元素的数据类型是(1)中所示的Box结构体,包含当前的位置x,y,还有方向di

(当时没想到可以建结构体当栈的数据类型,还在那研究怎么弄成数组或者字符串,这就太麻烦了)

(5)基本思路

如果迷宫块不是墙,则顺时针寻找可走的通路,寻找到通路后,把当前迷宫块入栈,更新新的迷宫块;如果没有寻找到通路,弹出栈顶迷宫块,顺时针换一个方向重新寻找通路;若栈中迷宫块最后被全部弹出,则迷宫无解。

完整代码:

迷宫大小和通路状况需要手动自己更新,将这里以10x10迷宫为例:

(数组堆栈.h包和迷宫.c包合并就是完整的代码)

(1)数组堆栈.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10

typedef struct {
	int x, y; //当前访问的迷宫格子的横纵坐标
	int di;//当前方向;  0--向右 1--向下 2--向左 3--向上
} Box;

typedef Box Elemtype;//太妙了还可以这样,用Box自定义的数据类型
typedef struct {
	Elemtype *data;
	int top;//栈顶指针,这里用int类型表示指针的下标
	int stacksize;
} SqStack;

Elemtype Pop(SqStack *s);

SqStack InitStack() {//空栈构造函数
	SqStack s;
	s.data = (Elemtype *)malloc(STACK_INIT_SIZE * sizeof(Elemtype));
	s.top = -1; //表示栈空
	s.stacksize = STACK_INIT_SIZE;
	if (s.data != NULL)
	{}
	else
		printf("Init error!\n");
	return s;
}

void DestroyStack(SqStack *s) {//销毁栈函数
	free(s->data);
}

int StackEmpty(SqStack *s) {//判断是否为空栈,是返回1,否 返回0
	if (s->top == -1)
		return 1;
	else
		return 0;
}

void ClearStack(SqStack *s) {//清除栈
	while (StackEmpty(s) != 1) {
		Pop(s);
	}
}

void Push(SqStack *s, Elemtype e) {//添加元素入栈
	if (s->top >= s->stacksize) {
		s->data = (Elemtype *)malloc((STACK_INIT_SIZE + STACKINCREMENT) * sizeof(Elemtype));
		s->stacksize += STACKINCREMENT;
		if (s->data != NULL) {}
		else
			printf("Push error!\n");
	} else {
		s->top++;
		s->data[s->top] = e;
	}
}

Elemtype Pop(SqStack *s) {
	if (StackEmpty(s) != 1 && s->top >= 0) {
		Elemtype e = s->data[s->top];
		s->top--;
		return e;
	}
	printf("Pop error!\n");
}

int StackLength(SqStack *s) {
	int len = 0, temp = s->top;
	while (temp >= 0) {
		len++;
		temp--;
	}
	return len;
}

Elemtype GetTop(SqStack *s) {
	if (StackEmpty(s) != 1) {
		return s->data[s->top];
	} else
		printf("GetTop error!\n");
}

int StackTraverse(SqStack *s) {//从栈底向栈顶访问每个元素
	if (StackEmpty(s) == 1) {
		printf("栈为空!\n");
		return 0;
	}
	int temp = 0;
	while (temp <= s->top) {
		printf("%c ", s->data[temp]);
		temp++;
	}
	return 1;
}

 (2)迷宫.c

#include "数组堆栈.h"
#define MAX_ROW 10
#define MAX_COL 10

typedef struct {
	int incX;//x方向增量,可取值1,0,-1
	int incY;//y方向增量,可取值1,0,-1
} Direction;
// 0--向右 1--向下 2--向左 3--向上
int findPath(Direction *, SqStack *);

Direction direct[4] = {
	0, 1,//向右
	1, 0,//向下
	0, -1,//向左
	-1, 0,//向上
};

int maze[MAX_ROW][MAX_COL] = {//初始化迷宫
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
	1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
	1, 0, 0, 0, 0, 1, 1, 0, 0, 1,
	1, 0, 1, 1, 1, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 1, 0, 0, 0, 0, 1,
	1, 0, 1, 0, 0, 0, 1, 0, 0, 1,
	1, 0, 1, 1, 1, 0, 1, 1, 0, 1,
	1, 1, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};

main() {
	SqStack s;
	s = InitStack();
	if (findPath(&direct, &s)) { //迷宫有解
		printf("迷宫从入口到出口路径的坐标依次为:\n");
		int temp = 0;
		while (temp <= s.top) {
			printf("(%d,%d)\n", s.data[temp].x, s.data[temp].y);
			temp++;
		}
		printf("用地图形式表示迷宫的路线:(字母表示顺序)\n");
		display(&s);
	} else {
		printf("该迷宫无解!");
	}
	DestroyStack(&s);
	return 0;
}

int findPath(Direction direct[], SqStack *s) {
	Box temp;
	int x, y, di; //迷宫格子当前处理单元的纵横坐标和方向
	int line, col; //迷宫数组下一单元的行坐标和列坐标
	maze[1][1] = -1; //0代表通路,1代表墙,-1代表已经走过了
	temp.x = 1, temp.y = 1, temp.di = -1;
	Push(s, temp);
	while (StackEmpty(s) != -1) { //若栈不为空
		temp = Pop(s); //当四个方向都不成立时,就会退回去一步
		x = temp.x, y = temp.y;
		di = temp.di + 1;
		while (di < 4) { //是否尝试完所有的方向
			line = x + direct[di].incX;
			col = y + direct[di].incY; //完成下一步的坐标赋值
			if (maze[line][col] == 0) { //如果是通路,入栈
				temp.x = x, temp.y = y, temp.di = di;
				Push(s, temp);
				x = line, y = col, di = 0; //更新当前状态
				maze[line][col] = -1;
				if (x == MAX_ROW - 2 && y == MAX_COL - 2) { //到达出口
					temp.x = x, temp.y = y, temp.di = di;
					Push(s, temp);
					return 1;
				}
			} else { //如果不是通路,换方向
				di++;
			}

		}
	}//不存在可通路
	return 0;
}

void display(SqStack *s) {
	char map[MAX_ROW][MAX_COL];
	for (int i = 0; i < MAX_ROW; i++) {
		for (int j = 0; j < MAX_COL; j++)
			map[i][j] = '.';
	}
	int temp = 0;
	int i = 0;
	while (temp <= s->top) {
		if (i == 25)
			i = 0; //超过26个字母就重来
		map[s->data[temp].x][ s->data[temp].y] = 'a' + i;
		i++;
		temp++;
	}
	for (int i = 0; i < MAX_ROW; i++) {
		for (int j = 0; j < MAX_COL; j++) {
			printf("%c ", map[i][j]);
			if (j == MAX_COL - 1)
				printf("\n");
		}
	}

}

(3)输出测试

 

 正确迷宫图形参考:(就是上例中的迷宫图形化)

初学小白,有错误的话欢迎指正喔~~

  • 3
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想写好代码的小猫头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值