本文参考博客园博客
https://www.cnblogs.com/cxyc/p/5322406.html
代码,加入了自己的理解,写出来希望对大家学习数据结构中的链栈有所帮助!
我在重新学习数据结构这本书(清华大学严纬敏老师)的时候对算法3.3迷宫求解产生了很大的兴趣,遂C语言代码实现。
1. 首先上结果:
(下一个问题:如何找到最近的路?)
思路:最开始入栈入口节点,然后入栈路径(显示为0)节点。如果一条路走到死路,出栈,直到回到先前的岔路口。
- 下面分块解析:
头文件:链栈节点结构体、链栈结构体
#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;
}
改进:找到入口-出口最近的路。
有思路的小伙伴欢迎在评论区讨论。