zoukankan      html  css  js  c++  java
  • 栈的简单应用-迷宫问题

                                                  迷宫问题

      迷宫问题一直是计算机工作者感兴趣的问题,因为它可以展现栈的巧妙应用,

    这里将利用栈开发一个走迷宫程序,虽然在发现正确路径前,程序要尝试许多

    错误路径,但是,一旦发现,就能够重新走出迷宫,而不会再去尝试任何错误路径。

                                                    迷宫问题求解

           计算机中可以用如图所示的方块图表示迷宫。图中空白方块为通道,蓝色方块为墙

      

           迷宫的储存可以使用二维数组,其中“0”代表墙值,“1”代表通路。由于迷宫被表示为

    二维数组,所以,在任何时刻,迷宫中的位置都可以用行,列坐标来描述。

           在迷宫中的某一个位置进行移动的可能方向如图所示

      

           值得注意的是,并不是每一个位置都有四个邻居。如果[row][col]在边界上那么邻居的

    个数就少于4个,甚至只有2个。为了避免边界条件的检查,在迷宫周围加上一圈边界。这样,

    一个m*n的迷宫就需要一个(m+2)*(n+2)的数组。入口位置在[1][1],而出口位置在[m][n]。

           另一个简化问题的策略是,用数值direc 预先定义出“可能的移动方向”数字0-3表示4个

    可能的移动方向,对每个方向都指出其垂直和水平的偏移量。

         

     

      求迷宫中一条径的算法的基本思想是:

    若当前位置“可通“,则纳入”当前路径”,并继续朝“下一个位置探索”,即切换“下一位置”为

    “当前位置”,如此反复直到出口;

    若当前位置“不可通”则应顺着“来向”退回到“前一通道块”,然后朝着除“来向”之外的其他方向继续探索;

    若该通道块的四周4个方块均“不可通”,则应从“当前路径”上删除该通道块。假设以栈

    S记录“当前路径”,则栈顶中存放的是“当前路径上最后一个通道块”。由此,

    “纳入路径”的操作即为“当前位置压入”,“从当前路径上删除前一块通道块”的操作即为“弹出”。

    需要说明的是,所谓的当前位置可通,指的是未曾走到过的通道块,即要求该方块位置S记录“当前路径”,

    则栈顶中存放的是“当前路径上最后一个通道块”。由此,“纳入路径”的操作即为“当前位置压入”,

    “从当前路径上删除前一块通道块”的操作即为“弹出”。不仅是通道块,而且不在当前路径上(否则路径不是简单路径),

    也不是曾经纳入过路径的通道块(否则只能在死胡同内转圈)。

     sqstack.h  

     1 #pragma once
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #define STACK_INIT_SIZE 100//储存空间初始分配量
     5 #define STACKINCREMENT  10//存储空间分配增量
     6 #define OK 1
     7 #define ERROR 0
     8 typedef struct {
     9     int x;  //行值
    10     int y;  //列值
    11 }PosType;  //迷宫坐标位置类型
    12 typedef struct 
    13 {
    14     int ord;      //序号
    15     int di;       //方向
    16     PosType seat; //位置  
    17 
    18 } StackType; //栈元素类型
    19 
    20 typedef struct {
    21     StackType *base;   //在构造之前和销毁之后,base的值为NULL
    22     StackType *top;    //栈顶指针
    23     int stacksize;     //当前已分配的存储空间,以元素为单位
    24     
    25 }SqStack; //顺序栈
    26 
    27 //栈的初始化
    28 int InitStack(SqStack *p) {
    29 
    30 
    31     p->base = (StackType*)malloc(STACK_INIT_SIZE * sizeof(StackType));
    32     if (p->base == NULL)  return ERROR;  //内存分配失败
    33     p->top = p->base;     //栈顶与栈底相同表示一个空栈
    34     p->stacksize = STACK_INIT_SIZE;
    35     return OK;
    36 
    37 }
    38 //判断栈是否为空
    39 int EmptyStack(SqStack *p) {
    40     //若为空栈 则返回OK,否则返回ERROR
    41     if (p->top == p->base) return OK;
    42     else return ERROR;
    43 }
    44 //顺序栈的压入
    45 int Push(SqStack *p, StackType e) {
    46     //插入元素e为新的栈顶元素
    47     if ((p->top - p->base) >= p->stacksize)   //栈满,追加储存空间
    48     {
    49         p->base = (StackType*)realloc(p->base, (p->stacksize + STACKINCREMENT) * sizeof(StackType));
    50         if (p->base == NULL)   return ERROR;// 储存空间分配失败
    51         p->top = p->base + p->stacksize;   
    52         p->stacksize += STACKINCREMENT;
    53     }
    54     *(p->top) = e;
    55     (p->top)++;
    56     return OK;
    57 }
    58 // 顺序栈的弹出     
    59 int Pop(SqStack *p, StackType *e) {
    60     //若栈不空,则删除p的栈顶元素,用e返回其值
    61     if (p->top == p->base) return ERROR;
    62     --(p->top);
    63     *e = *(p->top);
    64     return OK;
    65 
    66 
    67 }
    68 //将顺序栈置空 栈还是存在的,栈中的元素也存在,
    69 //如果有栈中元素的地址任然能调用
    70 int ClearStack(SqStack *p) {
    71     p->top = p->base;
    72     return OK;
    73 }
    74 //顺序栈的销毁
    75 int DestroyStack(SqStack *p) {
    76     //释放栈底空间并置空
    77     free(p->base);
    78     p->base = NULL;
    79     p->top = NULL;
    80     p->stacksize = 0;
    81 
    82     return OK;
    83 }

      源代码:

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include "sqstack.h" //引入顺序栈储存结构及其基本操作
      4 #define MAXLENGTH 25  //设迷宫最大行列为25
      5 #define ERROR 0
      6 #define OK 1
      7 #define TRUE 1
      8 #define FALSE 0
      9 typedef int Status;
     10 typedef int MazeType[MAXLENGTH][MAXLENGTH]; //迷宫数组[行][列]
     11 PosType begin, end;   //迷宫入口坐标,出口坐标
     12 PosType direc[4] = { {0,1},{1,0},{0,-1},{-1,0} }; //{行增量,列增量},移动方向依次为东南西北
     13 MazeType maze; //迷宫数组
     14 int x, y;//迷宫的行,列
     15 int curstep = 1;  //当前足迹,初值在入口处为1
     16 struct SElemType
     17 {
     18     int ord;  //通道块在路径上的“序号”
     19     PosType seat;   //通道块在迷宫中的“坐标位置”
     20     int di;  //从此通道块走向下一通道块的“方向”(0-3表示东-北)
     21 };
     22 void Print() {
     23     //输出迷宫结构
     24     int i, j;
     25     for (i = 0; i < x; i++)
     26     {
     27         for (j = 0; j < y; j++)
     28             printf("%3d", maze[i][j]);
     29         printf("
    ");
     30     }
     31 }
     32 void Init()
     33 {
     34     //设定迷宫布局(墙值为0,通道值为1)
     35     //全局变量maze 未初始化 所以均为值均为0
     36     int i, j, x1, y1;
     37     printf("请输入迷宫的行数,列数(包括外墙):");
     38     scanf("%d%d", &x, &y);
     39     for (i = 1; i < x - 1; i++)
     40         for (j = 1; j < y - 1; j++)
     41             maze[i][j] = 1;  //定义通道初值为1
     42     printf("请输入迷宫内墙单元数:");
     43     scanf("%d", &j);
     44     printf("请依次输入迷宫内墙每个单元的行数,列数:
    ");
     45     //FILE *fp; 注释掉的这五行之前是方便我调试的
     46     //fp = fopen("maze.txt", "r");
     47     //int *px1 = &x1, *py1 = &y1;
     48     for(i=1;i<=j;i++)
     49     {
     50         //fscanf(fp, "%d%d", px1, py1);
     51         //maze[x1][y1] = 0;
     52         scanf("%d%d", &x1, &y1);
     53         maze[x1][y1] = 0; //定义墙值为0
     54     }
     55     printf("迷宫结构如下:
    ");
     56     Print();
     57     printf("请输入入口的行数,列数:");
     58     scanf("%d%d", &begin.x, &begin.y);
     59     printf("请输入出口的行数,列数:");
     60     scanf("%d%d", &end.x, &end.y);
     61 }
     62 void MarkPrint(PosType seat)
     63 {//标记迷宫块不可通过
     64     maze[seat.x][seat.y] = -1;
     65     
     66 
     67 }
     68 Status Pass(PosType b)
     69 {
     70     //当迷宫m的b点的值为1,return OK;
     71     //否则return ERROR;
     72     if (maze[b.x][b.y] == 1)
     73         return OK;
     74     else
     75         return ERROR;
     76 }
     77 void FootPrint(PosType b)
     78 {
     79 //使迷宫m的b点的值变为足迹(curstep),表示经过
     80     maze[b.x][b.y] = curstep;
     81 
     82 }
     83 PosType NextPos(PosType  b,int di)
     84 {
     85     //根据当前位置b及其移动方向di,修改b为下一位置
     86     b.x += direc[di].x;
     87     b.y += direc[di].y;
     88     return b;
     89 }
     90 Status MazePath(PosType start, PosType end)
     91 {
     92     //若迷宫m中存在从入口start到出口end的通道
     93     //则求得一条存放在栈中,并返回TRUE;否则返回FAlSE
     94     SqStack s,*S; //顺序栈
     95     S = &s;
     96     PosType curpos;
     97     StackType e,*pe;
     98     pe = &e;
     99     InitStack(S);
    100     curpos = start;
    101     do
    102     {
    103         if (Pass(curpos)) //当前可以通过,则是未曾走到的通道块
    104         {
    105             FootPrint(curpos); //留下足迹
    106             e.ord = curstep;  //栈元素序号为当前序号
    107             e.seat = curpos;  //栈元素位置为当前位置
    108             e.di = 0;  //从当前位置出发,下一位置为东
    109             Push(S, e); //入栈当前位置及其状态
    110             curstep++;   //足迹加1
    111             if (curpos.x == end.x&&curpos.y == end.y)  //到达出口
    112                 return TRUE;
    113             curpos = NextPos(curpos, e.di); //由当前位置及移动方向确定下一个当前位置
    114         }
    115         else //当前位置不能通过
    116             if (!EmptyStack(S))  //栈不为空
    117             {
    118                 Pop(S, pe); // 退栈到前一位置
    119                 curstep--;  //足迹减1
    120                 while (e.di == 3 && !EmptyStack(S))  //前一位置处于最后一个方向(北)
    121                 {
    122                     MarkPrint(e.seat); //留下不能通过的标记(-1)
    123                     Pop(S, pe); //退一步
    124                     curstep--; //足迹再减1
    125 
    126                 }
    127                 if (e.di < 3)  //没有到最后一个方向(北)
    128                 {
    129                     e.di++;  //换下一个方向探索
    130                     Push(S, e); //入栈该位置的下一个方向
    131                     curstep++;// 足迹加1
    132                     curpos = NextPos(e.seat, e.di);
    133 
    134                 }
    135 
    136             }
    137 
    138     }
    139     while (!EmptyStack(S));
    140     return FALSE;
    141     
    142     
    143 }
    144 int main() 
    145 {
    146     Init();  //初始化迷宫
    147     if (MazePath(begin, end)) //有通路
    148     {
    149         printf("从此迷宫入口到出口的一条路径如下:
    ");
    150         Print();
    151     }
    152     else
    153         printf("此迷宫没有从入口到出口的路径
    ");
    154     return 0;
    155 
    156     
    157 }
    158         

        测试如下:

      需要注意的是得到的路径并不是最短路径。要求最短路径应该把所有

    路径求出,再比较栈的大小 。最小长度的栈则保存着最短的路径。

  • 相关阅读:
    Linux的命令2
    运维书
    管理MariaDB
    MariaDB基础命令
    Linux创建桥接网络
    聚合网络
    kickstart
    VLAN原理
    进程优先和ACL
    计划任务at、crontab
  • 原文地址:https://www.cnblogs.com/mwq1024/p/10293702.html
Copyright © 2011-2022 走看看