数据结构与算法 -- 09 队列

本文深入探讨了队列数据结构的特点及其实现方式,包括顺序队列、链式队列和循环队列。通过代码示例,展示了队列的基本操作如入队和出队,并讨论了队满时的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


引子:
  CPU任务调度处理的速度与线程数有关但非线性正相关,过多的线程反而会导致CPU频繁切换引起性能下降,所以我们需要选取特定大小的线程池。线程池的大小是根据如任务特点等软硬件环境来设置的。
  从线程池申请线程时会遇到没有空闲资源需排队等候的情况,处理这种情况会用到“队列(queue)”这种底层数据结构。

队列(queue)

 队列的数据结构特点和基本操作是?

  数据结构特点:先进先出,后进后出
  基本操作:入队(enqueue)–添加数据到队尾;出队(dequeue)–从对头取出数据。跟栈一样也是一种操作受限的的线性表数据结构。
  与栈对比:栈是先进后出后进先出,拥有入栈和出栈两个操作。队列与栈的一个很大的区别是,栈的数据出入只有一个口,而队列有两个数据口(一个进,一个出)。
在这里插入图片描述
  与栈一样,由数组组成的队列称为顺序队列,由链表组成的队列称为链式队列。

 顺序队列

  话不多说,show you the code:

"""
Discription:
    Array Queue
    obj.capacity  get queue capacity
    obj.head      get queue index head
    obj.tail      get queue index tail
    enqueue()
    dequeue()
    print_all()
Author: calm.xia@gmail.com
"""

import sys

class ArrayQueue(object):


    def __init__(self, capacity: int):
        self.__arrayqueue = [None] * capacity # Here can not work with self.__arrayqueue.append(item)
        self.__capacity = capacity
        self.__head = 0
        self.__tail = 0
    ...

    def enqueue(self, item: int):
        """
        Return:
            False    fail
            True     sucess
        """
        if self.__tail == self.__capacity :
            return False
        self.__arrayqueue[self.__tail] = item
        self.__tail += 1
        if self.__tail == self.__capacity :
            print("queue is full!")
        return True


    def dequeue(self):
        '''
        Return:
            None    fail
            item     success
        '''
        print("dequeue... head %d tail %d"%(self.__head, self.__tail))
        if self.__head == self.__tail :
            print("Aarray queue is NULL!")
            return
        #item = self.__arrayqueue.pop(self.__head)
        item = self.__arrayqueue[self.__head]
        self.__arrayqueue[self.__head] = None
        self.__head += 1
        return item

    ...

含测试代码的详细代码见 github:ArrayQueue.py
贴一张队满时候的图,注意,队满时 tail 所指向的下表并没有存储元素。
在这里插入图片描述

问题来了:当 tail 达到队列 capacity(队列大小)处,且 head 并不等于0,又有新元素入队时,怎么办?
在这里插入图片描述
在这里,出队并没有影响,仅仅是入队的时候需要进行处理,这里有两种方案:

  • 方案一:每次出队都是取出 index 0的元素,那么在每次出队操作完后都将剩余队列数据整体往左搬移填满 index 0。这是最容易想到的办法,但是出队操作一频繁,这样的操作时间复杂度达到O(n),有没有更好的办法?
  • 方案二:集中搬移。当 tail 达到队列 capacity(队列大小)处,且 head 不等于0,又有新元素入队时,触发一次整体数据搬移,其他时候数据不进行搬移。比如,head不等于0,tail 也未达到 capacity时,并不触发数据搬移操作。这样可以大大降低搬移数据的次数,降低操作的时间复杂度。整体过程如下图:
    在这里插入图片描述
    show you the code:
    def enqueue(self, item: int):
        """
        Return:
            False    fail
            True     sucess
        """
        print("enqueue... head %d tail %d"%(self.__head, self.__tail))
        if self.__tail == self.__capacity :
            if self.__head == 0 :
                print("enqueue error: queue is full ")
                return False
            else:
                print("enqueue warn: migrate queue data...")
                for i in range(self.__head, self.__tail):
                    self.__arrayqueue[i - self.__head] = self.__arrayqueue[i]
                    self.__arrayqueue[i] = None
                self.__tail = self.__tail - self.__head
                self.__head = 0
        #print("enqueue 002... head %d tail %d"%(self.__head, self.__tail))
        self.__arrayqueue[self.__tail] = item
        #self.__arrayqueue.append(item)
        self.__tail += 1
        if self.__tail == self.__capacity :
            print("queue is full!")
        return True

 链式队列

Todo

 循环队列

  上面顺序队列中提到的 tail == capacity 时数据搬移的问题能否优化避免呢?答案是可以的,用到的就是循环队列
将顺序队列首尾相连即可形成循环队列。
在这里插入图片描述
只要head和tail不交叉,tail可以一直递增下去,直到队列满;当队列满时,出队之后,可以继续入队,从而不用进行数据搬移。这依赖于循环队列的一个关键:确定队空和队满的判定条件。条件具体是?先上图,可以通过枚举的方式找出规律再验证。
在这里插入图片描述
答案是:(tail + 1) % capacity = head。其中capacity为队列大小。
话不多说,上代码:

"""
Discription:
    Circular Queue
    obj.capacity  get queue capacity
    obj.head      get queue index head
    obj.tail      get queue index tail
    enqueue()
    dequeue()
    print_all()
Author: calm.xia@gmail.com
"""

import sys

class CircularQueue(object):


    def __init__(self, capacity: int):
        self.__circularqueue = [None] * (capacity)
        self.__capacity = capacity
        self.__head = 0
        self.__tail = 0
...

    def enqueue(self, item: int):
        """
        Return:
            False    fail
            True     sucess
        """
        print("enqueue... head %d tail %d"%(self.__head, self.__tail))
        # Actually, the real capacity is self.__capacity-1. Circular queue will waste a element space
        if (self.__tail + 1) % self.__capacity == self.__head : 
            print("enqueue error: circular queue is full ")
            return False
        else:
            self.__circularqueue[self.__tail] = item
            #self.__circularqueue[i] = None
            self.__tail = (self.__tail + 1) % self.__capacity
        return True


    def dequeue(self):
        '''
        Return:
            None    fail
            item     success
        '''
        print("dequeue... head %d tail %d"%(self.__head, self.__tail))
        if self.__head == self.__tail :
            print("Acircular queue is NULL!")
            return
        #item = self.__circularqueue.pop(self.__head)
        item = self.__circularqueue[self.__head]
        self.__circularqueue[self.__head] = None
        self.__head = (self.__head +1) % self.__capacity
        return item

详细代码参看 github:ArrayQueue.py
注意:循环队列队满时会浪费一个元素的存储空间,这点需要注意。


预告:下一篇《数据结构与算法 – 09 队列 | 实际应用》将介绍一些队列的实际应用。你可以想到一些吗?欢迎大家交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值