数据结构与算法分析~笔记3栈和队列

从数据结构角度看,栈和队列属于特殊的线性表,它们在逻辑结构上和线性表相同。栈和队列在操作上相比一般的线性表多一些限制,其中栈只能在表的一端进行操作,而队列则是只能在一端进行插入,在另一端进行删除。

3.1 栈的基本概念

(Stack)是限定仅在表尾进行插入或删除的线性表。允许插入和删除的一端叫做栈顶(Top),另一端叫做栈底(Bottom)。当栈中没有任何元素时则称为空栈。将一个元素从栈顶插入栈的操作称为进栈入栈(Push),而从栈顶删除一个元素的操作称为出栈弹出(Pop)。
由于元素只能从顶部插入和删除,因此先被插入的元素,只能在比它后放入的元素全部取出后才能进行删除。故栈具有“先入后出(FILO)”或“后入先出(LIFO)”的特点。
栈的示意图如下,其中a1,a2,…,an为数据元素:
在这里插入图片描述
栈的基本操作除了在栈顶进行插入或删除外,还有栈的初始化、判空、销毁及取栈顶元素等。

3.2 栈的顺序存储结构及实现

栈是线性表的特例,因此线性表的存储结构对栈也适用,即栈也有两种存储方法。
采用顺序存储结构的栈称为顺序栈(Sequential Stack),即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,因为栈底位置是固定不变的,所以可以将栈底位置设置在数组的两端的任何一个端点;栈顶位置是随着进栈和退栈操作而变化的,故需用一个栈顶指针top来指示栈顶,栈底指针base来指示栈底。
顺序栈本质上是顺序表的简化,即利用一组地址连续的存储单元一次存放自栈底到栈顶的数据元素,同时附设top指针指示栈顶元素在顺序栈中的位置。
栈的初始化操作为:首先按照初始设定值进行存储分配,在顺序栈中,栈底指针base始终指向栈底的位置,所以若base=NULL,则表明栈结构不存在。序栈的基本结构及其操作示意图如下图:
在这里插入图片描述
(1)栈空时,栈顶指针top=base,如图(a)所示;
(2)入栈时,栈顶指针top=top+1,如图(b)所示;
(3)出栈时,栈顶指针top=top-1,如图(c)所示;
(4)栈满时,栈顶指针top=StackSize-1,如图(d)所示。
根据顺序栈的操作定义,很容易写出实现顺序栈基本操作的算法。

  1. 顺序栈入栈算法
    顺序栈入栈算法的操作步骤如下。
    Step1:如果栈满,则追加存储空间;否则直接执行Step2。
    Step2:将新元素插入栈顶位置。
    Step3:栈顶指针增加1。
  2. 顺序栈出栈算法
    顺序栈出栈算法的操作步骤如下。
    Step1:如果栈空,则返回错误信息,操作结束;否则执行Step2。
    Step2:取出栈顶元素赋值给e。
    Step3:栈顶指针减1,并返回e。

3.3 栈的链式存储结构及实现

采用链式存储结构的栈称为链栈(Linked Stack)。通常链栈采用单链表表示,链栈的插入和删除操作只能在表头进行。
链栈的结点结构与单链表的结点结构相同。链表只能在栈顶执行插入和删除操作,因此以单链表的头部做栈顶是最方便的,而且也没有必要为单链表附加头结点链表的头指针即为栈顶指针。
如图(a)所示便是链栈结构及其操作示意图。
在这里插入图片描述
(1)入栈时,将新创建的结点s加入到链表表头,并将栈顶指针top指向s,如图(b)所示。
(2)出栈时,栈顶指针top指向链表第一个结点的下一个结点,如图(c)所示。
链栈基本操作的实现本质上是单链表基本操作的简化。销毁链栈的主要工作是释放链栈所占的存储空间,插入和删除操作只需处理栈顶位置的情况。

  1. 链栈销毁算法
    销毁链栈可从栈顶开始依次释放链栈中的数据元素结点,销毁工作在析构函数中实现。
  2. 链栈入栈算法
    链栈入栈即在链表表头插入新结点,且栈顶指针指向该结点,具体操作步骤如下。
    Step1:创建一个新结点s,将s的值设为入栈元素值。
    Step2:将新结点s插入表头。
    Step3:栈顶指针指向s。
  3. 链栈出栈算法
    链栈出栈即删除链表的首元素结点,具体操作步骤如下。
    Step1:如果栈空,则返回错误信息,操作结束;否则继续执行Step2。
    Step2:取出栈顶元素赋值给e。
    Step3:栈顶指针后移一位,并删除原栈顶结点,返回e。

3.4 队列的基本概念

队列(Queue)也是线性表的特例。它将元素排列成队,有入口(队尾)和出口(队头),数据元素只能从队尾入队,从队头离队。所以,队列具有先进先出(First In First Out,FIFO)或后进后出(Last In Last Out,LILO)的特点。
队列(Queue)是另一种限定存取位置的线性表。它只允许在表的一端插入,在另一端删除,其中允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)。从队尾插入元素的操作称为入队;从队头删除元素的操作称为出队。队列的示意图如下图所示:
在这里插入图片描述

3.5 队列的顺序存储

队列的顺序存储结构称为顺序队列(Sequential Queue)。在顺序队列中,需要用一组地址连续的存储单元依次存放从队头到队尾的元素,由于队列的队头和队尾的位置是变化的,因而还需要两个指针front和rear作为队头指针和队尾指针来分别指示队头和队尾元素在队列中的位置。
(1)初始化队列时rear=front=0;
(2)元素入队时,如果队列未满则将入队元素放入rear所指向的存储单元,并令rear=rear+1;
(3)元素出队时,删除所指元素,如果队列不为空则返回front所指向的存储单元的元素,并令front=front+1。
(4)队列为空时,头尾指针相等。
在非空队列里,头指针始终指向队头,而尾指针始终指向队尾元素的下一个位置。顺序队列结构及其操作如图所示:
在这里插入图片描述
然而如果仅仅简单地将顺序队列如此定义,则会出现“假溢”现象,如图(d)所示,当rear大于等于容量时,新元素将无法入队,但事实上队列的低端仍有空闲的存储单元,这种现象称为“假溢”。为充分利用存储空间,对队列的存储方式进行了一定的改进解决“假溢”现象,由此产生了循环队列
解决“假溢”现象的方法是将存储队列的数组看成是头尾相接的圆环,并成为循环存储空间,即允许队列直接从数组中下标最大的位置延续到下标最小的位置,这个操作可以通过取模运算实现。队列的这种头尾相接的顺序存储结构称为循环队列(Circular Queue)。
在循环队列中,数据元素入队时尾指针向前追赶头指针,出队时头指针向前追赶尾指针,故队空和队满时头尾指针均相等。因此,无法通过front==rear来判断队列“空”还是“满”。
已知解决此问题的方法有以下3种:
(1)设一个布尔变量以区分队列的空和满,令布尔变量值为0则队空,值为1则队满;
(2)少用一个元素的空间:约定入队前,测试尾指针在循环意义下加1后是否等于头指针,若相等则认为队满;(但此时实际还有一个空位置)
(3)使用一个计数器记录队列中元素的总数,即队列长度。

  1. 循环队列入队算法
    循环队列入队的操作步骤如下。
    Step1:若队列满,无法入队,则返回错误信息,操作结束;否则执行Step2。
    Step2:将新结点插入队尾。
    Step3:队尾指针后移一位。
  2. 循环队列出队算法
    循环队列出队的操作步骤如下。
    Step1:若队列空,则无法出队,返回错误信息,操作结束;否则执行Step2。
    Step2:取出队头元素的值保存在e中。
    Step3:将队头指针后移一位,并返回e。
  3. 循环队列读取元素算法
    读取队头元素与出队操作类似,惟一的区别是不改变队头指针。

3.6 队列的链式存储

队列的链式存储结构称为链队列(Linked Queue)。根据队列先进先出的特性,链队列是仅在表头删除元素和表尾插入元素的单链表。为了操作方便,链队列用有头结点的链表表示,并设置队头指针指向链队列的头结点,队尾指针指向终端结点,如图(a)所示。链队列加上头结点,能够使空队列和非空队列的操作一致,链队列的具体判定和操作如下。
(1)当链队列为空时,头指针和尾指针均指向头结点,如图(b)所示。
(2)一个结点s入队时,将s插入到链表表尾,并将rear指针指向s,如图(c)所示。
(3)一个结点出队时,如果该链队列只有一个结点,则将头结点的next指针置为NULL,并将rear指针指向头结点,此时链队列为一个空队列;否则将头结点的next指向第一个结点的后一个结点s,如图(d)所示。
在这里插入图片描述

  1. 链队列入队算法
    链队列入队即在队尾插入一个元素,修改队尾,操作步骤如下。
    Step1:创建一个入队结点s,将s的值设为入队元素的值。
    Step2:将结点s加入链表表尾。
    Step3:将队尾指针指向s。
  2. 链队列出队算法
    链队列出队即从链队列队头删除一个元素并调整队头指针,需要注意在队列长度为1时的特殊处理。操作步骤如下。
    Step1:若队列空,则无法出队,返回错误信息,操作结束;若队列长度为1,则将队尾指针指向头结点,操作结束;否则执行Step2。
    Step2:取出队头元素的值保存在e中。
    Step3:将队头元素所在的结点从链队列中移除。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值