队列是一种“先进先出”(FIFO)的数据结构,队列有两端,一边只进,一边只出,即:数据从尾部进入,从头部出来,先进去的就会先出来,就像我们平时食堂打饭排队一样,先去排的先打到饭,后去排的后打到饭。
队列通常可以用数组或者是链表实现。这里以数组举例,假设这儿有一个长度为5的数组,左端为头部,负责取出数据,右端为尾部,负责存入数据。
从尾部加入数据A
再从尾部加入数据B
再从尾部加入数据C
从头部取出数据A
再从尾部加入数据D
再从头部取出数据B
再从尾部加入数据E
这时加入数据F却失败了,可是数组的前两个位置明明就是空的,但是却由于数组的最后一个位置有数据导致新数据无法正常加入,这种情况就是“假溢出”。
有人肯定会说,数组中的数据向前移动两位不久可以了吗?这确实可以,但是如果数组的长度很长,那么需要移动的数据量就会非常大,既消耗时间,又消耗性能,所以这种方法不太好。我这里介绍另外一种解决队列“假溢出”问题的方法:环形队列。
环形队列就是将顺序队列首尾相连形成环形结构的队列,这样环形结构的好处就是:只要队列没有真正的满,永远不会溢出
比如上述顺序队列中的例子,若首位相连,那么F就会放到数组的第一个位置上去
下面我使用Python的列表结构实现了该环形队列
注意:环形队列在数据结构上它是一个环形结构,但是在本质上,它还是一个数组(Python中的列表)。
用两个指针分别指向队列的头部(front)和尾部(rear),front和real初始值为-1,若每存入一个数据,则rear加一,若每取出一个数据,则front加一,当rear等于front的时候,表示队列为空,当rear和front之间的差值等于数组长度的时候,表示队列已满。
具体实现如下(有详细注释):
class CircularQueue:
"""环形队列"""
def __init__(self):
"""初始化,定义队列容量,定义队列头部指针(front)和尾部指针(rear)"""
self.max_queue = 5
self.queue = [None] * self.max_queue
self.front = self.rear = -1
def is_empty(self):
"""判断队列是否为空"""
if self.front == self.rear:
return True
else:
return False
def is_full(self):
"""判断队列是否已满"""
if self.rear - self.front == self.max_queue: # 用尾部指针和头部指针的距离来判断队列是否已满
return True
else:
return False
def enter_queue(self, data):
"""向队列中加入数据"""
if self.is_full(): # 队列已满就不能加入数据
print("队列已满,无法再加入")
else:
self.rear += 1 # 尾部指针向后移动一位
data_index = (self.rear + 1) % self.max_queue - 1 # 根据尾部指针计算出新加入的数据应该在列表的哪个位置(索引)
if data_index == -1: # 如果data_index == -1,说明该新加入的数据应该在列表的最后一个索引位置
data_index = self.max_queue - 1 # 计算出列表的最后一个索引位置
self.queue[data_index] = data # 将数据放入队列(列表)中
print("加入队列成功")
def del_queue(self):
"""从队列中取出数据"""
if self.is_empty(): # 队列已空就不能再取出数据了
print("队列已空,无法再删除")
else:
self.front += 1 # 头部指针向后移动一位
data_index = self.front % 5 # 计算出需要取出的数据所在列表中的索引位置
data = self.queue[data_index] # 拿到该数据
print("取出数据:", data)
self.queue[data_index] = None # 取出后将队列(列表中的该位置重置为None)
def show(self):
"""展示队列"""
if self.is_empty():
print("队列为空,无法显示")
return
num = 0 # 该变量用来计数,表示拿到第几个数据了
while True:
num += 1
data_index = (self.front + num) % 5 # 获取当前数据索引
data = self.queue[data_index] # 拿到数据
print(data, end=' ')
if self.front + num == self.rear: # 当前情况表示已经拿完了队列中的最后一个数据,所以要跳出循环
break
print()
if __name__ == '__main__':
cir_queue = CircularQueue()
while True:
num = input("1.存入队列 2.取出队列 3.查看队列 4.退出\n请输入:")
if num == '1':
try:
data = int(input("输入存入数据:"))
except ValueError:
print("输入数据错误")
continue
cir_queue.enter_queue(data)
elif num == '2':
cir_queue.del_queue()
elif num == '3':
cir_queue.show()
elif num == '4':
print("成功退出")
break
else:
print("输入选项错误,请重新输入")
部分运行结果如下: