栈和队列

物理结构和逻辑结构

物理结构:数组和链表在内存中都有实实在在的内存空间,这就是物理结构

逻辑结构:逻辑结构是抽象的概念,它依赖于物理结构而实现

 

栈stack

栈是什么:

栈是一种线性数据结构,栈中的元素只能先入后出(first in last out),即最早进的,最后一个出。

形象的类比:向玻璃杯中放乒乓球,玻璃杯不是很宽,直径等同于乒乓球的直径,取出来的时候只能从杯口取出

栈的基本操作

入栈(push),把新元素放到栈中,只允许从栈顶放入元素,新的元素将成为新的栈顶

出栈(pop),只有栈顶的元素才允许出栈,出栈元素的前一个会成为新的栈顶

Python中的实现

栈的实现,可以使用链表,也可以使用数组,关键的是逻辑

对于数组:Python中对应的是list,而且list已经很好的实现了栈的功能:append(),在最后加入元素,相当于入栈;pop(),弹出最后一个元素,相当于出栈。栈顶就是最后一个元素,栈尾就是首元素

对于链表:上一篇中我们实现了链表的插入和删除,如果使用链表实现栈的操作,意味着我们只能使用尾部插入和尾部删除或者首部插入和首部删除。

代码的实现,我们可以新建一个栈类(继承链表),增加入栈,出栈的方法,也可以再类中实例化(而不是继承)一个链表,然后编写入栈和出栈的操作

 

队列

队列是什么:

队列是一种线性数据结构,特征和行驶的车辆进入单行隧道很像,队列中的元素只能先入先出(first in first out,FIFO)队列中的出口端叫做队头(front),队列的入口端叫做队尾(rear)

队列的基本操作

入队

入队把一个新元素放到队尾,且只允许在队尾的位置放入新元素

出队

从队头取出一个元素,且只允许从队头取出元素

循环队列

什么是循环队列

假定我们使用数组实现对列(数组长度为7)

【0】【1】【9】【4】【5】【7】【】

队头              队尾

现在开始出队

【 】【 】【 】【 4】【5】【7】【】

            *队头*         *队尾*

我们发现队头的位置发生了变化

现在开始入队,但是我们发现,队列已经满了,

【 】【 】【 】【4】【5】【7】【】 <--- 2

       队头        队尾

但是如何分配空间呢,我们知道入队,只能从队尾进入。数组已经到头了,但是经过出队操作,数组并没有满,前面还有空间

我们知道数组是分配了固定且连续空间的数据结构,前面三个空白区间如果出队之后就意味着浪费,能不能把前面的利用起来,就是循环数组的作用了

让我们重新看待上次的入队

【 】【 】【 】【4】【5】【7】【2】

队尾      队头

队尾始终指向空白的地方

特别的,我们发现队尾竟然能够在物理意义上放在队列的前面

这样整个队列就开始循环起来了,称之为循环队列

怎么知道队列已经排满了呢?

对于循环队列我们肯定不能够直接使用 队头的索引 等于 队尾的索引+1这样的办法?

it not can 的时候

【2】【1】【3】【4】【5】【6】【】

队头              队尾

这个时候整个队列已经满了,不能够再增加新的元素了

but 队尾和队头的索引并不一致

it can 的时候

【2】【1】【3】【 】【5】【6】【7】

                            队尾  队头

队尾+1 = 队头

特殊情况

当我们一直出队,一直到底的话,队头 = 队尾

通用的式子

循环队列中队头的计算公式:

数组的首尾是队头,出队的时候队头的位置才会变化,队头的位置在往后挪,位置加一,因为是循环队列,索引不可能大于len(list) 于是对数组长度求余

front = (front + 1) % (len(list))

循环队列中队尾的计算公式:

入队的时候队尾的位置才会变化,队尾的位置往后挪,位置加一,因为是循环队列,所以求余

rear = (rear + 1) % (len(list))

因此,如果(rear + 1)% len(list) = front,意味着现在raar就在front的前一位,因为队尾始终保持为空,此时队列就真的满了

实现队列的代码

# 定义队列类
class MyQueue():
   # 初始化成员变量,队列的两属性:队头、队尾;借用数组实现队列
   def __init__(self,capacity):
       self.list = [None] * capacity
       self.front = 0
       self.rear = 0

   # 入队,从队尾插入元素
   def enqueue(self,element):
       if (self.rear+1) % (len(self.list)) == self.front:
           raise Exception("队列已满")
       self.list[self.rear] = element
       self.rear = (self.rear+1) % (len(self.list))

   # 出队,从队头出队
   def dequeue(self):
       if self.front == self.rear:
           raise Exception("队列已空")
       dequeue_element = self.list[self.front]
       self.front = (self.front+1) % len(self.list)
       return dequeue_element

   # 把整个队列输出出来,从队头-队尾
   def outPut(self):
       i = self.front
       while i != self.rear:
           print(self.list[i],end=" ")
           i = (i+1) % len(self.list)

myQueue = MyQueue(5)
# 入队,队列有效长度为4
myQueue.enqueue(1)
myQueue.enqueue(2)
myQueue.enqueue(3)
myQueue.enqueue(4)
myQueue.outPut()

# 因为队尾始终为空,所以,实际数组长度为5
# myQueue.enqueue(5)

# 分行
print()
# 出队
myQueue.dequeue()
myQueue.outPut()

image-20200526081355753

image-20200526082250579

 

 

posted on 2020-05-26 08:35  和朋  阅读(418)  评论(0)    收藏  举报