第三章 栈和队列
栈(Stack)是限定只能在表的一端进行插入和删除操作的线性表。在表中,允许插入和删除的一端称作"栈顶(top)",不允许插入和删除的另一端称作"栈底(bottom)" 。
数据对象:D={ai|ai∈ElemSet, i=1,2,...,n, n≥0 }
数据关系:R1={ <ai-1, ai>|ai-1,ai∈D, i=2,...,n } 约定an端为栈顶,a1 端为栈底。
栈有两种存储表示,其顺序存储结构简称为顺序栈。
一、顺序栈类型的定义
// 结构定义:
typedefstruct {
ElemType*base; // 存储空间基址
inttop; // 栈顶指针
intstacksize; // 允许的最大存储空间以元素为单位
}Stack;
和顺序表类似,对顺序栈也需要事先为它分配一个可以容纳最多元素的存储空间,base 为这个存储空间的基地址,也即一维数组的地址。
从名称来讲,"栈顶指针"意为指示栈顶元素在栈中的位置,但它的值实际是栈中元素的个数,和顺序表中的 length 值的意义相同。
// 基本操作接口(函数声明):
void InitStack (Stack &S,int maxsize);
// 构造一个最大存储容量为 maxsize 的空栈S。
void DestroyStack (Stack &S);
// 销毁栈S,S 不再存在。
void ClearStack (Stack &S);
// 将 S 置为空栈。
bool StackEmpty (Stack S);
// 若栈 S 为空栈,则返回 TRUE,否则返回 FALSE。
intStackLength (Stack S);
// 返回S的元素个数,即栈的长度。
bool GetTop (Stack S, ElemType &e);
// 若栈不空,则用 e 返回S的栈顶元素,并返回TRUE;否则返回FALSE。
boolPush (Stack &S, ElemType e);
// 若栈的存储空间不满,则插入元素 e 为新的栈顶元素,并返回 TRUE;
// 否则返回FALSE。
bool Pop (Stack &S, ElemType &e);
// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回TRUE;否则返回FALSE。
voidStackTraverse(Stack S, void (*visit(ElemType ))
// 依次对S的每个元素调用函数visit( ),一旦 visit( )失败,则操作失败。
在此只给出其中4个函数的定义。
对顺序栈来说,空栈的初始化和顺序表的初始化完全相同。这不奇怪,因为它们的结构是一样的。也就是说,并非对栈而言取不到除栈顶之外的元素,而是对栈类型来说,不允许这种操作。
void InitStack (Stack &S,intmaxsize)
{
// 构造一个最大存储容量为 maxsize 的空栈 S
if (maxsize == 0)
maxsize = MAXLISTSIZE;
S.base = new SElemType[maxsize];
if (!S.base) exit(1); // 存储分配失败
S.stacksize = maxsize;
S.top = 0; // 空栈中元素个数为0
}
在此定义中,栈顶指针的值恰为当前栈中元素个数,栈顶指针的初值为0和栈中元素个数为0是同一个含义。
bool GetTop (StackS, ElemType &e)
{
// 若栈不空,则用 e 返回S的栈顶元素,并返回TRUE;否则返回FALSE
if (S.top == 0) return FALSE;
e = *(S.base + S.top-1); //返回非空栈中栈顶元素
return TRUE;
}
*(S.base+ S.top-1)是S.base[S.top-1] 的另一种写法,其实质相同。
bool Push (Stack&S, ElemType e)
{
// 若栈的存储空间不满,则插入元素 e 为新的栈顶元素,
// 并返回 TRUE;否则返回 FALSE
if (S.top == S.stacksize) //栈已满,无法进行插入
return FALSE;
*(S.base + S.top) = e; // 插入新的元素
++S.top; // 栈顶指针后移
return TRUE;
}
bool Pop (Stack&S, ElemType &e)
{
// 若栈不空,则删除S的栈顶元素,用 e 返回其值,
// 并返回 TRUE;否则返回 FALSE
if (S.top == 0) return FALSE;
e = *(S.base + S.top-1); //返回非空栈中栈顶元素
--S.top; // 栈顶指针前移
return TRUE;
}
1. 队列的基本概念
(1)队列是一种特殊的、只能在表的两端进行插入或删除操作的线性表。允许插入元素的一端称为队尾,允许删除元素的一端称为队首。
(2)队列的逻辑结构和线性表相同,其最大特点是“先进先出”。
(3)队列的存储结构有顺序队列和链队列之分,要求掌握队列的C语言描述方法。
(4)重点掌握在顺序队列和链队列上实现:进队、出队、读队头元素、显示队列元素、判队空和判队满等基本操作。
(5)熟悉队列在计算机软件设计中的典型应用,能灵活应用队列的基本原理解决一些实际应用问题。
2. 顺序队列(1)顺序队列用内存中一组连续的存储单元顺序存放队列中各元素,一般用一维数组作为队列的顺序存储空间。除了队列的数据以外,一般还设有队首和队尾两个指针。typedef struct{ datatype Q[MAXLEN]; int front=–1, rear=–1; // 定义队头、队尾指针,并置队列为空}Queue;
(2)顺序队列缺点是存在“假溢出”现象。
3.循环队列(1)为了解决顺序队列中的“假溢出”现象,把数组想象成一个首尾相连的环,即队首的元素Q[0]与队尾的元素Q[MAXLEN–1]连接起来,存储在其中的队列称为循环队列。
(2)一般规定:当front= =rear,表示循环队列为空;当front= =(rear+1)% MAXLEN,表示循环队列为满
(3)在定义结构体时,附设一个存储循环队列中元素个数的变量n,当n= =0时表示队空;当n= = MAXLEN时为队满。循环队列的结构体类型定义:typedef struct{ datatype data[MAXLEN]; int front,rear; int n; // 用以记录循环队列中元素的个数}csequeue; // 循环队列变量名
(4)入队操作时:p->rear= (p->rear+1) % MAXLEN;出队操作时:p->front= (p->front+1) % MAXLEN;
4. 链队列
(1)队列的链式存储结构称为链队列(或链队)。链首结点为队头,链尾结点为队尾。
(2)链队列的描述: typedef struct queuenode{ datatype data; struct queuenode *next;}queuenode; // 链队结点的类型datatypetypedef struct{ queuenode *front,*rear;}linkqueue; // 将头指针、尾指针封装在一起的链队如下图
(3)若队头指针为Q->front,队尾指针为Q->rear,则队头元素的引用为Q->front->data,队尾元素的引用为Q->rear->data。
(4)初始时置Q->front=Q->rear=NULL。
(5)入队操作,与链表中链尾插入操作一样;出队操作,与链表中链首删除操作一样。
(6)队空标志为Q->front==NULL;对于链队列而言,一般不会出现队满。
栈的应用举例
数制转换
括弧匹配检验
迷宫求解问题
表达式求值问题