数据结构与算法(7)——队列Queue
- 队列基础定义
队列:一种有次序的数据集合。其特点是新数据项的添加总发生在尾端(rear)而现存数据项的移除总发生在另一端(首端front),先进先出。
队列只有一个入口,一个出口。当数据加入队列,首先出现在队尾,随着首数据项的移除,它逐渐接近队首。不允许数据项直接插入队中,也不允许从中间移除数据项(可联想成水管流动,排队等情况)
- 抽象数据类型Queue类型-python
Queue() | 创建一个空队列对象,返回值为Queue对象 |
enqueue(item) | 将数据项item添加到队尾,无返回值 |
dequeue() | 从队首移除数据项,返回值为队首数据项,队列被修改 |
isEmpty() | 测试队列是否为空,返回值为bool类型 |
size() | 返回队列中数据项的个数 |
用python自定义一个队列:
1 class Queue: 2 def __init__(self): 3 self.items = [] 4 5 def isEmpty(self): 6 return self.items == [] 7 8 def enqueue(self, item): #复杂度O(n) 9 self.items.insert(0,item) 10 11 def dequeue(self): #复杂度O(1) 12 return self.items.pop() 13 14 def size(self): 15 return len(self.items) 16 Q = Queue() 17 Q.enqueue(0) 18 Q.enqueue(1) 19 Q.enqueue('a') 20 print(Q.size())
[OUT] 3 Process finished with exit code 0
- 队列的应用
- 热土豆问题(约瑟夫问题)
问题描述:传烫手的热土豆,鼓声停的时候,手里有土豆的小朋友需要出列。(不想敲字了,直接截图PPT)
分析:用队列来实现。用Queue存放所有参加游戏的人名,队首默认为拥有土豆的人,游戏开始,队首的人出列,然后随即到队尾入队,记为土豆的一次传递,传了num次后,将队首的人移除,不再入队,如此反复,直到队列中只剩一个人。
代码:
1 from queue1 import Queue 2 3 def hotPotato(namelist, num): 4 simqueue = Queue() 5 6 for name in namelist: 7 simqueue.enqueue(name) 8 9 while simqueue.size() >1: 10 for i in range(num): 11 simqueue.enqueue(simqueue.dequeue()) #一次传递 12 13 simqueue.dequeue() 14 return simqueue.dequeue() 15 print(hotPotato(['a','b','c','d','e','f','g'],7))
d
Process finished with exit code 0
2.模拟打印任务(这部分内容结合数学建模知识)
问题:多人共享一台打印机,采用先到先服务的策略来执行打印任务。(直接截图条件)问题是怎么设定打印机的模式,让大家都不会等太久的前提下,尽量提高打印质量?
问题建模:首先需要确定打印机3个对象的属性:1.打印任务属性——是提交时间和打印页数,2.打印队列属性——是就队列性质的打印任务,3.打印机属性——打印速度和是否繁忙。然后确定打印任务的生成概率为:1/180(每秒),打印页数1~20之间概率相等,随机取即可。
一个简单的打印机任务模拟代码:
1 from queue1 import Queue 2 import random 3 4 class Printer: #建立打印机属性 5 def __init__(self,ppm): 6 self.pagerate = ppm #打印速度 7 self.currentTask = None #打印任务 8 self.timeRemaining = 0 #当前正在打印的任务还剩的时间 9 10 def tick(self): 11 ''' 12 打印1秒,即如果当前有打印作业,任务倒计时减1,当减到小于0,则当前任务结束了 13 ''' 14 if self.currentTask !=None: 15 self.timeRemaining = self.timeRemaining - 1 16 if self.timeRemaining <= 0: 17 self.currentTask = None 18 19 def busy(self): 20 ''' 21 判断打印机是否繁忙 22 ''' 23 if self.currentTask != None: 24 return True 25 else: 26 return False 27 28 def startNext(self,newtask): 29 ''' 30 开始打印新作业 31 :param newtask: 新的打印作业 32 :return: 33 ''' 34 self.currentTask = newtask #将当前打印对象设置为新的作业 35 self.timeRemaining = newtask.getPages() * 60 / self.pagerate #计算作业需要打印多久,/s为单位 36 37 class Task: #建立打印任务属性 38 def __init__(self,time): 39 self.timestamp = time #生成时间戳 40 self.pages = random.randrange(1,21) #随机数指定打印作业页数 41 def getStamp(self): #返回生成时间戳 42 return self.timestamp 43 def getPages(self): #返回页数 44 return self.pages 45 def waitTime(self,currentime): 46 return currentime - self.timestamp #这个打印任务被等了多少时间 47 48 def newPrintTask(): 49 num = random.randrange(1,181) #以1/180概率生成作业 50 if num == 180: 51 return True 52 else: 53 return False 54 55 def simulation(numSeconds, pagesPerMinute): 56 ''' 57 模拟打印机 58 :param numSeconds: 模拟多长时间(一段时间长度) 59 :param pagesPerMinute:打印机设定模式,即打印速度,每分钟多少页 60 :return: 61 ''' 62 labpriter = Printer(pagesPerMinute) 63 printQueue = Queue() #准备一个打印队列 64 waitingtimes = [] #等待时间 65 for currentSecond in range(numSeconds): 66 if newPrintTask(): #如果需要打印新任务,生成打印作业 67 task = Task(currentSecond) #记录开始打印时间戳 68 printQueue.enqueue(task) #记录打印任务队列 69 if (not labpriter.busy()) and (not printQueue.isEmpty()): #如果打印机空闲,且打印任务存在 70 nexttask = printQueue.dequeue() 71 waitingtimes.append(nexttask.waitTime(currentSecond)) 72 labpriter.startNext(nexttask) 73 labpriter.tick() 74 averageWait= sum(waitingtimes)/len(waitingtimes) 75 print('平均等待 %6.2f 秒,还有 %3d 打印任务剩余'%(averageWait,printQueue.size())) 76 for i in range(10): 77 simulation(36000,5)
[out] 平均等待 201.74 秒,还有 2 打印任务剩余 平均等待 151.27 秒,还有 1 打印任务剩余 平均等待 191.02 秒,还有 1 打印任务剩余 平均等待 269.86 秒,还有 0 打印任务剩余 平均等待 276.02 秒,还有 2 打印任务剩余 平均等待 226.06 秒,还有 1 打印任务剩余 平均等待 74.98 秒,还有 3 打印任务剩余 平均等待 151.76 秒,还有 0 打印任务剩余 平均等待 122.95 秒,还有 0 打印任务剩余 平均等待 119.68 秒,还有 0 打印任务剩余 Process finished with exit code 0