栈和队列
物理结构和逻辑结构
物理结构:数组和链表在内存中都有实实在在的内存空间,这就是物理结构
逻辑结构:逻辑结构是抽象的概念,它依赖于物理结构而实现
栈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()


浙公网安备 33010602011771号