1. 程式人生 > >簡單易懂的樹、堆講解

簡單易懂的樹、堆講解

樹、堆

 

樹:

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大的數。

  注:內容來源於《啊哈.演算法》