数据结构与算法之美 | 队列(Queue)

对列的相关概念

  • 先进先出,即队列
  • 队列的两个基本操作:入队(放一个数据到队列尾部)和出队(从队列头部取一个元素)
  • 队列是一种操作受限的线性表数据结构

顺序队列和链式队列

  • 用数组实现的队列叫作顺序队列

      class Queue:
      """
      一个简单的队列实现
      属性:
      	items(list): 队列数组
      """
    
      def __init__(self):
          """
          Queue类的构造函数
          
          参数:
          	无
          
          返回值:
          	无        
          """
          self.items = []
    
      def is_empty(self):
          """
          检查队列是否为空
          
          参数:
          	无
          
          返回值:
          	True 或 False
          """
          return len(self.items) == 0
    
      def enqueue(self, item):
          """
          将一个元素添加到队列中
          
          参数:
          	无
          
          返回值:
          	添加的元素
          """
          self.items.append(item)
    
      def dequeue(self):
          """
          将一个元素从队列中移除
          
          参数:
          	无
          
          返回值:
          	删除的元素值
          """
          if self.is_empty():
              raise Exception("Queue is empty")
          return self.items.pop(0) 
    
  • 用链表实现的队列叫作链式队列

    class Node:
        """
        初始化队列中的每个节点。
        属性:
            val (int): 表示节点值。
            next (int): 表示下一个节点的指针。
        """
        def __init__(self, val):
            """
            节点构造函数。
            
            参数:
                val: 节点的值。
                
            返回:
                无。
            """
            # val 节点的值
            # next 表示下一个节点的指针
            self.val = val
            self.next = None
    
    class Queue:
        """
        实现队列操作。
        属性:
            head (int): 表示队列头部索引的整数。
            tail (int): 表示队列尾部索引的整数。
        """
        def __init__(self):
            """
            队列构造函数。
    
            参数:
                无。
    
            返回:
                无。
            """
            # head 是队列头部节点
            self.head = None
            # tail 是队列尾部节点
            self.tail = None
    
        def enqueue(self, val):
            """
            将新元素添加到队列尾部。
    
            参数:
                val: 要添加到队列尾部的值。
    
            返回:
                无。
            """
            new_node = Node(val)
            if not self.head:
                self.head = new_node
                self.tail = new_node
            else:
                self.tail.next = new_node
                self.tail = new_node
    
        def dequeue(self):
            """
            从队列头部删除元素并返回其值。
    
            参数:
                无。
    
            返回:
                删除的元素值,如果队列为空则返回 None。
            """
            # 检查队列是否为空
            if not self.head:
                return None
            val = self.head.val
            self.head = self.head.next
            # 再次判断,确保队列状态正确
            if not self.head:
                self.tail = None
            return val
    
        def is_empty(self):
            """
            判断队列是否为空。
    
            参数:
                无。
    
            返回:
                如果队列为空则返回 True,否则返回 False。
            """
            return self.head is None
    

线程池没有空闲线程时,新的任务请求线程资源时,线程池该如何处理?各种处理策略又是如何实现的呢?

  • 非阻塞的处理方式,直接拒绝任务请求;

  • 阻塞的处理方式,将请求排队,等到有空闲线程时,取出排队的请求继续处理

  • 使用队列(数组实现 or 链表实现)存储排队请求

    1)基于链表的实现方式,可以实现一个支持无限排队的无界队列(unbounded queue),但是可能会导致过多的请求排队等待,请求处理的响应时间过长。所以,针对响应时间比较敏感的系统,基于链表实现的无限排队的线程池是不合适的。

    2)基于数组实现的有界队列(bounded queue),队列的大小有限,所以线程池中排队的请求超过队列大小时,接下来的请求就会被拒绝,这种方式对响应时间敏感的系统来说,就相对更加合理。不过,设置一个合理的队列大小,也是非常有讲究的。队列太大导致等待的请求太多,队列太小会导致无法充分利用系统资源、发挥最大性能。

  • 对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队

循环队列

  • 关键点:确定好队空和队满的判定条件
  • 队空判断条件:head == tail
  • 队满判断条件:(tail + 1) % n == head
class CircularQueue:
    """
    使用列表实现的循环队列。
    属性:
        queue (list): 用于存储队列元素的列表。
        head (int): 表示队列头部索引的整数。
        tail (int): 表示队列尾部索引的整数。
        maxSize (int): 表示队列的最大大小的整数。
    """

    def __init__(self):
        """
        CircularQueue类的构造函数。

        参数:
            无。

        返回:
            无。
        """
        self.queue = []
        self.head = 0
        self.tail = 0
        self.maxSize = 8

    def enqueue(self, data):
        """
        将一个元素添加到队列中。

        参数:
            data: 要添加到队列中的数据。

        返回:
            如果元素成功添加,则返回True;如果队列已满,则返回"Queue Full!"。
        """
        # 判断队列是否已满。
        if self.size() == self.maxSize - 1:
            return "Queue Full!"

        self.queue.append(data)
        self.tail = (self.tail + 1) % self.maxSize
        return True

    def dequeue(self):
        """
        从队列中移除一个元素。

        参数:
            无。

        返回:
            如果队列不为空,则返回从队列中移除的元素;如果队列为空,则返回"Queue Empty!"。
        """
        if self.size() == 0:
            return "Queue Empty!"
        data = self.queue[self.head]
        self.head = (self.head + 1) % self.maxSize
        return data

    def size(self):
        """
        计算队列的大小。

        参数:
            无。

        返回:
            队列的大小。
        """
        if self.tail >= self.head:
            return self.tail - self.head
        return self.maxSize - (self.head - self.tail)

阻塞队列

阻塞队列其实就是在队列基础上增加了阻塞操作。当队列已满时,插入操作将被阻塞,直到有空闲的位置;当队列为空时,删除操作将被阻塞,直到队列中有元素可供删除。阻塞队列通常用于多线程场景下的任务调度和协作,可以很好地控制线程的执行顺序和并发度,避免了线程之间的竞争和冲突

  • 使用阻塞队列可以实现一个”生产者 - 消费者模型“

  • 基于阻塞队列,我们还可以通过协调“生产者”和“消费者”的个数,来提高数据的处理效率

并发队列

并发队列是一种支持多线程并发访问的队列,它可以在多个线程同时对队列进行插入、删除等操作,而不会出现数据不一致或并发冲突的情况

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

  • 实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列

猜你喜欢

转载自blog.csdn.net/YuvalNoah/article/details/131033596
今日推荐