【数据结构】队列

举报
子都爱学习 发表于 2022/01/16 17:07:29 2022/01/16
【摘要】 什么是队列FIFO:先进先出只允许在一端进行插入操作,而在另一端进行删除操作的线性表队列实现1.利用列表实现注意:列表是线性表,队列在列表尾部入队(O(1)),列表头部出队(O(n)),出队时间开销大。所以不推荐用列表# 环形队列class Queue: def __init__(self, size=100): self.queue = [0] * size ...

什么是队列

  • FIFO:先进先出
  • 只允许在一端进行插入操作,而在另一端进行删除操作的线性表

队列实现

1.利用列表实现
注意:列表是线性表,队列在列表尾部入队(O(1)),列表头部出队(O(n)),出队时间开销大。所以不推荐用列表

# 环形队列
class Queue:
    def __init__(self, size=100):
        self.queue = [0] * size
        self.size = size
        self.rear = 0
        self.front = 0

    def push(self, value):
        if not self.is_filled():
            self.rear = (self.rear + 1) % self.size
            self.queue[self.rear] = value
        else:
            raise IndexError('Queue is filled')

    def pop(self):
        if not self.is_empty():
            self.front = (self.front + 1) % self.size
            return self.queue[self.front]
        else:
            raise  IndexError('Queue is empty')

    def is_empty(self):
        return self.rear == self.front

    def is_filled(self):
        return (self.rear + 1) % self.size == self.front

    def get_rear(self):
        return self.queue[self.rear]

    def get_front(self):
        return self.queue[self.front]


2.collections.deque模块

deque是双端队列(double-ended queue)的缩写

返回一个新的双向队列对象,从左到右初始化(用方法 append()) ,从 iterable (迭代对象) 数据创建。如果 iterable 没有指定,新队列为空。maxlen:Deque的最大尺寸,如果没有限定的话就是 None 。
Deque 支持线程安全,内存高效添加(append)和弹出(pop),从两端都可以,两个方向的大概开销都是 O(1) 复杂度。
虽然 list 对象也支持类似操作,不过这里优化了定长操作和 pop(0) 和 insert(0, v) 的开销。它们引起 O(n) 内存移动的操作,改变底层数据表达的大小和位置。

                                                                               

  • append(x):添加 x 到右端。
  • appendleft(x):添加 x 到左端。
  • clear():移除所有元素,使其长度为0.
  • copy():创建一份浅拷贝。
  • count(x):计算 deque 中元素等于 x 的个数。
  • extend(iterable):扩展deque的右侧,通过添加iterable参数中的元素。
  • extendleft(iterable):扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。
  • index(x[, start[, stop]]):返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一个匹配项,如果未找到则引发 ValueError。
  • insert(i, x):在位置 i 插入 x 。如果插入会导致一个限长 deque 超出长度 maxlen 的话,就引发一个 IndexError。
  • pop():移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个 IndexError。
  • popleft():移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError。
  • remove(value):移除找到的第一个 value。 如果没有的话就引发 ValueError。
  • reverse():将deque逆序排列。返回 None 。
  • rotate(n=1):向右循环移动 n 步。 如果 n 是负数,就向左循环。
    Deque对象同样提供了一个只读属性:

除了以上操作,deque 还支持迭代、封存、len(d)、reversed(d)、copy.copy(d)、copy.deepcopy(d)、成员检测运算符 in 以及下标引用例如通过 d[0] 访问首个元素等。 索引访问在两端的复杂度均为 O(1) 但在中间则会低至 O(n)。 如需快速随机访问,请改用列表。

Deque从版本3.5开始支持 __add__(), __mul__(), 和 __imul__() 。

 代码示例:

from collections import deque
Queue = deque(maxlen=3)
Queue.append(1)
Queue.append(2)
Queue.popleft()
Queue.count(2)
# Queue.clear()
print(Queue)
# deque([2], maxlen=3)
Queue1 = deque(range(4), maxlen=None)
new_queue = Queue + Queue1   # __add__操作中maxlen是前者
print(new_queue)
# deque([1, 2, 3], maxlen=3)


3.queue模块

Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

  • FIFO:先添加的任务先取回
  • LIFO :最近被添加的条目先取回(操作类似一个堆栈)
  • Priority:条目将保持排序( 使用 heapq 模块 ) 并且最小值的条目第一个返回。

常用方法:

Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False,Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当于Queue.get(False),非阻塞方法
Queue.put(item) 写入队列,timeout等待时间
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号。每个get()调用得到一个任务,接下来task_done()调用告诉队列该任务已经处理完毕。
Queue.join() 实际上意味着等到队列为空,再执行别的操作
示例代码:

from queue import Queue,LifoQueue,PriorityQueue
#先进先出队列
q=Queue(maxsize=5)
#后进先出队列
lq=LifoQueue(maxsize=6)
#优先级队列
pq=PriorityQueue(maxsize=5)


from queue import Queue
import time,threading
q=Queue(maxsize=0)

def product(name):
  count=1
  while True:
    q.put('第{}个冰淇凌'.format(count))
    print('{}做冰淇凌第{}个'.format(name, count))
    count+=1
    time.sleep(1)
def consume(name):
  while True:
    print('{}吃了{}'.format(name, q.get()))
    time.sleep(5)
    q.task_done()
t1=threading.Thread(target=product,args=('小狗',))
t2=threading.Thread(target=consume,args=('小猫',))
t3=threading.Thread(target=consume,args=('小猪',))

t1.start()
t2.start()
t3.start()

4.asyncio.Queue

queue.Queue和asyncio.Queue都是支持多生产者、多消费者的队列,基于collections.deque,他们都提供了Queue(FIFO队列)、PriorityQueue(优先级队列)、LifoQueue(LIFO队列),接口方面也相同。

区别在于queue.Queue适用于多线程的场景,asyncio.Queue适用于协程场景下的通信,由于asyncio的加成,queue.Queue下的阻塞接口在asyncio.Queue中则是以返回协程对象的方式执行,具体差异如下表:

5.multiprocessing.Queue

multiprocessing提供了三种队列,分别是Queue、SimpleQueue、JoinableQueue。 

multiprocessing.Queue既是线程安全也是进程安全的,相当于queue.Queue的多进程克隆版。和threading.Queue很像,multiprocessing.Queue支持put和get操作,底层结构是multiprocessing.Pipe。

multiprocessing.Queue底层是基于Pipe构建的,但是数据传递时并不是直接写入Pipe,而是写入进程本地buffer,通过一个feeder线程写入底层Pipe,这样做是为了实现超时控制和非阻塞put/get,所以Queue提供了join_thread、cancel_join_thread、close函数来控制feeder的行为,close函数用来关闭feeder线程、join_thread用来join feeder线程,cancel_join_thread用来在控制在进程退出时,不自动join feeder线程,使用cancel_join_thread有可能导致部分数据没有被feeder写入Pipe而导致的数据丢失。

和threading.Queue不同的是,multiprocessing.Queue默认不支持join()和task_done操作,这两个支持需要使用mp.JoinableQueue对象。

SimpleQueue是一个简化的队列,去掉了Queue中的buffer,没有了使用Queue可能出现的问题,但是put和get方法都是阻塞的并且没有超时控制。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。