隊列是一種“先進先出”(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("輸入選項錯誤,請重新輸入")
部分運行結果如下: