解決順序隊列的“假溢出”問題之環形隊列(循環隊列)——Python實現

隊列是一種“先進先出”(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("輸入選項錯誤,請重新輸入")

部分運行結果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章