来自《Python数据结构学习笔记》(张清云 编著)
第五章 队列和栈
5.1队列
5.1.1
主要作用:
- 解耦,使程序实现松耦合(一个模块修改不会影响其他模块)
- 提高程序的效率
循环队的入队算法:
- tail=tail+1
- 如果tail=n+1,则tail=1
- 如果head=tail,尾指针和头指针重合,表示元素已装满队列,实行“上溢”出错处理;否则Q(tail)=X,结束整个过程,X表示新的出入元素。
队列的基本操作:
(1)InitQueue(&Q)
构造一个空队列
(2)DestroyQueue(&Q)
初始条件:队列Q已存在
操作结果:销毁队列Q
(3)ClearQueue(&Q)
初始条件:队列Q已存在
操作结果:将队列Q重置为空队列
(4)QueueEmpty(Q)
初始条件:队列Q已存在
操作结果:若Q为空队列,则返回TRUE,否则返回FALSE
(5)QueueLength(Q)
初始条件:队列Q已存在
操作结果:返回队列Q中数据元素的个数
(6)GetHead(Q,&e)
初始条件:队列Q已存在且非空
操作结果:用e返回Q中对头元素
(7)EnQueue(&Q,e)
初始条件:队列Q已存在
操作结果:插入元素e为Q的新的队尾元素
(8)DeQueue(&Q,e)
初始条件:队列Q已存在且非空
操作结果:删除Q的队头元素,并用e返回其值
(9)QueueTraverse(Q,visit())
初始条件:队列Q已存在且非空
操作结果:从对头到队尾依次对Q的每个数据元素调用函数visit(),一旦visit()失败,则操作失败
5.1.2 Python内置的队列操作方法
- Queue():定义一个空队列。无参数,返回值是空队列
- enqueue(item):在队列尾部加入一个数据项。参数是数据项,无返回值
- dequeue():删除队列头部的数据项。不需要参数,返回值是被删除的数据,队列本身有变化
5.1.3 基于内置模块queue的队列
(1)FIFO(先进先出)
import queue
q=queue.Queue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
1
2
3
(2)LIFO(后进先出)
import queue
q=queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
3
2
1
(3)数据可设置优先级
import queue
q=queue.PriorityQueue()
q.put((2,'2'))
q.put((1,'1'))
q.put((3,'3'))
q.put((1,'a'))
print(q.get())
print(q.get())
print(q.get())
print(q.get())
(1,'1')
(1,'a')
(2,'2')
(3,'3')
(4)向任务已经完成的队列发送一个信号
当t2线程将队列中的数据全部取出之后,才继续执行主线程。
import queue
import threading
import time
def q_put():
for i in range(10):
q.put('1')
while True:
q.put('2')
time.sleep(1)
def q_get():
while True:
temp=q.get()
q.task_done() #告知等待的队列(queue.join())在这等待任务处理已完成
print(temp)
time.sleep(0.3)
q=queue.Queue()
t1=threading.Thread(target=q_put)
t2=threading.Thread(target=q_get)
t1.start()
t2.start()
q.join() #开始阻塞
print('queue is empty now')
(5)生产者和消费者模型
通过一个容器解决生产者和消费者的强耦合问题。
生产者和消费者彼此不直接通信,通过阻塞队列来进行通信,阻塞队列相当于缓冲区,平衡生产者和消费者的处理能力。
import threading
import time
import queue
def producer():
count=1
while 1:
q.put('No.%1' % count)
print('Producer put No.%1' % count)
time.sleep(1)
count+=1
def customer(name):
while 1:
print('%s get %s' % (name,q.get())
time.sleep(1.5)
q=queue.Queue(maxsize=5)
p=threading.Thread(target=producer,)
c=threading.Thread(target=customer,args=('jack',))
p.start()
c.start()
(6)完整的顺序队列的操作
from queue import Queue #LILO队列
q=Queue() #创建队列对象
q.put(0) #在队列尾部插入元素
q.put(1)
q.put(2)
print('LILO队列',q.queue) #查看队列中的所有元素
print(q.get()) #返回并删除队列头部元素
print(q.queue)
from queue import LifoQueue #LIFO队列
lifoQueue=LifoQueue()
lifoQueue.put(1)
lifoQueue.put(2)
lifoQueue.put(3)
print('LIFO队列',lifoQueue.queue)
lifoQueue.get() #返回并删除队列尾部元素
lifoQueue.get()
print(lifoQueue.queue)
from queue import PriorityQueue #优先队列
priorityQueue=PriorityQueue() #创建优先队列对象
priorityQueue.put(3) #插入元素
priorityQueue.put(78) #插入元素
priorityQueue.put(100) #插入元素
print(priorityQueue.queue) #查看优先级队列中的所有元素
priorityQueue.put(1) #插入元素
priorityQueue.put(2) #插入元素
print('优先级队列:',priorityQueue.queue) #查看优先级队列中的所有元素
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue)
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue) #删除后剩余元素
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue) #删除后剩余元素
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue) #删除后剩余元素
priorityQueue.get() #返回并删除优先级最低的元素
print('全部被删除后:',priorityQueue.queue) #查看优先级队列中的所有元素
from collections import deque #双端队列
dequeQueue=deque(['Eric','John','Smith'])
print(dequeQueue)
dequeQueue.append('Tom') #在右侧插入新元素
dequeQueue.appendleft('Terry') #在左侧插入新元素
print(dequeQueue)
dequeQueue.rotate(2) #循环右移两次
print('循环右移两次后的队列',dequeQueue)
dequeQueue.popleft() #返回并删除队列最左端元素
print('删除最左端元素后的队列:',dequeQueue)
dequeQueue.pop() #返回并删除队列最右端元素
print('删除最右端元素后的队列:',dequeQueue)
5.1.4 基于列表自定义实现的优先队列
class ListPriQueueValueError(ValueError):
pass
class List_Pri_Queue(object):
def __init__(self,elems=[]):
self._elems=list(elems)
#从小到大排序,末尾值最小,但优先级最高,方便弹出且效率为O(1)
self._elems.sort(reverse=True)
#判断队列是否为空
def is_empty(self):
return self._elems is []
#查看最高优先级O(1)
def peek(self):
if self.is_empty():
raise ListPriQueueError("in pop")
return self._elems[-1]
#弹出最高优先级O(1)
def dequeue(self):
if self.is_empty():
raise ListPriQueueValueError("in pop")
return self._elems.pop()
#入队新的优先级O(n)
def enqueue(self,e):
i=len(Self._elems)-1
while i>=0:
if self._elems[i]<e:
i-+1
else:
break
self._elems.insert(i+1,e)
if __name__=="__main__":
l=List_Pri_Queue([4,6,1,3,9,7,2,8])
print(l._elems)
print(l,peek())
l.dequeue()
print(l._elems)
l.enqueue(5)
print(l._elems)
l.enqueue(1)
print(l._elems)
输出:
[9,8,7,6,4,3,2,1]
1
[9,8,7,6,4,3,2]
[9,8,7,6,5,4,3,2]
[9,8,7,6,5,4,3,2,1]
一些功能:
class Queue(object):
def __init__(self):
self._item=[]
def is_empty(self):
"""判断队列是否为空"""
return self.__item==[]
def in_queue(self,item):
"""进队"""
self.__item.append(item)
def out_queue(self):
"""出队"""
return self.__item.pop(0)
def size(self):
"""返回大小"""
return self.__item.__len__()
if __name__=='__main__':
q=Queue()
print(q.is_empty())
q.in_queue(1)
q.in_queue(2)
q.in_queue(3)
q.in_queue(4)
print(q.is_empty())
print(q.size())
print(q.out_queue())
print(q.out_queue())
print(q.size())
输出:
True
False
4
1
2
2
5.1.5 基于堆实现的优先队列
首先判断要操作对象是否为空,然后将新的优先级加入到队列的末尾,将堆顶值最小优先级最高的元素出队,确保在弹出元素后仍然维持堆的顺序,并将最后的元素放在堆顶。
class Heap_Pri_Queue(object):
def __init__(self,elems=[]):
self._elems=list(elems)
if self._elems:
self.buildheap()
#判断是否为空
def is_empty(self):
return self._elems is []
#查看堆顶元素,即优先级最低元素
def peek(self):
if self.is_empty():
raise HeapPriQueueError("in pop")
return self._elems[0]
#将新的优先级加入队列O(logn)
def enqueue(self,e):
#在队列末尾创建一个空元素
self._elems.append(None)
self.siftup(e,len(self._elems)-1)
#新的优先级默认放在末尾,因此失去堆序,进行siftup构建堆序
#将e位移到正确的位置
def siftup(self,e,last):
elems,i,j=self._elems,last,(last-1)//2 #j为i的父节点
while i>0 and e<elems[j]:
elems[i]=elems[j]
i,j=j,(j-1)//2
elems[i]=e
#堆顶值最小优先级最高的出队,确保弹出元素后仍然维持秩序
#将最后的元素放在堆顶,然后进行siftdown
#O(logn)
def dequeue(self):
if self.is_empty():
raise HeapPriQueueError("in pop")
elems=self._elems
e0=elems[0]
e=elems.pop()
if len(elems)>0:
self.siftdown(e,0,len(elems))
return e0
def siftdown(self,e,begin,end):
elems,i,j=self._elems,begin,begin*2+1
while i<end:
if j+1<end and elems[j]>elems[j+1]:
j+=1
if e<elems[j]:
break
elems[i]=elems[j]
i,j=j,j*2+1
elems[i]=e
#构建堆序 O(n)
def buildheap(self):
end=len(self._elems)
for i in range(end//2,-1,-1):
self.siftdown(self._elems[i],i,end)
if __name__=="__main__":
l=Heap_Pri_Queue([5,6,1,2,4,8,9,0,3,7])
print(l._elems)
#[0,2,1,3,4,8,9,6,5,7]
l.dequeue()
print(l._elems)
#[1,2,7,3,4,8,9,6,5,7]
print(l.is_empty())
l.queue(0)
print(l._elems)
print(l.peak())
输出:
[0,2,1,3,4,8,9,6,5,7]
[1,2,7,3,4,8,9,6,5]
False
[0,1,7,3,2,8,9,6,5,4]
0
5.1.6 双端队列
class Deque:
"""双端队列"""
def __init__(self):
self.items=[]
def add_front(self,item):
"""从队头加入一个元素"""
self.items.insert(0,item)
def add_rear(self,item):
"""从队尾加入一个元素"""
self.items.append(item)
def remove_front(self):
"""从队头删除一个元素"""
return self.item.pop(0)
def remove_rear(self):
"""从队尾删除一个元素"""
return self.items.pop()
def is_empty(self):
"""是否为空"""
return self.items==[]
def size(self):
"""队列长度"""
return len(self.items)
if __name__=="__main__":
deque=Deque
deque.add_front(1)
deque.add_front(2)
deque.add_rear(3)
deque.add_rear(4)
print(deque.size()) #4
print(deque.remove_front()) #2
print(deque.remove_front()) #1
print(deque.remove_fear()) #4
print(deque.remove_fear()) #3
输出:
4
2
1
4
3