1. 程式人生 > >樹、堆

樹、堆

調整 view tex 數組 實現 二叉樹 末尾 簡寫 第k大的數

樹、堆

樹:

1、一課樹中的任意兩個結點有僅有唯一的一條路徑連通。
2、一棵樹如果有n個結點,那麽它一定恰好有n-1條邊。
3、在一棵樹中加一條邊將會構造一個回路。

滿二叉樹:二叉樹所有的葉結點都有同樣的深度。
深度為:n,結點數:2**n - 1

技術分享圖片

完全二叉樹

如果一棵二叉樹除了最右邊位置上有一個或者幾個葉結點缺少外,其他是豐滿的,那麽這樣的二叉樹就完全二叉樹。嚴格的定義是:若設二叉樹的高度為h,除h層外,其他各層(1~h-1)的結點數都達到最大個數,第n層從右向左連續缺若幹個結點,則這個二叉樹就是完全二叉樹。

技術分享圖片

技術分享圖片

通過上圖我們發現,如果完全二叉樹的一個父結點編號為k,那麽它左兒子的編號就是2*k,右兒子的編號就是2*k+1。如果已知兒子(左兒子或右兒子)的編號是x,那麽它父結點的編號就是x/2,註意這裏只取商的整數部分。
如果一顆完全二叉樹有N個結點,那麽這個完全二叉樹的高度為log2N,簡寫logN,即最多有logN層結點。完全二叉樹的最典型應用就是一個堆。

堆—神奇的優先隊列

技術分享圖片


最大堆:所有父節點都比兒子結點要大;
最小堆:所有父節點都比兒子結點要小;

技術分享圖片


很顯然最小的數就在堆頂,假設存儲這個堆的數組叫做h的話,最小數就是h[1]。接下來,我們將堆頂的數刪除。將新增加的數23放到堆頂。顯然加了新數後已經不符合最小堆的特性,我們需要將新增加的數調整到合適的位置。那如何調整呢?

技術分享圖片


向下調整!我們需要將這個數與它的兩個兒子2和5比較,選擇較小的一個與它交換,交換之後如下。

技術分享圖片


我們發展此時還是不符合最小堆的特性,因此還需要繼續向下調整。於是繼續將23與它的兩個兒子12和7比較,選擇較小一個交換,交換之後如下。

技術分享圖片


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

技術分享圖片


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

技術分享圖片
時間復雜度:logN

新增一個數:

新增一個數,只需要將新元素插入到末尾,再根據情況判斷新元素是否需要上移,直到滿足堆的特性為止。如果堆的大小為N(即有N個元素),那麽插入一個新元素所需要的時間為O(logN)。例如我們現在要新增一個數3

技術分享圖片

技術分享圖片

構造一個最小堆

技術分享圖片
當然目前這個棵樹仍然不符合最小堆的特性,我們需要繼續調整以3號結點為根的子樹,即將3號結點向下調整。

技術分享圖片
同理,繼續調整以2號結點為根的子樹,最後調整以1號結點為根的子樹。調整完畢之後,整棵樹就符合最小堆的特性了。

技術分享圖片

像這樣支持插入元素和尋找最大 (小)值元素的數據結構稱為優先隊列。如果使用普通隊列來實現這兩個功能,那麽尋找最大元素需要枚舉整個隊列,這樣的時間復雜度比較高。如果是已排序好的數據,那麽插入一個元素則需要移動很多元素,時間復雜度度依舊很高。而堆就是一種優先隊列的實現,可以很好地解決這兩種操作。

另外Dijkstr算法中每次找離源點最近的一個頂點也可以用堆來優化,使算法的時間復雜度降到O((M+N)logN)。堆還經常被用來求一個數列中第K大的數,只需要建立一個大小為K的最小堆,堆頂就是第K大的數。

樹、堆