1 队列抽象数据类型及Python实现
- 定义: 当数据项加入队列,首先出现在队尾,随着队首数据项的移除,它逐渐接近队首。
- 特性: 队列是一种有次序的数据集合,新数据项的添加总发生在一端(通常称为 “ 尾rear ” 端),而现存数据项的移除总发生在另一端(通常称为“ 首front ” 端)。
1.1 抽象数据类型Queue
Queue()
:创建一个空队列对象,返回值为Queue 对象;enqueue(item)
:将数据项item 添加到队尾,无返回值;dequeue()
:从队首移除数据项,返回值为队首数据项,队列被修改;isEmpty()
:测试是否空队列,返回值为布尔值size()
:返回栈中有多少个数据项
【操作样例】
Stack Operation | Stack Contents | Return Value |
---|---|---|
q.isEmpty() | [] | True |
q.enqueue(4) | [4] | |
q.enqueue(‘dog’) | [‘dog’,4] | |
q.enqueue(True) | [True,‘dog’,4] | |
q.size() | [True,‘dog’,4] | 3 |
q.isEmpty() | [True,‘dog’,4] | False |
q.enqueue(8.4) | [8.4,True,‘dog’,4] | |
q.dequeue() | [8.4,True,‘dog’] | 4 |
q.dequeue() | [8.4,True] | ‘dog’ |
q.size() | [8.4,True] | 2 |
1.2 Python实现ADT Queue
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
"""判断队列是否为空"""
return self.items == []
def enqueue(self, item):
"""进入队列,复杂度O(n)"""
self.items.insert(0, item)
def dequeue(self):
"""出队列,复杂度O(1)"""
return self.items.pop()
def size(self):
"""队列大小"""
return len(self.items)
2 队列的应用
2.1 热土豆(约瑟夫问题)算法
问题描述:
小孩围成一个圈,将土豆依次传递给相邻的人,在某个时间点,游戏暂停并且手里持有土豆的人出局,游戏继续直至剩下一个人。
算法:
-
用队列来实现热土豆问题的算法,参加游戏的人名列表,以及传土豆次数num,算法返回最后剩下的人名
-
模拟程序采用队列来存放所有参加游戏的人名,按照传递土豆方向从队首排到队尾,游戏时,队首始终是持有土豆的人
-
模拟游戏开始,只需要将队首的人出队,随即再到队尾入队,算是土豆的一次,传递传递了num 次后,将队首的人移除,不再入队如此反复,直到队列中剩余1
【代码】
class 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(["Bill", "David", "Susan", "Jane", "Kent", "Brad"], 6)) # Kent
2.2 打印任务算法
题目描述: 多人共享一台打印机。在一个实验室内,每一个小时内,平均在场10名学生,每名学生在这一个小时内平均发起两次打印,每次打印的1~20页(概率相同)。打印机打印模式有两种,以草稿模式打印,每分钟打印10页;以标准模式打印(打印质量较好),每分钟打印5页。那么怎么设定打印机的模式,使得大家在不会等太久的情况下提升打印质量?
题目分析: 这种题可以看出来是不能直接给出结论的,需要一次次模拟程序,不断执行,通过对比结果而得出最终结论。首先来简单看一下题目,分析几个关键点。问题是如何设置打印机的模式,使得不会等太久,并且打印质量高。
-
打印机的模式:标准模式和草稿模式,这两种模式其实是对应了打印机的速度和打印质量。
-
使得大家不会等太久:也就是大家的等待时间计算问题。等待时间,就是你提交了任务开始起(从这里开始等待),到打印机开始打印(打印机开始打印你的任务)。你提交了任务起,也就是你生成文档任务的时间,打印开始打印就是打印机开始下一个任务。而生成文档任务,也有学生人数和每人生成文档的概率等要素,这里需要把生成的任务,放入队列,而开始打印取决于打印机的状态,是空闲还是忙,打印过程就是时间流逝,当然也取决于文档的页数,也是概率问题。
-
打印质量高:其实就是打印机的模式问题,速度问题。和第一点一样。
通过上述分析可以抽取出几个属性。打印机的打印速度,当前状态(是否有任务),打印过程,开始打印任务;任务的生成时间,任务的页数,任务的等待时间;任务生成。因此可以模拟一个小时的打印过程,输出结果
所以可以大致抽象出打印机和任务两个对象。打印机,有打印速度,当前打印任务和剩余打印时间三个属性,打印过程,当前状态和开始打印三个方法。
【代码】
import random
class Queue:...
class Printer(object):
def __init__(self, ppm):
"""初始化打印的打印模式"""
self.pagerate = ppm # 打印速度,每分钟多少页
self.currentTask = None # 当前状态,是否有任务
self.timeRemaining = 0 # 如果有任务,当前任务剩余时间
def tick(self):
"""打印过程"""
if self.currentTask is not None:
self.timeRemaining -= 1 # 如果当前有任务,则进行打印(打印1秒)
if self.timeRemaining <= 0: # 并计算任务剩余时间
self.currentTask = None
def busy(self):
"""打印机是否忙"""
if self.currentTask is not None:
return True
else:
return False
def startNext(self, newtask):
"""开始打印下一个任务,计算任务剩余时间,这里按秒进行计算,需要进行单位换算"""
self.currentTask = newtask
self.timeRemaining = newtask.getPages() * 60 / self.pagerate
class Task(object):
"""
初始化任务生成时间
同等概率,随机指定任务的页数1-20页
"""
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():
"""20个任务 / 3600秒, 平均每秒产生1/180个任务"""
num = random.randrange(1, 181)
if num == 180:
"""每秒 1/180 概率产生新任务"""
return True
else:
return False
def simulation(newSeconds, pagePerMinute):
labprinter = Printer(pagePerMinute)
printQueue = Queue()
waitingtimes = []
# 时间流逝
for currentSecond in range(newSeconds):
if newPrintTask():
"""如果新任务产生"""
task = Task(currentSecond) # 产生新任务
printQueue.enqueue(task) # 打印队列加入新任务
if (not labprinter.busy()) and (not printQueue.isEmpty()):
"""如果打印机空闲且打印队列不为空"""
nexttask = printQueue.dequeue() # 打印队列弹出下个任务
waitingtimes.append(nexttask.waitTime(currentSecond)) # 记录新任务等待时间
labprinter.startNext(nexttask) # 打印机开始下个任务
labprinter.tick() # 若有任务,打印1秒
averageWait = sum(waitingtimes)/len(waitingtimes)
print('Average Wait {:6.2f} secs {:3d} tasks remaining.'.format(averageWait, printQueue.size()))
# 1 小时, 每分钟打印 10 页
for i in range(10):
simulation(3600, 10)
Average Wait 20.28 secs 0 tasks remaining.
Average Wait 22.43 secs 0 tasks remaining.
Average Wait 8.47 secs 0 tasks remaining.
Average Wait 15.77 secs 0 tasks remaining.
Average Wait 28.85 secs 0 tasks remaining.
Average Wait 0.47 secs 0 tasks remaining.
Average Wait 7.73 secs 0 tasks remaining.
Average Wait 11.82 secs 0 tasks remaining.
Average Wait 23.21 secs 0 tasks remaining.
Average Wait 19.95 secs 0 tasks remaining.
# 1 小时, 每分钟打印 5 页
for i in range(10):
simulation(3600, 5)
Average Wait 75.43 secs 0 tasks remaining.
Average Wait 38.35 secs 1 tasks remaining.
Average Wait 44.29 secs 2 tasks remaining.
Average Wait 125.79 secs 1 tasks remaining.
Average Wait 308.58 secs 3 tasks remaining.
Average Wait 69.22 secs 0 tasks remaining.
Average Wait 27.62 secs 0 tasks remaining.
Average Wait 81.05 secs 0 tasks remaining.
Average Wait 24.33 secs 0 tasks remaining.
Average Wait 91.05 secs 0 tasks remaining.