一分鐘瞭解堆的基本操做

基本操做

任何一個數據結構,無非就是增刪改查四大類:git

功能 方法 時間複雜度
offer(E e) O(logn)
poll() O(logn)
無直接的 API 刪 + 增
peek() O(1)

這裏 peek() 的時間複雜度很好理解,由於堆的用途就是可以快速的拿到一組數據裏的最大/最小值,因此這一步的時間複雜度必定是 O(1) 的,這就是堆的意義所在。github

那麼咱們具體來看 offer(E e)poll() 的過程。api

offer(E e)

好比咱們新加一個 0 到剛纔這個最小堆裏面:數組

那很明顯,0 是要放在最上面的,但是,直接放上去就不是一棵徹底二叉樹了啊。。數據結構

因此說,學習

  • 咱們先保證加了元素以後這棵樹仍是一棵徹底二叉樹,
  • 而後再經過 swap 的方式進行微調,來知足堆序性。

這樣就保證知足了堆的兩個特色,也就是保證了加入新元素以後它仍是個堆spa

那具體怎麼作呢:3d

Step 1.

先把 0 放在最後接上,別一上來就想着上位;code

OK!總算先上岸了,而後咱們再一步步往上走。blog

這裏「可否往上走」的標準在於:
是否知足堆序性

也就是說,如今 5 和 0 之間不知足堆序性,那麼交換位置,換到直到知足堆序性爲止

這裏對於最小堆來講的堆序性,就是小的數要在上面

Step 2. 與 5 交換

此時 0 和 3 不知足堆序性了,那麼再交換。

Step 3. 與 3 交換

還不行,0 還比 1 小,因此繼續換。

Step 4. 與 1 交換

OK!這樣就換好了,一個新的堆誕生了~

總結一下這個方法:

先把新元素加入數組的末尾,再經過不斷比較與 parent 的值的大小,決定是否交換,直到知足堆序性爲止。

這個過程就是 siftUp(),源碼以下:

時間複雜度

這裏不難發現,其實咱們只交換了一條支路上的元素,

也就是最多交換 O(height) 次。

那麼對於徹底二叉樹來講,除了最後一層都是滿的,O(height) = O(logn)

因此 offer(E e) 的時間複雜度就是 O(logn) 啦。

poll()

poll() 就是把最頂端的元素拿走。

對了,沒有辦法拿走中間的元素,畢竟要 VIP 先出去,小弟才能出去。

那麼最頂端元素拿走後,這個位置就空了:

咱們仍是先來知足堆序性,由於比較容易知足嘛,直接從最後面拿一個來補上就行了,先放個傀儡上來。

Step1. 末尾元素上位

這樣一來,堆序性又不知足了,開始交換元素。

那 8 比 7 和 3 都大,應該和誰交換呢?

假設與 7 交換,那麼 7 仍是比 3 大,還得 7 和 3 換,麻煩。

因此是與左右孩子中較小的那個交換。

Step 2. 與 3 交換

下去以後,還比 5 和 4 大,那再和 4 換一下。

Step 3. 與 4 交換

OK!這樣這棵樹總算是穩定了。

總結一下這個方法:

先把數組的末位元素加到頂端,再經過不斷比較與左右孩子的值的大小,決定是否交換,直到知足堆序性爲止。

這個過程就是 siftDown(),源碼以下:

時間複雜度

一樣道理,也只交換了一條支路上的元素,也就是最多交換 O(height) 次。

因此 offer(E e) 的時間複雜度就是 O(logn) 啦。

那以上就是有關堆的基本操做啦!對於堆,還有一個比較特別的操做,就是 heapify(),這是一個很神奇的操做,至於神奇在何處、爲何它能作到、它是怎麼作到的,咱們下一篇文章再說~

若是你喜歡這篇文章,記得給我點贊留言哦~大家的支持和承認,就是我創做的最大動力,咱們下篇文章見!

我是小齊,紐約程序媛,終生學習者,天天晚上 9 點,雲自習室裏不見不散!

更多幹貨文章見個人 Github: https://github.com/xiaoqi6666...

相關文章
相關標籤/搜索