第三章 栈和队列

3.1 栈和队列的定义和特点

3.1.1 栈的定义和特点

  栈 (stack)是限定仅在表尾进行插入或删除操作的线性表 因此, 对栈来说, 表尾端有其特殊含义, 称为栈顶 (top), 相应地, 表头端称为栈底 (bottom)。 不含元素的空表称为空栈。假设栈 S = (a1, a2, …,an), 则称 a1为栈底元素, an为栈顶元素。栈中元素按 a1, a2, ···, an的次序进栈, 退栈的第一个元素应为栈顶元素。 换句话说, 栈的修改是按后进先出的原则进行的, 如图 3.1 (a)所示。 因此, 栈又称为后进先出 (Last In First Out, LIFO)的线性表, 它的这个特点可用图 3.1(b) 所示的铁路调度站形象地表示。

 

 

在程序设计中,如果需要按照保存数据时相反的顺序来使用数据, 则可以利用栈来实现。 

 

 

3.1.2 队列的定义和特点

  和栈相反,队列(queue)是一种先进先出(First In First Out, FIFO)的线性表。它只允许在表的一端进行插入,而在另一端删除元素。这和日常生活中的排队是一致的,最早进入队列的元素最早离开。在队列中,允许插入的一端称为队尾(rear), 允许删除的一端则称为队头(front)。假设队列为q =(a1, a2, …,an), 那么, a,就是队头元素, an则是队尾元素。队列中的元素是按a1,a2,…,an的顺序进入的,退出队列也只按照这个次序依次退出,也就是说,只有在 a1,a2, …,an -I都离开队列之后, an才能退出队列。图3.2所示为队列的示意图。

 

 

  队列在程序设计中也经常出现。一个最典型的例子就是操作系统中的作业排队。在允许多道程序运行的计算机系统中,同时有几个作业运行。如果运行的结果都需要通过通道输出,那就要按请求输入的先后次序排队。每当通道传输完毕可以接受新的输出任务时,队头的作业先从队列中退出做输出操作。凡是申请输出的作业都从队尾进入队列。 
 

3.2 栈的表示和操作的实现

3.2.1 顺序栈的表示和实现

 

  顺序栈是指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针 top指示栈顶元素在顺序栈中的位置。通常习惯的做法是:以top=0表示空栈,鉴于 C 语言中数组的下标约定从 0 开始,则当以 C 语言作描述语言时,如此设定会带来很大不便,因此另设指针 base 指示栈底元素在顺序栈中的位置。当 top和 base 的值相等时,表示空栈。顺序栈的定义如下:
#define MAXSIZE 100//顺序栈初始空间分配量
typedef int SElemType;
typedef struct 
{
    SElemType *base;//栈底指针
    SElemType *top;//栈顶指针
    int stacksize;//顺序栈可用的最大容量
}SqStack;

Tip:①base 为栈底指针, 初始化完成后, 栈底指针 base 始终指向栈底的位置, 若base 的值为NULL, 则表明栈结构不存在。

   ②top 为栈顶指针, 其初值指向栈底。每当插入新的栈顶元素时, 指针 top 增1; 删除栈顶元素时, 指针 top 减1。

   ③栈空时, top 和 base 的值相等, 都指向栈底;栈非空时, top 始终指向栈顶元素的上一个。

   ④stacksize 指示栈可使用的最大容量, 后面的初始化操作为顺序栈动态分配MAXSIZE大小的数组空间, 将 stacksize 置为MAXSIZE。
 
图3.3所示为顺序栈中数据元素和栈指针之间的对应关系。

 

 

3.2.1.1 顺序栈的初始化

 算法步骤:

    ①为顺序栈预分配一定大小的空间,栈底base指向空间基地址。

    ②栈顶指针top赋初值为base,表示空栈。

    ③stacksize置为栈目前最大容量MAXSIZE。

//顺序栈的初始化
int InitStack(SqStack *S)
{
    (*S).base=(SElemType*)malloc(MAXSIZE);//以顺序栈的栈底指针作为所分配空间的基地址
    if(!(*S).base)
    {
        printf("\n分配空间失败!!!");
        return -1;
    }
    (*S).top=(*S).base;//栈底=栈顶,空栈
    (*S).stacksize=MAXSIZE;
    printf("\n分配空间成功!!!");
    return 0;
}

 

3.2.1.2 顺序栈的入栈

算法步骤:

    ①先判断顺序栈是否已满。

    ②如果顺序栈未满,将要插入的元素e赋值到栈顶top所指地址。

    ③栈顶指针top+1。

//入栈(栈顶插入)
int Push(SqStack *S,SElemType e)
{
    if((*S).top-(*S).base==(*S).stacksize)
    {
        printf("\n栈已满!!!");
        return -1;
    }
    *((*S).top++)=e;
    printf("\n元素%d入栈成功!!!",e);
    return 0;
}

 

3.2.1.3 顺序栈的出栈

算法步骤:

    ①先判断该顺序栈是否为空。

    ②若不为空,先将栈顶指针top-1。

    ③栈顶指针top此时指向原栈顶元素。

//出栈(栈顶删除)
int Pop(SqStack *S,SElemType *pe)
{
    if((*S).top==(*S).base)
    {
        printf("\n该栈为空栈!!!");\
        return -1;
    }
    pe=--(*S).top;//指针pe指向栈顶元素
    return 0;
}

 

3.2.1.4 顺序栈的取栈

算法步骤:

    ①先判断该顺序栈是否为空栈。

    ②若不为空,(top-1)地址的值即为栈顶元素。

//取顺序栈栈顶元素
SElemType GetTop(SqStack *S)
{
    if((*S).top==(*S).base)
    {
        printf("\n该栈为空栈!!!");
        return -1;
    }
    return *((*S).top-1);
}

 

3.2.1.5 顺序栈的打印

算法 步骤:

    ①用一个指针p指向base的地址。

    ②当p不等于top时,

      1.将p地址处的值输出。

      2.p++。

//打印栈元素
int PrintStack(SqStack *S)
{
    if((*S).top==(*S).base)
    {
        printf("\n该栈为空栈!!!");
        return -1;
    }
    int i=1;
    printf("\n打印栈(栈底开始)\n");
    SElemType *p;
    p=(*S).base;
    while(p!=(*S).top)
    {
        printf("%d(%d)->",*p,i);
        i++;
        p++;
    }
    return 0;
}

 

3.2.1.6 主函数实现

int main()
{
    SqStack Stack,*S;
    SElemType e,*pe;
    Stack.base=NULL;//栈底赋值为空,便于初始化中的判断
    S=&Stack;//指针S指向栈Stack
    InitStack(S);//顺序栈的初始化
    int n,i;
    printf("\n请输入要入栈的元素个数:");
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        printf("\n请输入入栈的第%d个元素",i);
        scanf("%d",&e);
        Push(S,e);
    }
    PrintStack(S);//打印栈
    Pop(S,pe);//栈顶出栈,删除
    printf("\n出栈的栈顶元素为%d",e);
    PrintStack(S);//打印栈
    printf("\n栈顶元素为%d",GetTop(S));
}

3.2.1.7 结果演示

 

 注意:代码连起来,即可运行(当然开头导入两个头文件)。

#include<stdio.h>
#include<stdlib.h>

 

posted @ 2021-09-02 15:27  暮色已现  阅读(324)  评论(0)    收藏  举报