第三章 栈和队列
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>

浙公网安备 33010602011771号