Python模拟 栈 队列 以及优先队列的操作记录

栈:数据是后进先出 (LIFO)     last in first out

队列:数据是先进先出 (FIFO)  first in first out

 

第一种就是列表:(既可以模拟栈也可以模拟队列)一好一差。(还有一个缺点,不能通过简单的方式,设置容器容量的空间。)

列表是一种容器序列,而且在模拟栈操作的时候,速度也是比较可以的。

在模拟栈操作时,必须用append于pop。(模拟栈的时候,性能优秀)

下面演示的是模拟队列操作,模拟栈操作很简单,就是append于pop所以不展示了。

In [228]: ll = list()                                                                   

In [229]: ll.append('任务1')                                                            

In [230]: ll.append('任务2')                                                            

In [231]: ll.append('任务3')                                                            

In [232]: ll.pop(0)                                                                     
Out[232]: '任务1'

In [233]: ll.pop(0)                                                                     
Out[233]: '任务2'

In [234]: ll.pop(0)                                                                     
Out[234]: '任务3'

 这样的模拟操作队列效率是非常低的,因为你每次移出第一个元素,后面的元素索引都会发生变化

专业的说法需要的时间为O(n)

 

第二个为collections.deque,这个一个模拟栈于队列都非常优秀的容器序列。(可以设置容器容量大小,当超过最大容量时,最先进去的元素将被踢出容器

除了随机访问该对象中间的元素的性能很差,耗时为O(n),但一般操作容器序列,很少取访问中间元素。

模拟栈操作:

 

 from collections import deque                                                  

In [34]: deque?                                                                         
Init signature: deque(self, /, *args, **kwargs)
Docstring:     
deque([iterable[, maxlen]]) --> deque object

A list-like sequence optimized for data accesses near its endpoints.
File:           /usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/collections/__init__.py
Type:           type
Subclasses:     

In [35]: d = deque(range(10),maxlen=5)                                                  

In [36]: d                                                                              
Out[36]: deque([5, 6, 7, 8, 9])

In [37]: d.append(10)                                                                   

In [38]: d                                                                              
Out[38]: deque([6, 7, 8, 9, 10])

In [39]: d.pop()                                                                        
Out[39]: 10

In [40]: d.pop()                                                                        
Out[40]: 9

In [41]: d.pop()                                                                        
Out[41]: 8

In [42]: d.pop()                                                                        
Out[42]: 7

In [43]: d.pop()                                                                        
Out[43]: 6

In [44]: d.pop()                                                                        
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-44-663961784a31> in <module>
----> 1 d.pop()

IndexError: pop from an empty deque

 

 

 

 模拟队列操作:

 d                                                                              
Out[45]: deque([])

In [46]: d.append(1)                                                                    

In [47]: d.append(2)                                                                    

In [48]: d.append(3)                                                                    

In [49]: a                                                                              
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-49-3f786850e387> in <module>
----> 1 a

NameError: name 'a' is not defined

In [50]: d                                                                              
Out[50]: deque([1, 2, 3])

In [51]: d.popleft()                                                                    
Out[51]: 1

In [52]: d.popleft()                                                                    
Out[52]: 2

In [53]: d.popleft()                                                                    
Out[53]: 3

 

 

上面两个是即可以模拟栈,又可以模拟队列的容器序列,下面介绍的是只能单一模拟栈或者队列的。

queue.LifoQueue从字面就可以看出来是模拟栈的,后进先出。queue模块下的容器序列,提供了锁语句来支持多个并发的生产者于消费者。模块下面的很多类的实例一般用在线程间的通讯。

因为这个容器在取不到元素或者元素的数量超过设置容量时,会阻塞程序。(所以很明显这个是可以设置容量的)

 

In [54]: from queue import LifoQueue                                                    

In [55]: LifoQueue?                                                                     
Init signature: LifoQueue(maxsize=0)
Docstring:      Variant of Queue that retrieves most recently added entries first.
File:           /usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/queue.py
Type:           type
Subclasses:     

In [56]: q = LifoQueue()                                                                

In [57]: q = LifoQueue(maxsize=3)                                                       

In [58]: q.put(1)                                                                       

In [59]: q.put(2)                                                                       

In [60]: q.put(3)                                                                       

In [61]: q.get()                                                                        
Out[61]: 3

In [62]: q.get()                                                                        
Out[62]: 2

In [63]: q.get()                                                                        
Out[63]: 1

In [64]: q.get()    
阻塞

 

 

 

栈的我了解的就是上面三种形式,一般优先使用collections.queue

 

后面的单一功能主要队列的序列容器。

queuq.Queue跟前面的LifoQueue功能于操作都差不多,而且都是用线程之间的任务通讯操作。

In [94]: q = Queue(maxsize=3)                                                           

In [95]: q.put(1)                                                                       

In [96]: q.put(2)                                                                       

In [97]: q.put(3)                                                                       

In [98]: q.get()                                                                        
Out[98]: 1

In [99]: q.get()                                                                        
Out[99]: 2

In [100]: q.get()                                                                       
Out[100]: 3

In [101]: q.get()    
阻塞

 

最后还有一个multiprocessing.Queue进行作业队列。

multiprocessing.Queue主要是进程间进行作业通讯的工具,具体使用基本于

queue.Queue差不多,就不上代码了。

 

最后简单的记录一下优先队列。

优先队列史一个容器数据结构,使用具有全序关系的键来管理元素,以便快速访问容器中键值最小或者最大的元素。

1、列表,手动维护有序队列。

In [102]: q = []                                                                        

In [103]: q.append((2, 'code'))                                                         

In [104]: q.append((1,'eat'))                                                           

In [105]: q.append((3,'sleep'))                                                         

In [106]: q.sort(reverse=True)                                                          

In [107]: q.pop()                                                                       
Out[107]: (1, 'eat')

In [108]: q.pop()                                                                       
Out[108]: (2, 'code')

In [109]: q.pop()                                                                       
Out[109]: (3, 'sleep')

In [110]: import bisect                                                                 

In [111]: bisect.insort(q,(2, 'code'))                                                  

In [112]: bisect.insort(q,(1, 'eat'))                                                   

In [113]: bisect.insort(q,(3, 'sleep'))                                                 

In [114]: q                                                                             
Out[114]: [(1, 'eat'), (2, 'code'), (3, 'sleep')]

In [115]:  

 bisect在已经排序完成的情况下,查寻索引非常快的,bisect.bisect可以查寻索引。比默认的.index快太多了。

 

heapq--基于列表的二叉堆。

heaqp是二叉堆,通常用普通列表实现,能在O(logn)时间内插入和获取最小的元素。

 

heapq模块是在Python中不错的优先级队列实现。由于heapq的技术上只提供最小堆实现,因此必须添加额外步骤来确保排序的稳定性,以此来获得实际的优先级队列中所含有的特性。

 

In [140]:  
     ...: q =  [(2, 'code'), (3, 'sleep'), (1, 'eat'), (4,'drink')] 
     ...:                                                                               

In [141]: random.shuffle(q)                                                             

In [142]: h_q = []                                                                      

In [143]: for i in q: 
     ...:     heapq.heappush(h_q, i) 
     ...:                                                                               

In [144]: heapq.heappop(h_q)                                                            
Out[144]: (1, 'eat')

In [145]: heapq.heappop(h_q)                                                            
Out[145]: (2, 'code')

In [146]: heapq.heappop(h_q)                                                            
Out[146]: (3, 'sleep')

In [147]: heapq.heappop(h_q)                                                            
Out[147]: (4, 'drink')

In [148]: q =  [(2, 'code'), (3, 'sleep'), (1, 'eat'), (4,'drink')]                     

In [149]: random.shuffle(q)                                                             

In [150]: heapq.heapify(q)                                                              

In [151]: heapq.heappop(q)                                                              
Out[151]: (1, 'eat')

In [152]: heapq.heappop(q)                                                              
Out[152]: (2, 'code')

In [153]: heapq.heappop(q)                                                              
Out[153]: (3, 'sleep')

In [154]: heapq.heappop(q)                                                              
Out[154]: (4, 'drink')

 上面用了heapq的两种方式堆列表进行了二叉堆操作。第一种是变量元素用heapq.heappush进行数据加载,第二种直接用heapq.heapify对列表元素实行二叉堆。

heapq.nlargest,heapq.nsmallest可以找出容器元素内的最大值范围于最小值范围,通过key=函数,传递函数。

 

from queue import PriorityQueue                                               

In [163]: PriorityQueue?                                                                
Init signature: PriorityQueue(maxsize=0)
Docstring:     
Variant of Queue that retrieves open entries in priority order (lowest first).

Entries are typically tuples of the form:  (priority number, data).
File:           /usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/queue.py
Type:           type
Subclasses:     

In [164]: q =  [(2, 'code'), (3, 'sleep'), (1, 'eat'), (4,'drink')]                     

In [165]: p_q = PriorityQueue()                                                         

In [166]: for i in q: 
     ...:     p_q.put(i) 
     ...:                                                                               


In [171]: p_q.empty                                                                     
Out[171]: <bound method Queue.empty of <queue.PriorityQueue object at 0x10b5e2b90>>

In [172]: p_q.empty()                                                                   
Out[172]: False

In [173]: while not p_q.empty(): 
     ...:     print(p_q.get()) 
     ...:      
     ...:                                                                               
(1, 'eat')
(2, 'code')
(3, 'sleep')
(4, 'drink')

In [174]:                                                                               

 queue.PriorityQueue内部也是使用了heapq,时间复杂度于空间复杂度于heapq相同,但PriorityQueue队列执行并发生产者消费者。可以在特定条件下阻塞。

 queue.PriorityQueue可以给nametuple对象进行优先级压入弹出,弹出参数按照nametuple对象第0个元素的值从小到大进行弹出,本质上来说,nametuple就是元祖。

In [23]: from queue import PriorityQueue                                                                        

In [24]: q = PriorityQueue()                                                                                    

In [25]: car = namedtuple('car','number,colour')                                                                

In [26]: car1 = car(3,'red')                                                                                    

In [27]: car2 = car(1,'yellow')                                                                                 

In [28]: car3 = car(2,'gren')                                                                                   

In [29]: q.put(car1)                                                                                            

In [30]: q.put(car2)                                                                                            

In [31]: q.put(car3)                                                                                            

In [32]: q.get()                                                                                                
Out[32]: car(number=1, colour='yellow')

In [33]: q.get()                                                                                                
Out[33]: car(number=2, colour='gren')

In [34]: q.get()                                                                                                
Out[34]: car(number=3, colour='red')

In [35]: q.get()                                                                                                

 

queue里面的三个模块基本在模拟队列,栈,于优先级队列中都又使用。

分别是queue.LifoQueue,queue.Queue,queue.PriorityQueue。

 

Python前面记录的三种优先队列,其中priorityQueue是其中的首选,具有良好的面向对象的接口

如果想避免PriorityQueue的锁开销,建议直接用heapq模块。

 

2021年1月21日更新:

queue的一些有趣玩法,主要是一个rotate还有就是,collections.queue更像一个加强版的list

参考官方链接:https://docs.python.org/zh-cn/3/library/collections.html?highlight=deque#collections.deque.rotate

这一节展示了deque的多种用法。

collestion.deque就是加强版的list,list能实现的功能,它都能实现。

限长deque提供了类似Unix tail 过滤功能

def tail(filename, n=10):
    'Return the last n lines of a file'
    with open(filename) as f:
        return deque(f, n)

  一个 轮询调度器 可以通过在 deque 中放入迭代器来实现。值从当前迭代器的位置0被取出并暂存(yield)。 如果这个迭代器消耗完毕,就用 popleft() 将其从对列中移去;否则,就通过 rotate() 将它移到队列的末尾

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    iterators = deque(map(iter, iterables))
    while iterators:
        try:
            while True:
                yield next(iterators[0])
                iterators.rotate(-1)
        except StopIteration:
            # Remove an exhausted iterator.
            iterators.popleft()

  这是rotate的使用确实让我感觉非常有意思,且非常使用。要是自己写,确实让人伤脑筋。

 

posted @ 2019-12-20 01:20  就是想学习  阅读(552)  评论(0编辑  收藏  举报