导语:
队列是一种先进先出(first in first out,FIFO)的线性表,是一种常用的数据结构。
它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
图1 队列
队列有很多种,按照存储结构划分,有链式队列,循环队列,单向队列,双端队列。实现队列的方式也有很多种,如基于链式存储结构的链接队列(又称链队列),基于顺序存储结构的队列。
本文介绍基于数组(一种顺序存储结构)的循环队列的实现和一些基本操作,并用代码的形式讲解。
一些概念:
队列的顺序存储结构和顺序栈类似
在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外,还需要设置头尾两个指针front和rear,分别指示队列头元素及队尾元素的位置
我们规定
- 初始化建立空队列时,令front=rear=0
- 每当插入新的队尾元素时,“尾指针增1”
- 每当删除队头元素时,“头指针增1”
- 在非空队列中,头指针始终指向队列头元素,尾指针始终指向队列尾元素的下一个位置
图2 队列的顺序存储结构
在入队和出队的操作中,头尾指针只增加不减小,致使被删除元素的空间永远无法重新利用,因此,尽管队列中实际的元素个数远远小于向量空间的规模,但也可能由于尾指针巳超出向量空间的上界而不能做入队操作,该现象称为假溢出
解决办法:将顺序队列臆造为一个环状的空间,称之为循环队列
循环队列:
图3 循环队列
循环队列的优势主要包括:
1. 有效利用存储空间:循环队列可以重复利用之前出队的空间,不会浪费存储空间。
2. 操作高效:循环队列的入队和出队操作的时间复杂度都是O(1),效率较高。
3. 实现简单:循环队列的实现相对简单,只需要一个数组和两个指针即可。
4. 适用于循环应用场景:循环队列适用于需要循环存储的场景,如循环缓冲区、循环任务调度等。
队空条件:rear = fornt |
队满条件:(rear+1)%MaxSize == front(零位置没放元素) |
元素进队:rear = (rear+1)%MaxSize |
元素出队:front = (front+1)%MaxSize |
代码:
# 队空条件:rear = fornt
# 队满条件:(rear+1)%MaxSize == front(零位置没放元素)
# 元素进队:rear = (rear+1)%MaxSize
# 元素出队:front = (front+1)%MaxSize
MaxSize = 5
class CircleQueue: # 循环队列
"""若只剩下一个空位置,该循环列表锁定,不能再加,但其优势在于可边删边加(剩两个及以上空位时),避免了假溢出"""
def __init__(self):
self.data = [None] * MaxSize # 初始空间
self.front = 0
self.rear = 0
def push(self, e): # 元素e进队
assert (self.rear + 1) % MaxSize != self.front # 判断队满
self.rear = (self.rear + 1) % MaxSize
self.data[self.rear] = e
def is_empty(self): # 判断队空
return self.rear == self.front
def pop(self): # 元素出队
assert not self.is_empty() # 先判断是否为空
self.front = (self.front + 1) % MaxSize
return self.data[self.front]
def gethead(self): # 获取头元素
assert not self.is_empty()
return self.data[(self.front + 1) % MaxSize]
def getsize(self): # 获取队列长度,在front下标小于rear时,size可以直接用rear-front获取,但是如果边删边加,导致rear小于front,此方法出错
return (self.rear - self.front + MaxSize) % MaxSize #该式满足上叙所有情况
def dispaly(self):
q=self.front
if self.front !=self.rear: #判断队空
for i in range(self.getsize()):
q = (q+1)%MaxSize #符合两种情况的式子
print(self.data[q], end=",")
else:c
return None
def pushk(qu, k, e):
n = qu.getsize()
if k < 1 or k > n + 1: #k必须正常
return False
if k <= n:
for i in range(1, n + 1): #边删边进
if i == k: #插个队,它插完,后面的再边删边进
qu.push(e)
x = qu.pop()
qu.push(x)
e1se: qu.push(e)
return True
def popk(qu, k):
n = qu.getsize()
assert 1 <= k <= n
for i in range(1, n + 1): #和上面的思想一样
x = qu.pop()
if i != k:
qu.push(x)
else:
e = x # 取第k个出队的元素
return e
if __name__=="__main__":
hh = CircleQueue()
print(hh.is_empty())
hh.push(0)
hh.push(1)
hh.push(2)
hh.push(3)
print(hh.getsize())
hh.dispaly()
# True
# 4
# 0, 1, 2, 3,
# Process
# finished
# with exit code 0