數據結構與算法-隊列

什么是隊列谭胚?

隊列(queue)懈费,是先進先出(FIFO, First-In-First-Out)的線性表捞蛋。在具體應用中通常用鏈表或者數組來實現。隊列只允許在后端(稱為rear)進行插入操作洋幻,在前端(稱為front)進行刪除操作。

這是維基百科上對隊列的解釋翅娶,隊列和棧一樣文留,也是一種受限的線性表結構好唯。隊列有兩個操作,入隊出隊燥翅,隊列可以用數組鏈表來實現骑篙,用數組實現的稱為順序隊列,用鏈表實現的稱為鏈式隊列森书。


入隊和出隊操作

與棧不同的是靶端,隊列中有兩個指針,分別為隊列頭指針 head 和隊列尾指針 tail 凛膏。當 head == tail 時杨名,隊為空;當 tail == n 時猖毫,隊滿台谍,不能再加入新的元素。當元素出隊時吁断, head + 1趁蕊,當元素入隊時,tail + 1仔役。假設用數組實現隊的操作介衔,下面用一段Python代碼描述一下具體過程(自己寫的代碼,可能有不對的地方)骂因。

class Queue(object):

    def __init__ (self,head,tail,arr):
        self.head = head   #傳入的頭指針
        self.tail = tail   #傳入的尾指針
        self.arr = arr    #傳入的隊列
        self.len = len(arr)

    def enqueue (self,element):
        if (self.tail == self.len-1):  #判斷隊列是否滿
            print ("The queue is full!")
            return self.arr,self.head,self.tail
        self.arr[self.tail] = element  #在隊尾插入新的元素
        self.tail += 1#尾指針加 1
        print ("The element entered")
        return self.arr,self.head,self.tail  #返回新的隊列
    def dequeue (self):
        if (self.head == self.tail):  #判斷隊列是否為空
            print ("The queue is empty!")
            return self.arr,self.head,self.tail 
        headData = self.arr[self.head]
        print ("The head data is %d"%(headData))  #輸出隊頭元素
        self.head += 1#頭指針加 1
        return self.arr,self.head,self.tail  #返回新的隊列 

work = Queue(0,1,[1,2,0])
Arrin,Hin,Tin = work.enqueue(5)
for i in range(Tin-Hin):
    print (Arrin[Hin+i])   #打印入隊后新的隊列
#Arrout,Hout,Tout = work.dequeue()
#for j in range(Tout-Hout):
#   print (Arrout[Hout+j])  #打印出隊后新的隊列

仔細看一下炎咖,會發(fā)現。入隊和出隊寒波,head 指針tail 指針不斷移動的乘盼,當 tail == n 時 ,不管數組有沒有存儲空間俄烁,就不能再有新元素入隊了绸栅。前面學習數組的時候,遇到這種問題页屠,需要用數據搬移來解決粹胯,但是每一次搬移,時間復雜度都是 O(n)辰企,為了降低平均時間復雜度风纠,只有在tail ==n & head !=0 的情況下,才觸發(fā)一次數據搬移牢贸,具體代碼就不寫了竹观,畫張圖看一下就很清晰了。

隊列數據搬移.jpg


循環(huán)隊列

還有一種不需要數據搬移的隊列臭增,叫做循環(huán)隊列懂酱,它將隊列轉變成一種抽象的環(huán)形結構,如圖所示誊抛。

循環(huán)隊列.jpg

當新的元素入隊時列牺, tail 不會更新為 8,而是更新為 0拗窃,后面新入隊的元素一次放入 0瞎领、1、2并炮,當 tail 為 3 時默刚,隊滿。
具體怎么判斷堆滿呢逃魄,根據規(guī)律可得荤西,當 (tail + 1)%n == head 時,隊滿伍俘,這樣的話會浪費一個數組的存儲空間邪锌。具體實現代碼如下。

class Queue(object):

    def __init__ (self,head,tail,arr):
        self.head = head
        self.tail = tail
        self.arr = arr
        self.len = len(arr)

    def enqueue (self,element):
        if ((self.tail+1)%self.len == self.head):  #判斷隊列是否滿
            print ("The queue is full!")
            return self.arr,self.head,self.tail,self.len
        if (self.tail == self.len-1):
            self.arr[self.tail] = element
            self.tail = 0
        else :
            self.tail += 1
        print ("The element entered")
        return self.arr,self.head,self.tail,self.len
    def dequeue (self):
        if (self.head == self.tail):  #判斷隊列是否為空
            print ("The queue is empty!")
            return self.arr,self.head,self.tail,self.len
        headData = self.arr[self.head]
        print ("The head data is %d"%(headData))
        if (self.head == self.len-1):
            self.head = 0
        else:
            self.head += 1
        return self.arr,self.head,self.tail,self.len 

work = Queue(4,7,[1,2,3,4,5,6,7,8])
#Arrin,Hin,Tin,Lin = work.enqueue(5)
#for i in range(Lin-Hin+Tin):
#    if (Hin+i >=  Lin):
#        print (Arrin[Hin+i-Lin])
#    else:
#        print (Arrin[Hin+i])

Arrout,Hout,Tout,Lout = work.dequeue()
if (Tout < Hout):
    for j in range(Lout-Hout+Tout):
        if (Hout+j >= Lout):
            print (Arrout[Hout+j-Lout])
        else:
            print (Arrout[Hout+j])
else:
    for j in range(Tout-Hout):
        print (Arrout[Hout+j])

阻塞隊列和并發(fā)隊列

阻塞隊列 就是在阻塞的基礎上加入了阻塞操作癌瘾。就是觅丰,當隊為空時,出隊阻塞妨退,當有數據時阻塞才結束妇萄。當隊滿時,入隊阻塞咬荷,當隊列中有空閑位置時阻塞才結束冠句。

這就相當于定義了一個生產者-消費者模型,需要協調生產者和消費者的個數幸乒,來提高數據處理的效率。

例如罕扎,可以配置多個消費者來應對一個生產者聚唐,這樣會涉及到多線程的問題,多線程操作腔召,需要在數據入隊或出隊時加鎖杆查,同一時刻只允許一個存或者取操作。實際上宴咧,基于數組的循環(huán)隊列根灯,利用 CAS 原子操作,可以實現非常高效的并發(fā)隊列掺栅。

小結

隊列是一種先進先出的數據結構烙肺,可以用鏈表和數組實現,針對響應時間比較敏感的系統(tǒng)氧卧,數組更合適桃笙,但要設計好隊列的大小。對于大部分資源有限的場景沙绝,當沒有空閑資源時搏明,基本上都可以通過“隊列”來實現請求排隊

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末闪檬,一起剝皮案震驚了整個濱河市星著,隨后出現的幾起案子,更是在濱河造成了極大的恐慌粗悯,老刑警劉巖虚循,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異样傍,居然都是意外死亡横缔,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門衫哥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茎刚,“玉大人,你說我怎么就攤上這事撤逢√哦В” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵蚊荣,是天一觀的道長初狰。 經常有香客問我,道長妇押,這世上最難降的妖魔是什么跷究? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮敲霍,結果婚禮上俊马,老公的妹妹穿的比我還像新娘。我一直安慰自己肩杈,他們只是感情好柴我,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扩然,像睡著了一般艘儒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天界睁,我揣著相機與錄音觉增,去河邊找鬼。 笑死翻斟,一個胖子當著我的面吹牛逾礁,可吹牛的內容都是我干的。 我是一名探鬼主播访惜,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼嘹履,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了债热?” 一聲冷哼從身側響起砾嫉,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窒篱,沒想到半個月后焕刮,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡舌剂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年济锄,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霍转。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡荐绝,死狀恐怖,靈堂內的尸體忽然破棺而出避消,到底是詐尸還是另有隱情低滩,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布岩喷,位于F島的核電站恕沫,受9級特大地震影響,放射性物質發(fā)生泄漏纱意。R本人自食惡果不足惜婶溯,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望偷霉。 院中可真熱鬧迄委,春花似錦、人聲如沸类少。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硫狞。三九已至信轿,卻和暖如春晃痴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背财忽。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工倘核, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人定罢。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓笤虫,卻偏偏與公主長得像旁瘫,于是被迫代替她去往敵國和親祖凫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內容