我们主要通过一个栈来实现一个迷宫,用压栈来记录路径
- 迷宫我们定义66大小的迷宫这个走法原则上是可以无限的,但是我们定义的我们必须需要考录这个我们用栈实现这个迷宫需要考虑的问题,首先来说栈是有大小的啊,所以在没有回溯的前提下,这个迷宫一直不走老路线也就是压栈,这个对于66迷宫来说绰绰有余了
- 我们定义的这个迷宫在计算x,y坐标时候的逻辑是x向下的,y向右的为正
- 时候越界以及我们怎么样才算是走到出口了,就要看我们是否走到定义出口的呢一行或或者一列了,我把出口定义最右边,那么如果在迷宫一个出口的前提下,碰到最后一列也就是肯定走出来了,即(X,6)
- 用二位数组定义迷宫以后,一个正常的迷宫是由区分障碍物的和什么不是障碍物,我们在计算他那条路径是最短路径时候,还在加一个区分条件,只要能区分原理其实都一样,拿什么区分也·都一样。
- 判断能不能走凭什么判断了,不能凭空判断的,可以定义两个数一个0,一个1,碰见1,就要寻找其他路径,0就表示没有障碍物了。
- 看一下代码吧
#pragma once
#include <stdio.h>
#include <string.h>
// 用全局变量,但不是最好的方式
#define ROWS (6)
#define COLS (6)
// 用来保存迷宫中的坐标
// 坐标方向和平时不太一样,x 朝下,y 朝右
typedef struct {
int x;
int y;
} Position;
// 栈代码
#include <assert.h>
typedef Position StackDataType;
#define MAX_SIZE (100)
typedef struct Stack {
StackDataType array[MAX_SIZE];
int top; // 表示当前个数
} Stack;
// 初始化/销毁
// 增(只能从栈顶)/删(只能删除栈顶)/查(只能查看栈顶元素)
// 个数 / 是否空 / 是否满
// 增 -> 顺序表的尾插
// 删 -> 顺序表的尾删
void StackInit(Stack *pStack)
{
pStack->top = 0;
}
void StackDestroy(Stack *pStack)
{
pStack->top = 0;
}
void StackPush(Stack *pStack, StackDataType data)
{
assert(pStack->top < MAX_SIZE);
pStack->array[pStack->top++] = data;
}
void StackPop(Stack *pStack)
{
assert(pStack->top > 0);
pStack->top--;
}
StackDataType StackTop(const Stack *pStack)
{
assert(pStack->top > 0);
return pStack->array[pStack->top - 1];
}
int StackSize(const Stack *pStack)
{
return pStack->top;
}
int StackFull(const Stack *pStack)
{
return pStack->top >= MAX_SIZE;
}
int StackEmpty(const Stack *pStack)
{
return pStack->top <= 0;
}
void StackCopy(Stack *pDest, Stack *pSrc)
{
memcpy(pDest->array, pSrc->array, sizeof(StackDataType)* pSrc->top);
pDest->top = pSrc->top;
}
// 栈代码结束
int gMaze[ROWS][COLS] = {
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 0 },
{ 0, 0, 1, 0, 1, 0 },
{ 0, 0, 1, 0, 1, 0 },
{ 0, 0, 1, 1, 1, 1 },
{ 0, 0, 1, 0, 0, 0 }
};
// 入口点
Position gEntry = { 5, 2 };
// 判断是否走到出口,最后一列都是出口
int IsExit(Position pos)
{
if (pos.y == COLS - 1) {
return 1;
}
else {
return 0;
}
}
// 判定是否可以走
// 没有越界 && 值是 1
int CanPass(Position pos)
{
if (pos.x >= ROWS) {
return 0;
}
if (pos.y >= COLS) {
return 0;
}
return gMaze[pos.x][pos.y] == 1;
}
void PrintPath(Stack *pStack)
{
Position at;
for (int i = 0; i < pStack->top; i++) {
at = pStack->array[i];
printf("x = %d, y = %d\n", at.x, at.y);
}
}
Stack path;
Stack min;
void PrintMaze()
{
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (gMaze[i][j] == 0) {
printf("█");
}
else if (gMaze[i][j] == 1) {
printf(" ");
}
else if (gMaze[i][j] == 2) {
printf("⊕");
}
}
printf("\n");
}
printf("\n\n");
}
void RunMazeRec(Position at)
{
Position next;
StackPush(&path, at);
// 一进来标记这个我走过了
gMaze[at.x][at.y] = 2;
PrintMaze();
// 为什么不需要在栈里记录,直接利用调用栈回溯
if (IsExit(at)) {
// 如果当前路径 (path)小于之前的最小路径 (min),则当前路径是最短
if (StackEmpty(&min) || StackSize(&path) < StackSize(&min)) {
StackCopy(&min, &path);
}
PrintPath(&path);
printf("============================\n");
//printf("找到出口: x = %d, y = %d\n", at.x, at.y);
// 重新置为 1
gMaze[at.x][at.y] = 1;
StackPop(&path);
return; // 会发生回溯
}
// 根据 左 -> 上 -> 右 -> 下 来尝试
next.x = at.x;
next.y = at.y - 1;
if (CanPass(next)) {
RunMazeRec(next);
PrintMaze();
}
next.x = at.x - 1;
next.y = at.y;
if (CanPass(next)) {
RunMazeRec(next);
PrintMaze();
}
next.x = at.x;
next.y = at.y + 1;
if (CanPass(next)) {
RunMazeRec(next);
PrintMaze();
}
next.x = at.x + 1;
next.y = at.y;
if (CanPass(next)) {
RunMazeRec(next);
PrintMaze();
}
// 重新置为 1
gMaze[at.x][at.y] = 1;
StackPop(&path);
return; // 回溯
}
void RunMaze()
{
// 需要一个栈,实现回溯
Stack stack;
StackInit(&stack);
Position at;
Position next;
at.x = gEntry.x;
at.y = gEntry.y;
while (1) {
// 标记当前位置我已经走过了
gMaze[at.x][at.y] = 2;
// 在栈里记录当前位置,为了以后回溯做准备
StackPush(&stack, at);
if (IsExit(at)) {
//printf("x = %d, y = %d\n", at.x, at.y);
PrintPath(&stack);
return;
}
// 根据 左 -> 上 -> 右 -> 下 来尝试
next.x = at.x;
next.y = at.y - 1;
if (CanPass(next)) {
at.x = next.x;
at.y = next.y;
continue;
}
next.x = at.x - 1;
next.y = at.y;
if (CanPass(next)) {
at.x = next.x;
at.y = next.y;
continue;
}
next.x = at.x;
next.y = at.y + 1;
if (CanPass(next)) {
at.x = next.x;
at.y = next.y;
continue;
}
next.x = at.x + 1;
next.y = at.y;
if (CanPass(next)) {
at.x = next.x;
at.y = next.y;
continue;
}
//回溯
StackPop(&stack); // 这里 pop 的是当前的 at
if (StackEmpty(&stack)) {
printf("没有出口\n");
return;
}
at = StackTop(&stack);
StackPop(&stack);
}
}
void TestRunMaze1()
{
StackInit(&path);
StackInit(&min);
RunMazeRec(gEntry);
printf("最短路径长度是 %d\n", StackSize(&min));
}
int main()
{
TestRunMaze1();
return 0;
}
看一下末尾结果,看一下我们的迷宫有没有按照正常迷宫的思维逻辑走的,遇到障碍物就不能走,遇到出口就出了,看一下最短路径,与我们思考的最短路径一样不一样
回溯过程和最短路径都没有什么问题,就这样了啊。。。。。。