栈和队列(持续更新中)


This article is mainly about my organization of the data structure. If you get any problems,you can ask me on GitHub.

1 栈

1.1 基本概念:

—栈是运算受限的线性表

—限制:插入和删除操作只能在同一端进行

—特点:后进先出(LIFO)或先进后出(FILO)

—栈是仅在表尾进行插入、删除操作的线性表。表尾称为栈顶(Top),表头称为栈底(Base)。

1.2 数学内容

若n个元素顺序入栈,则可能的出栈顺序为C(2n,n)/(n+1)=(2n)!/((n+1)!n!)

1.3 顺序表示

==*加代码==

1.4 链式表示

//测试样例是PPT中十进制转八进制  1348
#include<stdio.h>
#include<stdlib.h>
#define ok 1
#define overflow 0
#define error 0
typedef int status;
typedef int selemtype;
typedef struct snode{
    selemtype date;
    struct snode *next;
}lsnode,*slnode;

status InitStack(slnode *h)//初始化 
{
    *h=(slnode)malloc(sizeof(lsnode));
    if(!*h) exit(overflow);
    (*h)->next=0;
    return ok; 
}

status Push(slnode *h,selemtype e)//入栈 
{
    slnode r;
    r=(slnode)malloc(sizeof(lsnode));
    if(!r) exit(overflow);
    r->date=e;
    r->next=(*h)->next;
    (*h)->next=r;
    return ok;    
}

status Pop(slnode *h,selemtype *e)//出栈 
{
    *h=(*h)->next;
    *e=(*h)->date;
    return ok;
}

status GetTop(slnode h, selemtype *e)//读栈顶 
{
    if(h->next==0) return error;
    *e=h->next->date;
    return ok; 
}

status StackEmpty(slnode h)//判断栈空 
{
    if(h->next==0) return 1;
    else return 0;
}

void fun1(selemtype n)
{
    printf("%d",n);
}

status StackTravers(slnode h, void (*fun1)(selemtype n))//遍历栈 
{
    slnode r=h->next;
    while(r!=0)
    {
        (*fun1)(r->date);
        r=r->next;
    }
    return ok;
}

status DestroyStack(slnode *h)//销毁栈 
{
    slnode r;
    while(*h)
    {
        r=*h;
        *h=(*h)->next;
        free(r);
    }
    free(*h); 
    return ok;
}

status StackLength(slnode h,int *i)//求栈长度 
{
    (*i)=0;
    slnode r=h->next;
    while(r)
    {
        (*i)++;
        r=r->next;
    }
    return ok;
 } 

int main()
{
    int m,i=0;
    scanf("%d",&m);
    slnode h;
    selemtype e;
    
    InitStack(&h);//初始化栈 
    printf("验证初始化栈:%d\n",h);
    DestroyStack(&h);//销毁栈  
    printf("验证毁灭栈:%d\n",h);
    
    InitStack(&h);//初始化栈 
    while(m!=0)//入栈 
    {
        Push(&h,m%8);
        m=m/8;
    }
    
    if(!StackEmpty(h))//判断栈空 
    {
        printf("遍历栈:") ;
        StackTravers(h,fun1);//遍历栈
        printf("\n"); 
    
        GetTop(h,&e);//读栈顶
        printf("读栈顶:%d\n",e); 
        
        StackLength(h,&i);//求栈长度 
        printf("求栈长:%d\n",i);    
     } 
    
    while(1)//出栈 
    {
        if(!StackEmpty(h))
        {
            Pop(&h,&e);
            printf("出栈:%d ",e);
                
            StackLength(h,&i);//求栈长度 
            printf("目前栈长:%d\n",i);
        }
        else break;
     } 
    
    DestroyStack(&h);//销毁栈
    return 0;
}

1.5 应用

1.5.1 数制转换

例题:设计算法,将十进制整数转换为八进制,在函数中输入数据并输出结果。

见链表表示

1.5.2 括号匹配

例题:假设通过键盘输入的一个表达式中只出现()和[]并允许任意顺序的嵌套,设计算法,检测括号是否匹配。

==*加代码==

1.5.3 迷宫求解

迷宫

迷宫求解1

迷宫求解2

#include<stdio.h>
#include<stdlib.h>
#define ok 1
#define overflow 0
#define error 0
int maze[10][10]={
           {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 int status;
typedef struct{
    int x;
    int y;
}postype; 

typedef struct{
    int di;
    int step;
    postype    seat; 
}selemtype;



typedef struct snode{
    selemtype date;
    struct snode *next;
}lsnode,*slnode;

status InitStack(slnode *h)//初始化 
{
    *h=(slnode)malloc(sizeof(lsnode));
    if(!*h) exit(overflow);
    (*h)->next=0;
    return ok; 
}

status Push(slnode *h,selemtype e)//入栈 
{
    slnode r;
    r=(slnode)malloc(sizeof(lsnode));
    if(!r) exit(overflow);
    r->date=e;
    r->next=(*h)->next;
    (*h)->next=r;
    return ok;    
}

status Pop(slnode *h,selemtype *e)//出栈 
{
    *h=(*h)->next;
    *e=(*h)->date;
    return ok;
}

status GetTop(slnode h, selemtype *e)//读栈顶 
{
    if(h->next==0) return error;
    *e=h->next->date;
    return ok; 
}

status StackEmpty(slnode h)//判断栈空 
{
    if(h->next==0) return 1;
    else return 0;
}

void fun1(selemtype n)
{
    printf("%d",n);
}

status StackTravers(slnode h, void (*fun1)(selemtype n))//遍历栈 
{
    slnode r=h->next;
    while(r!=0)
    {
        (*fun1)(r->date);
        r=r->next;
    }
    return ok;
}

status DestroyStack(slnode *h)//销毁栈 
{
    slnode r;
    while(*h)
    {
        r=*h;
        *h=(*h)->next;
        free(r);
    }
    free(*h); 
    return ok;
}

status StackLength(slnode h,int *i)//求栈长度 
{
    (*i)=0;
    slnode r=h->next;
    while(r)
    {
        (*i)++;
        r=r->next;
    }
    return ok;
 } 

status pass(postype pos)
{
    if(maze[pos.x][pos.y]==0) return 1;
    else return 0;
 } 
 
postype nextpos(postype pos,int di)
 {
     switch(di)
     {
         case 1:pos.x++;break;
         case 2:pos.y++;break;
         case 3:pos.x--;break;
        case 4:pos.y--;break;
     }
     return pos;
 }
 

status mazepath(int maze[10][10],postype start,postype end)
{
    slnode h;
    InitStack(&h);//初始化栈
    selemtype e;
    postype curpos=start;
    int curstep=1;
    do
    {
        if(pass(curpos))
        {
            maze[curpos.x][curpos.y]=curstep;
            e.step=curstep;
            e.di=1;
            e.seat=curpos;
            Push(&h,e);
            if(curpos.x==end.x&&curpos.y==end.y) return 1;
            curpos=nextpos(curpos,1);
            curstep++;
        }
        else
        {
            if(!StackEmpty(h)) 
            {
                Pop(&h,&e);
                while(e.di==4&&!StackEmpty(h))
                {
                    maze[e.seat.x][e.seat.y]=-1;
                    Pop(&h,&e);
                }
                
                if(e.di<4)
                {
                    e.di++;
                    Push(&h,e);
                    curpos=nextpos(e.seat,e.di);
                }
            }
        }
    
    }while(!StackEmpty(h));
    return error;
}

int main()
{
    int i,j;
    postype start={1,1},end={8,8};
     for(i=0;i<10;i++)
   {
     for(j=0;j<10;j++)
       printf("%3d",maze[i][j]);
     printf("\n");
   }
   printf("\n");
   
   if(!mazepath(maze,start,end))
       printf("无解!\n");
       
   for(i=0;i<10;i++)
   {
     for(j=0;j<10;j++)
       printf("%3d",maze[i][j]);
     printf("\n");
   }
    return 0;    
}

2 队列

2.1 基本概念:

—队列是运算受限的线性表

—限制:一端插入,另一端删除

—特点:先进先出(FIFO)

—允许插入(入队)的一端称队尾、允许删除(出队)的一端称队头。

2.2 顺序表示

==*加代码==

2.3 链式表示

==*加代码==

2.4 应用——离散事件模拟

假设某银行有N个窗口对外接待客户,从银行开门起不断有客户进入银行,若有窗口空闲,则直接办理业务,反之,则排在人数最少的窗口后面。编写一个程序来模拟银行业务并计算客户在银行逗留的平均时间。

思路:1. 0分钟:银行开门,假定一客户到达。只要有客户到达,就产生两个随机数,分别表示该客户的业务办理时间和下一客户到达的时间间隔(例如随机数为8和5),并排在人数最少的队列中。2. 此时可确定,5分钟后将有一个客户到达事件发生,8分钟时将有一个客户离开事件发生。3. 5分钟:客户到达,产生两个随机数(例如25和4),该客户何时离开?若排在空窗口,30分钟时,否则当他为队头时计算。

事件链表:客户到达事件(0)和客户离开事件(i),按事件发生时间非递减有序;窗口队列:模拟排队效果,记录客户到达时间和业务办理时间

离散事件流程图

==*加代码==

3 习题

例题1:

假设一个栈的输入序列为1,2,3,若在入栈的过程中允许出栈,则可能得到的出栈序列是什么?

① 1入1出, 2入2出,3入3出, 即123;

② 1入1出, 2、3入,3、2出, 即132;

③ 1、2入,2出, 3入3出, 即231;

④ 1、2入,2、1出,3入3出, 即213;

⑤ 1、2、3入,3、2、1出, 即321;

例题2:

假设一个栈的输入序列是12345,若在入栈的过程中允许出栈,则栈的输出序列12345可能实现吗? 43512的输出呢?

12345 可能;43512 不可能

4 附录——ADT定义

1.栈

ADT Stack {
数据对象:D={ ai | ai ∈ElemSet, i=1,2,...,n, n≥0 }
数据关系:R1={ <ai-1, ai >| ai-1, ai∈D, i=2,...,n }
                      约定an 端为栈顶,a1 端为栈底。
基本操作:
InitStack(&S)
    操作结果:构造一个空栈S
DestroyStack(&S)
    初始条件:栈 S 已存在。
    操作结果:栈 S 被销毁。
ClearStack(&S)
    初始条件:栈 S 已存在。
    操作结果:将 S 清为空栈。
StackEmpty(s)
    初始条件:栈 S 已存在。
    操作结果:若栈 S 为空栈,返回 TRUE,否则返回 FALSE。
StackLength(S)
    初始条件:栈 S 已存在。
    操作结果:返回 S 的元素个数,即栈的长度
GetTop(S, &e)
    初始条件:栈 S 已存在且非空。
    操作结果:用 e 返回 S 的栈顶元素。
Push(&S, e)
    初始条件:栈 S 已存在。
    操作结果:插入元素 e 为新的栈顶元素。
Pop(&S, &e)
    初始条件:栈 S 已存在且非空。
    操作结果:删除栈顶元素,并用 e 返回其值。
StackTravers(S, visit())
    初始条件:栈 S 已存在且非空。
    操作结果:从栈底开始遍历栈。
} ADT Stack

2.队列

ADT Queue {
数据对象:D={ ai | ai ∈ElemSet, i=1,2,...,n, n≥0 }
数据关系:R1={ <ai-1, ai >| ai-1, ai∈D, i=2,...,n }
                      约定an 端为队尾,a1 端为队头。
基本操作:
InitQueue(&Q)
    操作结果:构造一个空队列Q。
DestroyQueue(&Q)
    初始条件:队列Q已存在。
    操作结果:队列Q被销毁, 不再存在。
ClearQueue(&Q)
    初始条件:队列Q已存在。
    操作结果:将Q清为空队列。
QueueEmpty(Q)
    初始条件:队列Q已存在。
    操作结果:若Q为空队列,则返回TRUE,否则返回FALSE。
QueueLength(Q)
    初始条件:队列Q已存在。
    操作结果:返回Q的元素个数,即队列的长度
GetHead(Q, &e)
    初始条件:Q为非空队列。
    操作结果:用e返回Q的队头元素。
EnQueue(&Q, e)
    初始条件:队列Q已存在。
    操作结果:插入元素e为Q的新的队尾元素。
DeQueue(&Q, &e)
    初始条件:Q为非空队列。
    操作结果:删除Q的队头元素,并用e返回其值
}ADT Queue

文章作者: 猴猴猴
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 猴猴猴 !
  目录