1. 什麼是隊列?
隊列是一種有次序的數據集合,其特徵是新數據項的添加總髮生在一端(通常稱爲“尾端”),而現存數據項的移除總髮生在另一端(通常稱爲“首front”端)。
新加入的數據項必須在數據集末尾等待,而等待時間最長的數據項則是隊首。這種次序安排的原則稱爲先進先出(FIFO:First-in-first-on)
隊列僅有一個入口和一個出口。不允許數據項直接插入隊中,也不允許從中間移除數據項。
2.抽象數據類型Queue
抽象數據類型Queue是一個有次序的數據集合。數據項僅添加到尾端,而且僅從首端移除。Queue具有FIFO的操作次序
2.1 抽象數據類型Queue定義的操作
2.2操作樣例
2.3用python實現ADT Queue
採用List來容納Queue的數據項,將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):
return self.items.insert(0,item)#將數據項item添加到隊尾
def dequeue(self):
return self.items.pop()#從隊首移除數據項
def size(self):
return len(self.items)#返回對列的大小
2.4 Queue的應用:打印任務
一個具體的實例配置如下:
一個實驗室,在任意的一小時內,大約有10名學生在場。這一小時中,沒人會發起兩次左右的打印,每次1~20頁
打印機的性能:
以草稿模式打印的話,每分鐘10頁;以正常模式打印的話,打印質量好,但速度降爲每分鐘5頁。
問題是:
怎麼設定打印機的模式,讓大家都不會等太久的前提下儘量提高打印質量?
2.4.1 如何對問題建模
對問題進行抽象,確定相關的對象和過程。拋棄那些對問題是指沒有關係的學生性別、年齡、打印機型號、打印內容、紙張大小等等衆多細節。
2.4.1.1 對象:打印任務、打印隊列、打印頁數
打印任務的屬性:提交時間、打印頁數
打印隊列的屬性:具有FIFO性質的打印任務隊列
打印機屬性:打印速度、是否忙
2.4.1.2 過程:
- 生成和提交打印任務
- 確定生成概率:實例爲每小時會有10個學術提交20個作業,這樣,概率是每180秒會有一個作業生成並提交,概率爲每秒1/180
- 確定打印頁數:實例是1-20頁,那麼就是1-20頁之間的概率相同
- 確定生成概率:實例爲每小時會有10個學術提交20個作業,這樣,概率是每180秒會有一個作業生成並提交,概率爲每秒1/180
- 實施打印
- 當前的打印作業:正在打印的作業
- 打印結束倒計時:新作業開始打印時開始倒計時,回0表示打印完畢,可以處理下一個作業。
2.4.1.3 模擬時間
- 統一的時間框架:以最小單位(秒)均勻流逝的時間,設定結束時間
- 同步所有過程:在一個時間單位裏,對生成打印任務和實施打印兩個過程各處理一次。
2.4.2 模擬流程
(1)創建打印隊列對象
(2)時間按照秒的單位流逝
按照概率生成打印作業,加入打印隊列
如果打印機空閒,且隊列不空,則取出隊首作業打印,記錄此作業等待時間
如果打印機忙,則按照打印速度進行1秒打印
如果當前作業打印完成,則打印機進入空閒
(3)時間用盡,開始統計平均等待時間
(4)作業的等待時間
生成作業時,記錄生成的時間戳
開始打印時,當前時間減去生成時間即可
(5)作業的打印時間
生成作業時,記錄作業的頁數
開始打印時,頁數除以打印速度即可
2.4.3 python實現
#隊列
import random
class Queue:
def __init__(self):
self.items=[]#產生一個對列
def isEmpty(self):
return self.items==[] #判斷對列是否爲空
def enqueue(self,item):
return self.items.insert(0,item)#將數據項item添加到隊尾
def dequeue(self):
return self.items.pop()#從隊首移除數據項
def size(self):
return len(self.items)#返回對列的大小
#打印機
class Printer:
def __init__(self,ppm):#ppm打印速度
self.pagerate=ppm
self.currentTask=None#當前打印任務
self.timeRemaining=0#任務倒計時
def tick(self):#打印1秒
if self.currentTask !=None:
self.timeRemaining=self.timeRemaining-1
if self.timeRemaining<=0:#倒計時小於等於0
self.currentTask=None#當前沒有打印任務
def busy(self):#判斷打印機是否正忙
if self.currentTask !=None:#當currentTask不爲None,表示打印機正忙,返回True
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():
num=random.randrange(1,181)
if num==180:
return True #1/180概率生成作業
else:
return False
#模擬
def simulation(numSeconds,pagesPerMinute):
labprinter=Printer(pagesPerMinute)
printQueue=Queue()#產生一個空隊列
waitingtimes=[]#等待時間
for currentSecond in range(numSeconds):
#時間流逝
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()
averageWait=sum(waitingtimes)/len(waitingtimes)
print("Average Wait %6.2f secs %3d tasks remaining"%(averageWait,printQueue.size()))
for i in range(10):
simulation(3600,5)
按照5ppm、1小時的設定,模擬運行10次。結果如下:
上述結果中,總平均等待時間爲133.603秒,最長的平均等待爲586.28秒,最短的平均等待14.60秒。還有兩次模擬中有作業沒有開始打印。
按照10ppm、1小時的設定,模擬運行10次。結果如下:
上述結果中,總平均等待時間爲23.535秒,最長的平均等待爲56.58秒,最短的平均等待9.37秒。而且所有的作業都打印了。
3.雙端隊列(Deque)
雙端隊列Deque是一種有次序的數據集。跟隊列相似,其兩端可以稱作“首”“尾”端,但deque中數據項既可以從隊首加入,也可以從隊尾加入;數據項也可以從兩端移除。某種意義上說,雙端隊列繼承了棧和隊列的能力。但雙端隊列並不具有內在的LIFO或者FIFO特性。如果用雙端隊列來模擬棧或者隊列,需要由使用者自行維護操作的一致性。
3.1 抽象數據類型Deque定義的操作:
3.2 操作樣例
3.3 python實現ADT Deque
採用List實現,List下標0作爲deque的尾端,List下標-1作爲deque的首端。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):#將item加入隊首
self.items.append(item)
def addRear(self,item):#將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)#返回雙端隊列中包含數據項的個數
3.4 雙端隊列的應用——“迴文詞”判定
"迴文詞"指正讀和反讀都一樣的詞,如radar、madam、toot、“上海自來水來自上海”、“山東貨花生花落東山”
利用雙端隊列解決“迴文詞”判斷問題
首先將需要判定的詞從隊尾加入deque,再從兩端同時移除字符判定是否相同,直到deque中剩下0個或一個字符
python實現代碼
class Deque:
def __init__(self):
self.items=[]#建立空的雙端隊列
def isEmpty(self):
return self.items==[]#判斷雙端隊列是否爲空
def addFront(self,item):#將item加入隊首
self.items.append(item)
def addRear(self,item):#將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)#返回雙端隊列中包含數據項的個數
#迴文詞判定
def palchecker(aString):
adeque=Deque()#創建一個空的雙端隊列
#將需要判定的詞從隊尾加入到deque
for ch in aString:
adeque.addRear(ch)
isHuiWen=True
#從兩端同時移除字符判定是否相同
while adeque.size()>1 and isHuiWen:
left=adeque.removeFront()#移除隊首的數據項
right=adeque.removeRear()#移除隊尾的數據項
if left!=right:
isHuiWen=False
return isHuiWen
print(palchecker('radar'))
print(palchecker('raddarb'))