数据结构 | 队列

93 阅读4分钟

队列

先进先出的线性数据结构,队列模拟排队现象,从尾部入队,头部出队

  • 队列头部为队首,尾部为队尾
  • 元素从队尾加入的操作叫入队,删除队首元素为出队

定义节点数据类型为链表

# 定义链表节点
class ListNode():
    def __init__(self, val):
        self.val = val
        self.next = None

队列常用操作

方法名描述时间复杂度
push()入队O(1)
pop()出队O(1)
peek()获取队首元素O(1)

体验队列,可以直接使用编程语言自带的队列类

# 导入对应包,Python 中,虽然 queue.Queue() 是纯正的队列类,但不太好用,我们一般将双向队列类 deque 当作队列使用
from collections import deque

# 实例化对象
queue = deque()

# 入队
for _ in range(1,11):
    queue.append(_)

# 出队
queue.popleft()

# 访问队首元素
queue[0]
2

实现队列

实现思路

1.front 就指向队列的第一个元素的前一个位置, 也就是font队头索引值为-1.
2.rear 指向队列的最后一个元素,rear初始值为-1.
3.当队列满时,条件是 rear == maxSize - 1 【满】.
4.对队列为空的条件, rear == front【空】.
5.队列中有效的数据的个数 (rear - front).

基于链表实现队列

将链表头节点和为节点看作队列的队首和队尾

  • 入队时,在队尾节点加入元素
  • 出队时,在队首删除一个元素
class LinkedListQueue:
    def __init__(self):
        # 数据类型 ListNode
        # 前指针指向队头
        self.front = None
        # 后指针指向队尾
        self.rear = None
        # 长度
        self._size = 0

    def size(self):
        # 获取长度
        return self._size

    def is_empty(self):
        # 判断是否为空
        return self._size == 0

    def push(self, val):
        # 入队从链表尾入队
        node = ListNode(val)
        # 如果队列都为空头指针和尾指针都直线它
        if self.front == None:
            self.front = node
            self.rear = node
        # 但队列不为空改变尾指针指向它
        else:
            self.rear.next = node
            self.rear = node
        self._size += 1

    def pop(self):
        # 出队,从队头出队
        # 取出队头的值
        number = self.peek()
        # 头指针后移
        self.front = self.front.next
        self._size -= 1
        return number

    def peek(self):
        # 返回队头的值
        if self.front == None:
            return '队列为空'
        return self.front.val

    def to_list(self):
        # 转化为列表用于打印
        arr = []
        p = self.front
        while p:
            arr.append(p.val)
            p = p.next
        return arr
# test
queen = LinkedListQueue()
print(queen.is_empty())
for i in range(5):
    queen.push(i)
print(queen.to_list())
for i in range(2):
    print(f'出队:{queen.pop()}')
print(queen.size())
queen.to_list()
True
[0, 1, 2, 3, 4]
出队:0
出队:1
3

[2, 3, 4]

基于数组实现队列

同样的,将数组首元素和尾元素看作队首和队尾,在数组中删除首元素的操作时间复杂度为O(n),使得效率降低

此时,我们只需要两个变量front指向队列队首,size记录队列元素个数(队列长度),此时队尾位置可以由front + size得到

得到的数组范围为[front, rear-1]

  • 入队时,元素赋值给rear索引,并将size索引+1
  • 出队时,front值+1,并将size-1

基于以上的设计可以得到一个逻辑上的队列,但是会出现假溢出现象

  • 假溢出:顺序队列因多次入队列和出队列操作后出现的有存储空间但不能进行入队列操作的溢出
  • 真溢出:顺序队列中,由于数组空间不够而产生的溢出

front和rear进队出队都在往数组尾移动,当它们都到达数组尾部时就无法移动了,出现假溢出情况,解决的办法是把顺序队列所使用的存储空间构造成一个逻辑上首尾相连的循环队列

基于数组的环形队列

实现思路
1.front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素
2.front 的初始值 = 0
3.rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
4.rear 通过front与size计算得到,公式为:rear = (front + size) % maxSize
5.当队列满时,条件是 (rear + 1) % maxSize == front 【满】
6.对队列为空的条件, rear== front【空】
7.当我们这样分析, 队列中有效的数据的个数 (rear + maxSize - front) % maxSize
class ArrayQueue:
    def __init__(self,size):
        # 基于数组初始化指针不需要尾指针了通过计算得到尾指针的位置
        self.front = 0
        # 初始化长度 
        self._size = 0
        # 存储元素的数组 * size表示重复几个
        self.arr = [0] * size
        
    def capacity(self):
        return len(self.arr)
    
    def size(self):
        return self._size

    def is_empty(self):
        return self._size == 0
    
    def push(self,val):
        # 队满
        if self._size == self.capacity():
            raise IndexError('队满')
        # 未满计算尾指针
        rear = (self.front + self._size) % self.capacity()
        self.arr[rear] = val
        self._size += 1

    def pop(self):
        # 取队首元素
        number = self.peek()
        # 队首位置
        self.front = (self.front + 1) % self.capacity()
        self._size -= 1
        return number


    def peek(self):
        # 队空
        if self.capacity() == 0:
            raise IndexError('出队异常,当前队空')
        # 取队首元素
        return self.arr[self.front]

    def to_list(self):
        res = [0] * self._size
        j = self.front
        for i in range(self._size):
            res[i] = self.arr[j % self.capacity()]
            j += 1
        return res
# test
queen = ArrayQueue(10)
print(queen.is_empty())
for i in range(10):
    queen.push(i)
print(queen.to_list())
for i in range(2):
    print(f'出队:{queen.pop()}')
print(queen.size())
queen.to_list()
True
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
出队:0
出队:1
8

[2, 3, 4, 5, 6, 7, 8, 9]