栈的应用:迷宫问题(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)输出测试

 

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

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_63223213/article/details/125592675

智能推荐

元素选择器之排除特定元素_input排他选择器-程序员宅基地

文章浏览阅读2.1k次。 需求如下:该搜索框是对整个页面的input检索 ,但与弹出层中的input冲突 博主几经辗转 简单处理 解决问题,思路如下:排除掉特定class的input。代码如下:$('input:not(.pop)', this.footer()).on('keyup change', function () { if (that.search() !== th..._input排他选择器

使用JAXB进行XML与JavaBean的转换(支持泛型)_jaxb 泛型-程序员宅基地

文章浏览阅读5.6k次,点赞6次,收藏20次。看到别人有个1024的勋章,特意留了一篇在今年的10.24日,看看会不会获得。在日常开发中可能涉及接口之间的相互调用,虽然在现在微服务的理念推广下,很多公司都采用轻量级的JSON格式做为序列化的格式,但是不乏有些公司还是有一些XML格式的报文,最近就在对接某个合作方的时候遇到了XML报文。在JSON报文爽快的转换下很难试用一个一个的拿报文参数,还是希望能直接将报文转换成Bean。接下来就了解到..._jaxb 泛型

python numpy学习笔记_ndarray的位置-程序员宅基地

文章浏览阅读1.2k次。numpy的主要数据对象是多维数组,其中包含相同类型的元素,通常是数字类型,每个元素都有一个索引。使用numpy前通常要导入包。import numpy as np目录类型维度创建运算索引和切片类型numpy的数组被称为ndarray。numpy.array只处理一维数组,而ndarray对象才提供更多功能。a = np.array([[1, 2, 3], [4, 5, 6]])type(a) # <class 'numpy.ndarray'>dtype属性可以获得元素的数_ndarray的位置

我的世界java版gamemode指令_《我的世界》Java版常用指令代码大全!你想要的都在这里了!...-程序员宅基地

文章浏览阅读1.6w次。还在苦于网上找到的一些指令已经不适用了吗?还在苦于有些地方的指令有误吗?还在苦于有些地方整理的指令不够全面吗?那么你来对地方了!小编为大家整理了《我的世界》原版游戏常用的指令,这些基本足以满足各位的基本需求了!大家来一起看看吧!注:表示的是必须输入的部分,[方括号]表示的是可选择性输入的部分基本命令列表命令描述/?/help的替代命令,提供命令使用帮助。/ban + 玩家名字将玩家加入封禁列表。/..._gamemode指令java

Spring Boot 结合shiro做第三方登录验证_shiro 第三方token登录-程序员宅基地

文章浏览阅读1.5w次,点赞3次,收藏3次。Spring Boot 结合shiro做第三方登录验证1、首先,说一下我的具体实现思路。在做spring boot拦截器的过程中,开始我准备用spring security来实现,但是研究了一段时间之后发现spring security的集成度太高,需要修改的东西比较多,而且对它本身的使用方法不是很了解,后来转而使用Apache shiro。由于是第三方登录,是不需要我来验证密码的。最开始,我陷入了_shiro 第三方token登录

labelme UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xaf in position 227: illegal mult_file "c:\rgzn\labelme-main\setup.py", line 91, in -程序员宅基地

文章浏览阅读1.9k次,点赞4次,收藏4次。[INFO ] __init__:get_config:71 - Loading config file from:C:\Users\xxx\.labelmercTraceback (most recent call last): File .... line 191, in <module> main() File ...., line 145, in main config = get_config(config_file_or_yaml, config_fro_file "c:\rgzn\labelme-main\setup.py", line 91, in main if sys.argv[1] == "re

随便推点

代码报错原因和处理方法-程序员宅基地

文章浏览阅读8.7k次。代码错误的原因和调试方法_代码报错

深度解析Java游戏服务器开发-程序员宅基地

文章浏览阅读5.2k次,点赞9次,收藏40次。---恢复内容开始---1.认识游戏  1.1什么是游戏    1.1.1游戏的定义              任何人类正常生理需求之外的活动均可称为游戏    1.1.2游戏的分类      RPG角色扮演游戏、ACT动作游戏、AVG冒险游戏、FPS第一人称视角射击游戏、TPS第三人称视角射击游戏、FTG格斗游戏、SPT体育游戏、RAC竞速游戏、RTS即时战略游戏、STG..._深度解析java游戏服务器开发

【ThinkPHP5初体验(二)1】CSRF防范原理(thinkphp5 CSRF ajax令牌)_tp5 开启csrf令牌-程序员宅基地

文章浏览阅读4k次。CSRF是什么我就不解释了,百度一搜全是,比波姐的片源还要多,千篇一律都他么是复制粘贴。那为什么这个令牌(token)操作可以防范CSRF呢?下面我就随便说说说错了大家不要介意。首先我们要知道令牌是存储在session里面的,这个很重要 php代码如下&lt;?php namespace app\index\controller; //我直接允许跨域,因为伪装..._tp5 开启csrf令牌

市盈率、市净率、净资产收益率股息率介绍-程序员宅基地

文章浏览阅读1.7k次,点赞2次,收藏6次。市盈率PE市盈率 = 市值/净利润概念解析:买入一家公司,几年回本,年化收益率:净利润/市值(市盈率的倒数)举例:砖头10万买个砖头,每年拍人带来1万利润,需要10年回本市盈率:10/1 = 10年化收益率:1/10 = 10%市净率PB市净率 = 市值/净资产净资产 = 总资产 - 负债举例:张三便利店,净资产:120万市值:1..._净资产收益率和股息率

墨器杯垫 文创商品设计特优_杯垫文创设计说明-程序员宅基地

文章浏览阅读737次。教育部昨举行「102年国立馆所文创商品设计比赛」颁奖典礼,台北科技大学创新设计研究所硕士生谢镇宇,为TW艺术教育馆设计「墨器」杯垫,取「默契」谐音,用5片压克力板,展现水墨画层层渲染效果,增加立体视觉感受,并在杯架后方加入LED光源,获评审肯定夺特优奖和奖金10万元。台南应用科技大学商品设计系学生高郁翔,为国立自然科学博物馆设计「恐龙化石钉书机」,他认为小朋友把钉书机钉下去的那一刻,会觉得像暴龙準_杯垫文创设计说明

C#中关于XML与对象,集合的相互转换-程序员宅基地

文章浏览阅读404次。XML与对象,集合的相互转化  今天小伙伴在群里问了一下关于XML与对象之间的相互转换,作为菜鸟的我正好趁着闲着的时间学习了一波,直接上代码了,有疑问或者有错误的地方还请大家指正,谢谢。。。。 1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System...._c# xml转集合