《数据结构与算法之美》笔记——队列,数组实现顺序队列,数组实现循环队列,链式队列

一、什么是队列?

1.先进者先出,这就是典型的「队列」结构。

2.支持两个操作:

  • 入队 enqueue(),放一个数据到队尾;
  • 出队 dequeue(),从队头取一个元素。

3.所以,和栈一样,队列也是一种操作受限的线性表

img

二、如何实现队列?

1.队列API

public interface Queue<T> {
public void enqueue(T item); //入队
public T dequeue(); //出队
public int size(); //统计元素数量
public boolean isNull(); //是否为空
}

2.数组实现(顺序队列)

在这里插入图片描述

下面是 python 代码:

class ArrayQueue():
    # 数组实现顺序队列
    def __init__(self, capacity):
        self._capacity = capacity
        self._items = []
        self._head, self._tail = 0, 0

    # 入队列,从队尾入
    def enqueue(self, item):
        # 判断队列是否已满
        if self._tail == self._capacity:
            if self._head == 0:
                return False
            else:
                # 数据搬移
                for i in range(self._tail - self._head):
                    self._items[i] = self._items[self._head + i]
                self._tail = self._tail - self._head
                self._head = 0
        self._items.insert(self._tail, item)
        self._tail += 1
        return True

    # 出队列,从队头出
    def dequeue(self):
        # 判断队列是否为空
        if self._head == self._tail:
            return None
        else:
            item = self._items[self._head]
            self._head += 1
            return item

    def __repr__(self):
        if self._head == self._tail:
            return '空'
        else:
            result = []
            cur = self._head
            while cur != self._tail:
                result.append(self._items[cur])
                cur += 1
        return '->'.join(str(x) for x in result)

    # def __str__(self):
        # return self.__repr__()


# 测试
if __name__ == '__main__':
    queue = ArrayQueue(10)
    for i in range(10):
        queue.enqueue(i)
    print(queue)
    for i in range(5):
        print(queue.dequeue())
    print(queue)

3.数组实现(循环队列)

在这里插入图片描述

下面是 python 代码:

class LoopArrayQueue():
    # 数组实现循环队列
    def __init__(self, capacity):
        self._capacity = capacity + 1
        self._items = [None] * self._capacity
        self._head, self._tail = 0, 0

    # 入队列,从队尾入
    def enqueue(self, item):
        # 如果队列已满
        if self.is_full():
            return False
        else:
            self._items[self._tail] = item
            self._tail = (self._tail + 1) % self._capacity
            return True

    # 出队列,返回的是出队列的元素的值
    def dequeue(self):
        # 如果队列为空
        if self.is_empty():
            return False
        else:
            self._items[self._head] = None  # 将其清空
            self._head = (self._head + 1) % self._capacity
            return True

    def is_full(self):
        return (self._tail + 1) % self._capacity == self._head

    def is_empty(self):
        return self._tail == self._head

    # 返回循环队列的队头元素
    def prior(self):
        return self._items[self._head] if not self.is_empty() else -1

    # 返回循环队列的队尾元素
    def tail(self):
        return self._items[(self._tail - 1 + self._capacity) % self._capacity] if not self.is_empty() else -1

    def __repr__(self):
        if self._head == self._tail:
            return '空'
        else:
            cur = self._head
            result = []
            while cur != self._tail:
                result.append(self._items[cur])
                cur = (cur + 1) % self._capacity
        return '->'.join(str(x) for x in result)

if __name__ == '__main__':
    queue = LoopArrayQueue(10)
    for i in range(8):
        queue.enqueue(i)
    print(queue)
    for i in range(5):
        queue.dequeue()
    print(queue)
    print(queue.prior(), queue.tail())
    for i in range(4):
        queue.enqueue(i)
    print(queue)

4.链表实现(链式队列)

在这里插入图片描述

下面是 python 代码,这里使用的是不带头结点的单链表

class Node():
    def __init__(self, x):
        self.data = x
        self.next = None

class LinkedQueue():
    def __init__(self):
        self._head = None
        self._tail = None

    # 进队列
    def enqueue(self, val):
        newNode = Node(val)
        # 如果链表队列为空
        if self._tail == None:
            self._head = newNode
        else:
            self._tail.next = newNode
        self._tail = newNode

    # 出队列,返回的是出队列的node的值
    def dequeue(self):
        # 如果队列为空
        if self._head == self._tail:
            return None
        else:
            node = self._head
            self._head = self._head.next
            return node.data

    def __repr__(self):
        # 队列为空
        if self._head == self._tail:
            return '空'
        else:
            result = []
            cur = self._head
            while cur != None:
                result.append(cur.data)
                cur = cur.next
            return '->'.join(str(x) for x in result)

if __name__ == '__main__':
    queue = LinkedQueue()
    for i in range(10):
        queue.enqueue(i)
    print(queue)
    for i in range(5):
        queue.dequeue()
    print(queue)
    for i in range(5):
        queue.enqueue(i)
    print(queue)
    for i in range(10):
        queue.dequeue()
    print(queue)

需要注意的是,一般情况下 head == tail 表示的是队列为空,但是在不带头结点的单链表的情况下,head == tail 还有可能表示队列只有一个节点,所以还要加上 head == tail == None 才能表示队列为空,所以不带头结点的单链表是一种很特殊的情况,这里要重点强调。

三、队列有哪些常见的应用?

1.阻塞队列

1)在队列的基础上增加阻塞操作,就成了阻塞队列。

2)阻塞队列就是在队列为空的时候,从队头取数据会被阻塞,因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后在返回。

从上面的定义可以看出这就是一个「生产者-消费者模型」。这种基于阻塞队列实现的「生产者-消费者模型」可以有效地协调生产和消费的速度。当「生产者」生产数据的速度过快,「消费者」来不及消费时,存储数据的队列很快就会满了,这时生产者就阻塞等待,直到「消费者」消费了数据,「生产者」才会被唤醒继续生产。不仅如此,基于阻塞队列,我们还可以通过协调「生产者」和「消费者」的个数,来提高数据处理效率,比如配置几个消费者,来应对一个生产者。

2.并发队列

1)在多线程的情况下,会有多个线程同时操作队列,这时就会存在线程安全问题。能够有效解决线程安全问题的队列就称为并发队列。

2)并发队列简单的实现就是在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或取操作。

3)实际上,基于数组的循环队列利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。

3.线程池资源枯竭时的处理

资源有限的场景,当没有空闲资源时,基本上都可以通过「队列」这种数据结构来实现请求排队。

欢迎关注微信公众号 shinerise,与你一起慢慢进步~

Alt

发布了34 篇原创文章 · 获赞 9 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Shine_rise/article/details/103949051