基本數據結構——線性結構(隊列、雙端隊列)

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頁之間的概率相同
  • 實施打印
    • 當前的打印作業:正在打印的作業
    • 打印結束倒計時:新作業開始打印時開始倒計時,回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'))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章