栈的定义

栈是一种只能在一端进行插入或删除操作的线性表 表中允许进行插入、删除操作的一端称为栈顶(top)表的另一端称为栈底(bottom)。当栈中没有数据元素时称为空栈 栈的插入操作通常称为进栈入栈(push),栈的删除操作通常称为出栈退栈(pop)。

栈的顺序存储结构及其基本运算的实现

采用顺序结构存储的栈称为顺序栈

typedef struct {
    ElemType data[MaxSize];
    int top;
}SqStack;
  1. 初始化栈
void InitStack(SqStack *&s)
{
    s = (SqStack*)malloc(sizeof(SqStack));
    s->top = -1;
}
  1. 销毁栈
void DestroyStack(SqStack *&s)
{
    free(s);
}
  1. 判断栈是否为空
bool StackEmpty(SqStack *s)
{
    return(s->top == -1);
}
  1. 进栈
bool Push(SqStack*&s, ElemType e)
{
    if (s->top == MaxSize - 1)
        return false;
    s->top++;
    s->data[s->top] = e;
    return true;
}
  1. 出栈
bool Pop(SqStack*&s, ElemType &e)
{
    if (s->top == -1)
        return false;
    e = s->data[s->top];
    s->top--;
    return true;
}
  1. 取栈顶元素
bool GetTop(SqStack *s, ElemType &e)
{
    if (s->top == -1)
        return false;
    e = s->data[s->top];
    return true;
}
栈的链式存储结构及其基本运算的实现

采用链式存储结构的栈称为链栈

typedef struct linknode
{
    ElemType data;
    struct linknode *next;
}LinkStNode;
  1. 初始化栈
void InitStack(LinkStNode *&s)
{
    s = (LinkStNode*)malloc(sizeof(LinkStNode));
    s->next = NULL;
}
  1. 销毁栈
void DestroyStack(LinkStNode *&s)
{
    LinkStNode *pre = s,*p=s->next;
    while (p != NULL)
    {
        free(pre);
        pre = p;
        p = pre->next;
    }
    free(pre);
}
  1. 判断栈是否为空
bool StackEmpty(LinkStNode *s)
{
    return(s->next = NULL);
}
  1. 进栈
void Push(LinkStNode *&s, ElemType e)
{
    LinkStNode *p;
    p = (LinkStNode*)malloc(sizeof(LinkStNode));
    p->data = e;
    p->next = s->next;
    s->next = p;
}
  1. 出栈
bool Pop(LinkStNode *&s, ElemType &e)
{
    LinkStNode *p;
    if (s->next == NULL)
        return false;
    e = s->data;
    p = s->next;
    s->next = p->next;
    free(p);
    return true;
}
  1. 取栈顶元素
bool GetTop(LinkStNode *s, ElemType &e)
{
    if (s->next == NULL)
        return false;
    e = s->data;
    return true;
}
求解迷宫问题

问题描述

给定一个(M , N ) 的迷宫图,求一条从指定入口到出口的迷宫路径。假设一个迷宫图如下图所示,其中的每个方块用空白表示通道,用阴影表示障碍物。一般情况下,所求迷宫路径是简单路径,即在求得的迷宫路径上不会重复出现同一方块。一个迷宫图的迷宫路径可能有多条,这里仅仅考虑用栈求一条从指定入口到出口的迷宫路径。

数据组织

为了表示迷宫,设置一个数组mg,其中每个元素表示一个方块的状态,为0时表示对应的方块是通道,为1时表示对应方块时障碍物。为了运算方便,一般在迷宫的外围加一条围墙。

#define M 8
#define N 8
int mg[M+2][N+2]=
{
    {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}
};

在算法中用到的栈采用顺序栈存储结构

typedef struct{
    int i;//当前方块的行号
    int j;//当前方块的列号
    int di;//di是下一相邻可走方位的方位号
}Box;

typedef struct{
    Box data[MaxSize];
    int top;
}StType;

设计运算算法

  • 对于迷宫中的每个方块,有上、下、左、右 4 个方块相邻,规定上方方块为0,并按顺时针方向递增标号。
  • 求解迷宫问题采用的算法思想为“穷举法”。即从入口出发,按方位0到方位3的次序试探相邻的方块,一旦找到一个可走的相邻方块就继续走下去,并记下所走的方位。若没有相邻的可走方块,则沿原路退回到前一个方块。
  • 若一个非出口方块是可走的,将它进栈,如果找到某个方位 的相邻方块是可走的,则将栈顶方块的方位 di 置为d
  • 在算法上应保证试探的相邻可走方块不是已走路径上的方块,因此在一个方块进栈后将对应的 mg 数组元素值改为 -1 当退栈时将其恢复为 0
将入口(xi,yi)进栈(其初始方位设置为-1)
mg[xi][yi]=-1;
while(栈不为空)
{
    取栈顶方块(i,j,di);
    if((i,j)是出口(xi,yi))
    {
        输出栈中的全部方块构成一条迷宫路径
        return true;
    }
    查找(i,j,di)的下一个相邻可走方块;
    if(找到一个相邻可走方块)
    {
        该方块为(i1,j1),对应方位d;
        将栈顶方块的di设置为d;
        (i1,j1,-1)进栈;
        mg[i1][j1]=-1;
    }
    if(没有找到(i,j,di)的任何相邻可走方块)
    {
        将(i,j,di)出栈:
        mg[i][j]=0;
    }
}
return false; //没有找到迷宫路径
bool mgpath(int xi,int yi,int xe,int ye)
{
    Box path[MaxSize],e;
    int i,j,di,i1,j1,k;
    bool find;
    StType*st;
    InitStack(st);
    e.i=xi;e.j=yi;e.di=-1;
    Push(st,e);
    mg[xi][yi]=-1;
    while(!StackEmpty(st))
    {
        GetTop(st,e);
        i=e.i;j=e.j;di=e.di;
        if(i==xe&&j==ye)
        {
            printf("一条迷宫路径如下:\n");
            k=0;
            while(!StackEmpty(st))
            {
                Pop(st,e);
                path[k++]=e;
            }
            while(k>=1)
            {
                k--;
                printf("\t(%d,%d)",path[k].i,path[k].j);
                if((k+2)%5==0)
                    printf("\n");
            }
            DestroyStack(st);
            return true;
        }
        find=false;
        while(di<4&&!find)
        {
            di++;
            switch(di)
            {
                case 0: i1=i-1;j1=j;break;
                case 1: i1=i;j1=j+1;break;
                case 2: i1=i+1;j1=j;break;
                case 3: i1=i;j1=j-1;break;
            }
            if(mg[i1][j1]==0) find=true;
        }
        if(find)
        {
            st->data[st->top].di=di;
            e.i=i1;e.j=j1;e.di=-1;
            Push(st,e);
            mg[i1][j1]=-1;
        }
        else
        {
            Pop(st,e);
            mg[e.i][e.j]=0;
        }
    }
    DestroyStack(st);
    return false;
}

设计求解程序

int main(){
    if(!mppath(1,1,M,N))
        printf("该迷宫问题没有解");
    return 0;
}

问题进阶

在上述的过程中,我们只能找到一条路径。可是,往往我们需要找出最短路径,对于栈而言,我们可以通过找出所有的路径,从而确定最短的路径。由上述的分析可知,回溯的关键是 di 值要找出所有的路径,直接退栈即可,并将该方块对应的mg数组置为0,由于回溯过后的那个方块保存了信息就可以实现穷举出所有的路径。

bool mgpath(int xi, int yi, int xe, int ye)
{
    Box path[MaxSize], e;
    int i, j, di, i1, j1, k;
    int num=0;//用来判断是否有路径输出
    bool find;
    StType*st;
    InitStack(st);
    e.i = xi; e.j = yi; e.di = -1;
    Push(st, e);
    mg[xi][yi] = -1;
    while (!StackEmpty(st))
    {
        GetTop(st, e);
        i = e.i; j = e.j; di = e.di;
        if (i == xe && j == ye)
        {
            num++;
            printf("\n一条迷宫路径如下:\n");
            k = 0;
            int n = st->top;//由于要回溯需要栈的信息不能采用退栈的方式
            while (n!=-1)
            {
                e = st->data[n--];
                path[k++] = e;
            }
            while (k >= 1)
            {
                k--;
                printf("\t(%d,%d)", path[k].i, path[k].j);
                if ((k + 2) % 5 == 0)
                    printf("\n");
            }
            mg[i][j] = 0; //设置出口状态为0
            Pop(st, e); //回溯
            mg[e.i][e.j] = 0; //设置其状态为0
            i = e.i; j = e.j; di = e.di; //将i和j置为当前方块的值
        }
        find = false;
        while (di < 4 && !find)
        {
            di++;
            switch (di)
            {
            case 0: i1 = i - 1; j1 = j; break;
            case 1: i1 = i; j1 = j + 1; break;
            case 2: i1 = i + 1; j1 = j; break;
            case 3: i1 = i; j1 = j - 1; break;
            }
            if (mg[i1][j1] == 0) find = true;
        }
        if (find)
        {
            st->data[st->top].di = di;
            e.i = i1; e.j = j1; e.di = -1;
            Push(st, e);
            mg[i1][j1] = -1;
        }
        else
        {
            Pop(st, e);
            mg[e.i][e.j] = 0;
        }
    }
    DestroyStack(st);
    if(num>0) return true;
    return false;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容