堆是什麼?是一種特殊的完全二叉樹,就像下面這棵樹一樣。
有沒有發現這棵二叉樹有乙個特點,就是所有父結點都比子結點要小(注意:圓圈裡面的數是值,圓圈上面的數是這個結點的編號,此規定僅適用於本節)。符合這樣特點的完全二叉樹我們稱為最小堆。反之,如果所有父結點都比子結點要大,這樣的完全二叉樹稱為最大堆。那這一特性究竟有什麼用呢?
假如有14
個數分別是99、
5、36、
7、22、
17、46、
12、2、
19、25、
28、1和
92。請找出這
14個數中最小的數,請問怎麼辦呢?最簡單的方法就是將這14個數從頭到尾依次掃一遍,用乙個迴圈就可以解決。這種方法的時間複雜度是o(14)
也就是o(n)。
for(i=1;i<=14;i++)
//如果發現最小的結點編號不是自己,說明子結點中有比父結點更小的
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小,因此需要再次與父結點交換。至此又重新滿足了最小堆的特性。向上調整完畢後如下。
向上調整的**如下。
void siftup(int i) //傳入乙個需要向上調整的結點編號i
{ int flag=0; //用來標記是否需要繼續向上調整
if(i==1) return; //如果是堆頂,就返回,不需要調整了
//不在堆頂 並且 當前結點i的值比父結點小的時候繼續向上調整
while(i!=1 && flag==0)
{//判斷是否比父結點的小
if(h[ i]
說了半天,我們忽略乙個很重要的問題!就是如何建立這個堆。我們周一接著說。
買了的朋友記得來啥單,還可以得到《啊哈!演算法》的t恤哦~~~
啊哈!演算法 演算法11 堆 神奇的優先佇列(上)
堆是什麼?是一種特殊的完全二叉樹,就像下面這棵樹一樣。有沒有發現這棵二叉樹有乙個特點,就是所有父結點都比子結點要小 注意 圓圈裡面的數是值,圓圈上面的數是這個結點的編號,此規定僅適用於本節 符合這樣特點的完全二叉樹我們稱為最小堆。反之,如果所有父結點都比子結點要大,這樣的完全二叉樹稱為最大堆。那這一...
啊哈!演算法 1 1 快速排序
1.基本思路 就是選定乙個基準數,然後從兩端開始同時像中間探測,如果是要公升序排序,且選定第乙個為基準數的話,則當後面探測到比基準數小的就停下,然後就讓前面的走,前面當探測到比基準數大的就停下,然後交換,最後兩個探測針相遇時,就跳出迴圈,並且讓基準數放到中間去。2.注意事項 注意,在讓前面和後面探測...
《啊哈!演算法》第7章 神奇的樹
把n個元素建立乙個堆,首先將這n個結點以自頂向下 從左到右的方式從1到n編碼,這樣可以把n個結點轉換成一顆完全二叉樹 緊接著從最後乙個非葉子結點 結點編號為n 2 開始到根節點 結點編號為1 逐個掃瞄所有結點,根據需要將當前結點向下調整,直到以當前結點為根結點的子樹符合堆的特性。include in...