链栈求解迷宫从入口到出口的路径

本文参考博客园博客

https://www.cnblogs.com/cxyc/p/5322406.html

代码,加入了自己的理解,写出来希望对大家学习数据结构中的链栈有所帮助!

我在重新学习数据结构这本书(清华大学严纬敏老师)的时候对算法3.3迷宫求解产生了很大的兴趣,遂C语言代码实现。

1. 首先上结果:

在这里插入图片描述(下一个问题:如何找到最近的路?)

思路:最开始入栈入口节点,然后入栈路径(显示为0)节点。如果一条路走到死路,出栈,直到回到先前的岔路口。

  1. 下面分块解析:

头文件:链栈节点结构体、链栈结构体

#pragma once

/*小人在迷宫某位置可以向四周行动的描述 结构体*/
typedef struct  //某一方向为0才能向该方向移动,为1则不能
{
	int top;
	int bottom;
	int left;
	int right;
}direction;  //该结构体为direction型

/*当前小人所在迷宫的位置 结构体*/
typedef struct
{
	int x;
	int y;
}point;  //该结构体为point型

/*链栈节点 结构体*/
typedef struct
{
	direction direct;
	point position;
	struct StackNode* next;
}StackNode, * LinkStackPtr;  //命名为StackNode,指向该结构体的指针名为LinkStackPtr

/*链栈 及 计算链栈含有节点个数 结构体*/
typedef struct
{
	LinkStackPtr top;  //链栈头指针
	int count;  //该链栈拥有的节点个数
}LinkStack;

源文件1:节点、链栈数据结构及基本操作

#include<stdio.h>
#include<stdlib.h>
#include "定义.h"

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE  -1
#define OVERFLOW  -2

#define size 10

typedef int Status;
typedef int bool;



/*判断链栈是否为空(只有头节点)*/
bool isEmpty(LinkStack L)
{
	if (L.count == 1) return TRUE;
	return FALSE;
}

/*创建链栈 函数*/
Status createLink(LinkStack L)   //                                          这里可能有问题
{
	L.top = (LinkStackPtr)malloc(sizeof(StackNode));  //链栈头指针类型为栈节点,指针为指向这个栈节点的指针
	if (!L.top) exit(OVERFLOW);  //分配失败,没有多余的空间分配给链栈了
	L.top = NULL;  //链栈头指针初始化指向空
	L.count++;
	return OK;
}

/*取头节点  因为链栈入栈头插法,所以要讨论新插入的节点必须取头节点*/
LinkStackPtr GetTop(LinkStack L)  //LinkStackPtr已经是指针了,所以不用加*
{
	return L.top;  //返回的是个链栈型指针
}

/*入栈*/
Status Push(LinkStack* L, LinkStackPtr s)  //s是指向新增节点的指针
{
	s->next = (*L).top;
	(*L).top = s;
	(*L).count++;  //每插入一个,链栈长度加1
	return OK;
}

/*出栈*/
Status Pop(LinkStack* L)
{

	if (isEmpty(*L)) return ERROR;
	LinkStackPtr p;  //创建一个链栈节点型指针p
	p = (*L).top;
	(*L).top = (*L).top->next;
	free(p);
	(*L).count--;  //每弹出一个,链栈长度减1
	return OK;
}

源文件2:预定义及用数组实现的迷宫地图

#include<stdio.h>
#include<stdlib.h>
#include "定义.h"

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE  -1
#define OVERFLOW  -2

#define size 10

typedef int Status;
typedef int bool;

int enter_x, enter_y;  //入口坐标
int exit_x, exit_y;  //出口坐标

/*导入迷宫地图,1表示墙壁,0表示通路*/
int map[size][size] = {
	{1, 0, 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, 0},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};

/*找迷宫的入口和出口。迷宫入口只会存在于第一行,出口只会存在于最后一列*/
void findMap(int map[][size], int* enter_x, int* enter_y, int* exit_x, int* exit_y)  //当二维数组有初值表后,第一维大小就可以省略
{
	for (int i = 0; i < size; i++)
	{
		for (int j = 0; j < size; j++)
		{
			if (i == 0 && map[i][j] == 0)
			{
				*enter_x = i;
				*enter_y = j;
			}
			if (j == size - 1 && map[i][j] == 0)
			{
				*exit_x = i;
				*exit_y = j;
			}
		}
	}//遍历二维数组
	if (*enter_x == *exit_x && *enter_y == *exit_y)
	{
		printf("该迷宫出入口相同,无解。\n");
		system("pause");  //暂停一下程序,按任意键继续
		exit(ERROR);
	}
}  //

/*打印迷宫*/
void printMap(int a[][size])
{
	for (int i = 0; i < size; i++)
	{
		for (int j = 0; j < size; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}

/*判断这一个坐标是不是0,是,能走,否,不能走*/
bool judge(int x, int y)
{
	if (map[x][y] == 0) return TRUE;
	else return FALSE;
}

源文件3:主函数和实现

#include<stdio.h>
#include<stdlib.h>
#include "定义.h"

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE  -1
#define OVERFLOW  -2

#define size 10

typedef int Status;
typedef int bool;

extern map[size][size];

int main()
{
	printMap(map);
	bool flag = FALSE;  //一开始默认求解失败,成功找到通路了,改为TRUE

	/*初始化链栈并创建链栈结构体中的头指针和count*/
	LinkStack L;
	createLink(&L);  //要加&,表示直接对链栈地址所指的真实值进行操作

	/*找到入口和出口横纵坐标*/
	int enterx, entery, exitx, exity;
	enterx = entery = exitx = exity = 0;
	findMap(map, &enterx, &entery, &exitx, &exity);  //输出的是数组序号0开始

	/*创建链栈头节点并初始化,头节点存储*/
	LinkStackPtr p = (LinkStackPtr)malloc(sizeof(StackNode));  //p是个指针!!p很有用,一直都要用,所以用malloc

	p->position.x = enterx;
	p->position.y = entery;

	p->direct.top = 1;  //入口不能上移,因为在第一行
	p->direct.right = 0;
	p->direct.bottom = 0;
	p->direct.left = 0;

	map[p->position.x][p->position.y] = 2;  //把迷宫入口置2,2表示通路
	Push(&L, p);  //直接把L地址放进去处理,真实改变了L的值,L的值可以传到函数之外

	/*寻路*/
	while (!isEmpty(L))
	{
		LinkStackPtr q;  //q只是临时用一下,随当前坐标的变化,指向不同的当前坐标所属链栈节点
		q = GetTop(L);  //q指向当前坐标节点

		/*判断是否到达终点*/
		if (q->position.x == exitx && q->position.y == exity)
		{
			flag = TRUE;
			break;  //到达迷宫终点,跳出while循环
		}
		/*向上走*/
		if (q->direct.top == 0)  //如果该坐标显示其上方向可走
		{
			if (q->position.x - 1 >= 0)  //如果向上走还在迷宫范围之内
			{
				if (judge(q->position.x - 1, q->position.y))  //如果向上的坐标的数字是0,就是“通路”
				{
					q->direct.top = 1;  //当前坐标向上方向置为1,表示经由该坐标,向上的方向已经走过了

					/*已经向上走一步,把向上走到达的坐标 这一结构体,加入链栈L*/
					LinkStackPtr m = (LinkStackPtr)malloc(sizeof(StackNode));

					m->position.x = q->position.x - 1;  //初始化链栈节点结构体的position数据项,包括x,y坐标
					m->position.y = q->position.y;

					m->direct.top = 0;  //初始化链栈节点结构体的direct数据项,包括四方向可否向其移动
					m->direct.right = 0;
					m->direct.bottom = 1;  //从下面来的,所以下一跳不能回去
					m->direct.left = 0;

					map[m->position.x][m->position.y] = 2;  //将走到的坐标置2
					Push(&L, m);  //上方向坐标块入栈
				}
				else  //如果向上的坐标的数字是1,就是“墙”
				{
					q->direct.top = 1;   //告诉当前坐标,不能向上走
				}
			}
			else  //如果上面不在迷宫范围之内了
			{
				q->direct.top = 1;   //告诉当前坐标,不能向上走
			}
		}
		/*向右走*/
		else if(q->direct.right == 0)
		{
			if (q->position.y + 1 < size)  //如果向右走还在迷宫范围之内
			{
				if (judge(q->position.x, q->position.y + 1))  //如果向右的坐标的数字是0,就是“通路”
				{
					q->direct.right = 1;  //当前坐标向右方向置为1,表示经由该坐标,向右的方向已经走到过了

					/*已经向右走一步,把向右走到达的坐标 这一结构体,加入链栈L*/
					LinkStackPtr m = (LinkStackPtr)malloc(sizeof(StackNode));

					m->position.x = q->position.x;  //初始化链栈节点结构体的position数据项,包括x,y坐标
					m->position.y = q->position.y + 1;

					m->direct.top = 0;  //初始化链栈节点结构体的direct数据项,包括四方向可否向其移动
					m->direct.right = 0;
					m->direct.bottom = 0;  
					m->direct.left = 1;  //从左面来的,所以下一跳不能回去

					map[m->position.x][m->position.y] = 2;  //将走到的坐标置2
					Push(&L, m);  //右方向坐标块入栈
				}
				else  //如果向上的坐标的数字是1,就是“墙”
				{
					q->direct.right = 1;   //告诉当前坐标,不能向右走
				}
			}
			else  //如果上面不在迷宫范围之内了
			{
				q->direct.right = 1;   //告诉当前坐标,不能向右走
			}
		}
		/*向下走*/
		else if (q->direct.bottom == 0)
		{
			if (q->position.x + 1 < size)  //如果向下走还在迷宫范围之内
			{
				if (judge(q->position.x + 1, q->position.y))  //如果向下的坐标的数字是0,就是“通路”
				{
					q->direct.bottom = 1;  //当前坐标向下方向置为1,表示经由该坐标,向下的方向已经走到过了

					/*已经向下走一步,把向下走到达的坐标 这一结构体,加入链栈L*/
					LinkStackPtr m = (LinkStackPtr)malloc(sizeof(StackNode));

					m->position.x = q->position.x + 1;  //初始化链栈节点结构体的position数据项,包括x,y坐标
					m->position.y = q->position.y;

					m->direct.top = 1;  //初始化链栈节点结构体的direct数据项,包括四方向可否向其移动 从上面来的,所以下一跳不能回去
					m->direct.right = 0;
					m->direct.bottom = 0;
					m->direct.left = 0;  

					map[m->position.x][m->position.y] = 2;  //将走到的坐标置2
					Push(&L, m);  //下方向坐标块入栈
				}
				else  //如果向下的坐标的数字是1,就是“墙”
				{
					q->direct.bottom = 1;   //告诉当前坐标,不能向下走
				}
			}
			else  //如果下面不在迷宫范围之内了
			{
				q->direct.bottom = 1;   //告诉当前坐标,不能向下走
			}
		}
		/*向左走*/
		else if (q->direct.left == 0)
		{
			if (q->position.y - 1 >= 0)  //如果向左走还在迷宫范围之内
			{
				if (judge(q->position.x, q->position.y - 1))  //如果向左的坐标的数字是0,就是“通路”
				{
					q->direct.left = 1;  //当前坐标向左方向置为1,表示经由该坐标,向左的方向已经走到过了

					/*已经向左走一步,把向左走到达的坐标 这一结构体,加入链栈L*/
					LinkStackPtr m = (LinkStackPtr)malloc(sizeof(StackNode));

					m->position.x = q->position.x;  //初始化链栈节点结构体的position数据项,包括x,y坐标
					m->position.y = q->position.y - 1;

					m->direct.top = 0;  //初始化链栈节点结构体的direct数据项,包括四方向可否向其移动 
					m->direct.right = 1;  //从右面来的,所以下一跳不能回去
					m->direct.bottom = 0;
					m->direct.left = 0;

					map[m->position.x][m->position.y] = 2;  //将走到的坐标置2
					Push(&L, m);  //左方向坐标块入栈
				}
				else  //如果向左的坐标的数字是1,就是“墙”
				{
					q->direct.left = 1;   //告诉当前坐标,不能向左走
				}
			}
			else  //如果左面不在迷宫范围之内了
			{
				q->direct.left = 1;   //告诉当前坐标,不能向左走
			}
		}
		/*四个方向都不能走*/
		else if (q->direct.top == 1 && q->direct.right == 1 && q->direct.bottom == 1 && q->direct.left == 1)
		{
			map[q->position.x][q->position.y] = 0;  //与51行获得q相对应。q此时已经被设为2了,现在要退回去,重新设置为0
			Pop(&L);
        }
	}

	/*判断是否找到了路径*/
	if (flag == TRUE)
	{
		printf("成功找到路径。0:未走路径;1墙壁;2:路径\n");
		printMap(map);
	}
	else printf("未找到路径");

	return 0;
}

改进:找到入口-出口最近的路。

有思路的小伙伴欢迎在评论区讨论。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值