資料結構——優先佇列和堆
優先佇列:
優先佇列也是一個佇列,他和普通佇列的區別是:普通佇列是先進先出,後進先出的;優先佇列是出隊順序和入隊順序無關,和優先順序相關。(如:醫院看病時,病情最嚴重的那個需要先看病)
實現優先佇列同樣底層也可以選擇多種資料結構,
選擇 普通的線性結構實現:出隊O(1),入隊O(n);
選擇 順序線性結構:入隊O(n),出隊O(1);
選擇 堆實現:入隊O(logn),出隊O(logn); 下面我們就看一下堆這種資料結構。
堆:
堆同樣可以通過多種底層實現,這裡我們主要看一下主流的,底層用二叉樹實現的堆也稱之為二叉堆。
特點:
二叉堆是一顆完全二叉樹(完全二叉樹:把元素順序排列成樹的形狀,即元素一層一層的排列,排滿當前層才排下一層)
堆中某個節點的值總是不大於其父親節點的值(根節點的值為最大值),這樣定義出來的叫最大堆,同理反過來也有最小堆。
實現:
由於是一顆完全二叉樹,可以相應的給每個節點標號(層序),將它們放到一個數組中。
儲存在陣列中之後,就有一個問題,就是如何根據一個節點找他的孩子以及父親節點,其實也很簡單,看如下公式:
如果從陣列下標為1開始存放:parent(i) =i/2 ; left child(i) = 2i ; right child = 2*i+1
如果從陣列下標為0開始存放:parent(i) =(i-1)/2 ; left child(i) = 2i+1 ; right child = 2*i+2
新增元素(Sift Up):
首先將待新增元素插入末尾位置如圖中52
但這樣打破了堆的性質,每個節點都必須大於其孩子節點,那麼接下來就將插入的節點和它的父親節點作比較,如果大於其父親節點,那麼交換位置,然後再和當前父親節點進行比較,如果父親節點大繼續交換,直到沒有父親節點,或者小於父親節點為止,上圖交換後如下:
取出元素Sift Dowm:
根據堆的性質只能取出最大的元素也就是堆頂的元素,同時取出後還需要保持堆的結構不被打破,那麼我們進行如下操作:
先將堆末尾的元素放到堆的開頭,如圖中元素16
現在將堆頂元素(16)和它的兩個孩子中較大的那個進行位置交換,然後再進行新一輪交換,直到沒有孩子或者大於它的孩子中較大的那一個,上圖操作完如下:
取出最大元素後,放入一個新的元素 replace:
可以直接將對頂元素替換以後進行 Sift Down,只需一次O(logn)的操作
j注:ava中的PriorityQueue底層用的是最小堆