堆 神奇的優先佇列 上

2021-06-23 06:16:32 字數 2471 閱讀 5931

堆是什麼?是一種特殊的完全二叉樹,就像下面這棵樹一樣。

有沒有發現這棵二叉樹有乙個特點,就是所有父結點都比子結點要小(注意:圓圈裡面的數是值,圓圈上面的數是這個結點的編號,此規定僅適用於本節)。符合這樣特點的完全二叉樹我們稱為最小堆。反之,如果所有父結點都比子結點要大,這樣的完全二叉樹稱為最大堆。那這一特性究竟有什麼用呢?

假如有14

個數分別是99、

5、36、

7、22、

17、46、

12、2、

19、25、

28、1和

92。請找出這

14個數中最小的數,請問怎麼辦呢?最簡單的方法就是將這14個數從頭到尾依次掃一遍,用乙個迴圈就可以解決。這種方法的時間複雜度是o(14)

也就是o(n)。

[cpp]view plain

copy

for(i=1;i<=14;i++)    

現在我們需要刪除其中最小的數,並增加乙個新數23

,再次求這

14個數中最小的乙個數。請問該怎麼辦呢?只能重新掃瞄所有的數,才能找到新的最小的數,這個時間複雜度也是o(n)。假如現在有

14次這樣的操作(刪除最小的數後並新增乙個新數)。那麼整個時間複雜度就是o(14

2)即o(n2)。那有沒有更好的方法呢?堆這個特殊的結構恰好能夠很好地解決這個問題。

首先我們先把這個14

個數按照最小堆的要求(就是所有父結點都比子結點要小)放入一棵完全二叉樹,就像下面這棵樹一樣。

很顯然最小的數就在堆頂,假設儲存這個堆的陣列叫做h

的話,最小數就是

h[ 1]。接下來,我們將堆頂的數刪除,並將新增加的數23

放到堆頂。顯然加了新數後已經不符合最小堆的特性,我們需要將新增加的數調整到合適的位置。那如何調整呢?

向下調整!我們需要將這個數與它的兩個兒子2和5

比較,並選擇較小乙個與它交換,交換之後如下。

我們發現此時還是不符合最小堆的特性,因此還需要繼續向下調整。於是繼續將23

與它的兩個兒子12和

7比較,並選擇較小乙個交換,交換之後如下。

到此,還是不符合最小堆的特性,仍需要繼續向下調整直到符合最小堆的特性為止。

我們發現現在已經符合最小堆的特性了。綜上所述,當新增加乙個數被放置到堆頂時,如果此時不符合最小堆的特性,則將需要將這個數向下調整,直到找到合適的位置為止,使其重新符合最小堆的特性。

向下調整的**如下。

[cpp]view plain

copy

void

siftdown(

inti) 

//傳入乙個需要向下調整的結點編號i,這裡傳入1,即從堆的頂點開始向下調整

//如果發現最小的結點編號不是自己,說明子結點中有比父結點更小的 

if(t!=i)  

else

flag=1;//則否說明當前的父結點已經比兩個子結點都要小了,不需要在進行調整了

}  }  

我們剛才在對23

進行調整的時候,竟然只進行了

3次比較,就重新恢復了最小堆的特性。現在最小的數依然在堆頂為2

。之前那種從頭到尾掃瞄的方法需要14

次比較,現在只需要

3次就夠了。現在每次刪除最小的數並新增乙個數,並求當前最小數的時間複雜度是o(3)

,這恰好是

o(log

214)即

o(log

2n)簡寫為

o(logn)。假如現在有

1億個數(即

n=1億),進行

1億次刪除最小數並新增乙個數的操作,使用原來掃瞄的方法計算機需要執行大約1

億的平方次,而現在只需要1億

*log1

億次,即

27億次。假設計算機每秒鐘可以執行10億次,那原來則需要一千萬秒大約115天!而現在只要

2.7秒。是不是很神奇,再次感受到演算法的偉大了吧。

說到這裡,如果只是想新增乙個值,而不是刪除最小值又該如何操作呢?即如何在原有的堆上直接插入乙個新元素呢?只需要直接將新元素插入到末尾,再根據情況判斷新元素是否需要上移,直到滿足堆的特性為止。如果堆的大小為n

(即有n

個元素),那麼插入乙個新元素所需要的時間也是o(logn)。例如我們現在要新增乙個數3。

先將3與它的父結點25比較,發現比父結點小,為了維護最小堆的特性,需要與父結點的值進行交換。交換之後發現還是要比它此時的父結點5小,因此需要再次與父結點交換。至此又重新滿足了最小堆的特性。向上調整完畢後如下。

向上調整的**如下。

[cpp]view plain

copy

void

siftup(

inti) 

//傳入乙個需要向上調整的結點編號i

}  說了半天,我們忽略乙個很重要的問題!就是如何建立這個堆。我們周一接著說。

買了的朋友記得來啥單,還可以得到《啊哈!演算法》的t恤哦~~~ 

堆 神奇的優先佇列 上 經典

原文章 堆是什麼?是一種特殊的完全二叉樹,就像下面這棵樹一樣。有沒有發現這棵二叉樹有乙個特點,就是所有父結點都比子結點要小 注意 圓圈裡面的數是值,圓圈上面的數是這個結點的編號,此規定僅適用於本節 符合這樣特點的完全二叉樹我們稱為最小堆。反之,如果所有父結點都比子結點要大,這樣的完全二叉樹稱為最大堆...

啊哈!演算法 演算法11 堆 神奇的優先佇列(上)

堆是什麼?是一種特殊的完全二叉樹,就像下面這棵樹一樣。有沒有發現這棵二叉樹有乙個特點,就是所有父結點都比子結點要小 注意 圓圈裡面的數是值,圓圈上面的數是這個結點的編號,此規定僅適用於本節 符合這樣特點的完全二叉樹我們稱為最小堆。反之,如果所有父結點都比子結點要大,這樣的完全二叉樹稱為最大堆。那這一...

啊哈!演算法 演算法11 堆 神奇的優先佇列(上)

堆是什麼?是一種特殊的完全二叉樹,就像下面這棵樹一樣。有沒有發現這棵二叉樹有乙個特點,就是所有父結點都比子結點要小 注意 圓圈裡面的數是值,圓圈上面的數是這個結點的編號,此規定僅適用於本節 符合這樣特點的完全二叉樹我們稱為最小堆。反之,如果所有父結點都比子結點要大,這樣的完全二叉樹稱為最大堆。那這一...