数据结构与算法(python版)之队列
一、什么是队列
1.队列
队列是一种有次序的数据集合,其特征是新数据项的添加总发生在一端(通常称为“尾 rear”端),而现存数据项的移除总发生在另一端(通常称为“首front”端)。
当数据项加入队列,首先出现在队尾,随着队首数据项的移除,它逐渐接近队首。
2.队列特征
(1)先进先出或先到先服务;
(2)队列只有一个入口和一个出口。
(3)不允许数据项直接插入队中,也不允许从中间移除数据项。
3.队列的5个操作
Queue() #创建一个空队列对象,返回值为Queue对象
enqueue(item) #将数据项item添加到队尾,无返回值
dequeue() #从队首移除数据项,返回值为队首数据项,队列被修改
isEmpty() #测试是否为空队列,返回值为bool值
size() #返回队列中数据项的个数
4.示例
5.用list实现队列
将list的首端作为队列尾端,list的末端作为队列首端,enqueue()的复杂度为O(n),dequeue()的复杂度为O(1)。首尾倒过来,复杂度也会倒过来。
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
二、队列的应用一:热土豆问题
1.问题描述
2.算法设计
用队列来实现热土豆问题的算法,参加游戏人的列表,以及传土豆的次数num,算法返回最后剩下的人名。
3.实现程序
from pythonds.basic.queue import Queue
def hotPotato(nameList,num):
simQueue=Queue()
for name in nameList:
simQueue.enqueue(name)
while simQueue.size()>1:
for i in range(num):
simQueue.enqueue(simQueue.dequeue())
simQueue.dequeue()
return simQueue.dequeue()
print(hotPotato(['A','B','C','D'],7))
三、队列的应用二:打印任务
1.任务
多人共享一台打印机,采取先到先服务的队列策略来执行打印任务。在这种设定下,一个首要的问题是:
(1)这种打印作业系统的容量有多大?
(2)在能够接受的等待时间内,系统能容纳多少用户以多高频率提交多少任务。
模拟算法:
2.问题
怎么设定打印机的模式,让大家都不要等太久的前提下尽量提高打印质量。这是一个典型的决策支持问题,无法通过规则直接计算。我们用一段程序来模拟这种打印任务场景,然后对程序运行结果进行分析,支持我们对打印机模式设定的决策。
3.建模
3.模拟打印流程
4.实现程序
from pythonds.basic.queue import Queue
import random
class Printer:
def __init__(self,ppm):
#打印机速度
self.pageRate=ppm
#打印任务
self.currentTask=None
#任务倒计时
self.timeRemaining=0
#执行打印1s
def tick(self):
self.timeRemaining=self.timeRemaining-1
if self.timeRemaining<=0:
self.currentTask=None
def isBusy(self):
if self.currentTask!=None:
return True
else:
return False
#打印新作业
def startNext(self,newTask):
self.currentTask=newTask
self.timeRemaining=newTask.getPages()*60/self.pageRate
class Task:
def __init__(self,time):
#任务生成时间戳
self.timeStamp=time
#打印页数
self.pages=random.randrange(1,21)
def getStamp(self):
return self.timeStamp
def getPages(self):
return self.pages
def waitTime(self,currentTime):
return currentTime-self.timeStamp
def newPrintTask():
# 1/180概率生成作业
num=random.randrange(1,181)
if num==180:
return True
else:
return False
#仿真
#numSeconds:模拟时长
#pagePerMin:打印机速度
def simulation(numSeconds,pagePerMin):
#创建打印机对象
labPrinter=Printer(pagePerMin)
#创建打印队列
printQueue=Queue()
#等待时间列表
waitingTimes=[]
for currentSecond in range(numSeconds):
#是否有任务提交
if newPrintTask():
task=Task(currentSecond)
printQueue.enqueue(task)
# 如果打印机空闲且打印队列不空
if (not labPrinter.isBusy()) and (not printQueue.isEmpty()):
nextTask=printQueue.dequeue()
waitingTimes.append(nextTask.waitTime(currentSecond))
labPrinter.startNext(nextTask)
labPrinter.tick()
averageWait=sum(waitingTimes)/len(waitingTimes)
print("Average Wait %6.2f secs %3d tasks remaining."%(averageWait,printQueue.size()))
for i in range(10):
simulation(3600,10)
四、双端队列Deque
1.什么是双端队列Deque
双端队列Deque是一种有次序的数据集,跟队列相似,其两端可称作“首”“尾”端,但Deque中的数据项既可以从队首加入,也可以从队尾端加入;数据项也可以从两端移除。
从某种意义上说,双端队列集成了栈和队列的能力。
2.双端队列的7个操作
Deque() #创建一个空的双端队列
addFront(item) #将item加入队首
addRear(item) #将item加入队尾
removeFront() #从队首移除数据项,返回值为移除的数据项
removeRear() #从队尾移除数据项,返回值为移除的数据项
isEmpty() #返回Deque是否为空
size() #返回Deque中包含数据项的个数
3.实例
4.用List实现双端队列Deque
将List的末端作为队首,将List的首端作为队尾。addFront()和removeFront()的复杂度为O(1),addRear()和removeRear()的复杂度为O(n).
实现程序:
class Deque:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def addFront(self, item):
self.items.append(item)
def addRear(self, item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def size(self):
return len(self.items)
5.双端队列的一个应用:回文词的判断
(1)什么是回文词?
回文词指正读和反读一样的词。如:radar、madam、toot、上海自来水来自海上、山东落花生花落东山。
(2)思路
①先将要判断的词从队尾加入Deque;
②从两端同时移除字符判断是否相同,直到Deque中的剩下0个或一个字符。
(3)代码实现
from pythonds.basic.deque import Deque
def palchecker(aStr):
charDeque=Deque()
for ch in aStr:
charDeque.addRear(ch)
stillEqual=True
while charDeque.size()>1 and stillEqual:
first=charDeque.removeFront()
last=charDeque.removeRear()
if first!=last:
stillEqual=False
return stillEqual
print(palchecker('lsdkjfskf'))
print(palchecker('radar'))