數據結構:堆

  最近學習了數據結構裏的堆,和開始想的不一樣,原本以爲堆的實現和二叉樹一樣都要使用遞歸來寫,麻煩死了。

學了之後發現,堆這個東西和二叉樹有關係但又不一定用遞歸來寫。

 堆分爲大根堆,小根堆,定義貼在下面

大根堆要求根節點的關鍵字既大於或等於左子樹的關鍵字值,又大於或等於右子樹的關鍵字值
最小堆,是一種經過排序的完全二叉樹,其中任一非終端節點的數據值均不大於其左子節點和右子節點的值。

看定義基本抓瞎,還是看圖來的直觀。

這東西就是一個小根堆,根節點比左右孩子都小,左右孩子又比自己的孩子都小。

子子孫孫無窮盡也,對不起說串詞

和小根堆對應的大根堆是下面這貨

(圖片來自百度,侵刪)

可以看到每個節點都比自己的只有孩子大。

看起來很簡單,但是怎麼實現的。

首先咱們需要明確一個概念,邏輯結構和實際結構。

在邏輯上堆是二叉樹,但是實際實現堆的不一定是二叉樹這種數據結構。

我是以順序表作爲堆實現的,畢竟堆的優點是排序快只要logn次就能得到最大值,比如由100萬個數字只要排20次就得到最大值或者最小值,賊高效。

struct Heap 
{ 
HPDataType* _arry; 
size_t  _size; 
size_t  _capacity; 
}

假如你要實現大根堆,

 ,1.首先如果左右子樹都是有序的大根堆,只要和自己的左右孩子中比較大的比較,假如比自己大,交換兩節點的值,再往判斷交換位置後的左右孩子,如果比自己大,交換,直到左右孩子都比自己小或者最大的和自己相等,或者已經到樹的最底端就停止交換。這樣這棵樹就是有序的大根堆。

   但是日常生活中很少有數據這麼“整齊”,所以要換一種思路,首先一棵樹如果只有根節點,又可以認爲它本身是有序的,是大根堆或者小根堆,那麼,上面那種思想就可以使用了,

  因爲給的數組,可以看成完全二叉樹,首先從後往前數第一個非葉子節點和左右孩子進行調整,調整爲大根堆,數組的序號往前走繼續調整,直到走到數組序號0停止。

(把一個數組視爲二叉樹,則它的根與左右孩子的序號的關係爲:lchild == prent*2+1 ,rchild == lchld+1    )

如果插入一個新數據除了注意擴容外,其他的步驟和上面思想相同,因爲插入的數據是葉子節點,它和它的兄弟(如果有的話),父節點,滿足上面的關係(左右子樹都是有序的大根堆)

 但是頭刪除節點的步驟不一樣,如果直接刪除頭節點,會直接打亂整個堆的順序,如果重新建堆代價太大, 我們可以這麼做,首先把頭節點和最後一個葉子節點的值交換,再刪除末尾值,因爲沒有打亂堆的順序,根的左右節點依然是大根堆,所以只要再調整一次就能得到新的堆。

 其他的尾刪,尾插思想大同小異就不細說了,

(附:堆實現的代碼:c版本 https://blog.csdn.net/cat_want_fly/article/details/86501197

課外小知識:topk的實現;

所謂的topk問題舉個例子,就像一所體校裏有1000個學生10個班,有一天校長要找10個全校最壯的學生,那是一個班級挨着一個班級測試找到最壯的學生,還是每個班都選出10個最壯的出來,這些選出來的學生再比較,更高效。

 可以抽象有1000個數字,如何高效的找到最大的十個數字

   可以考慮用堆排序,用先用前十個數字構建一個小根堆,然後再插入新的數據,如果比堆頂的數值大,就替換堆頂的數字,進行堆排序,否則就跳過,直到遍歷一遍,堆裏面的數字就是1000個數裏最大的數字,

這種方法叫做最小堆法,像他這麼吊的方法還有3個 。局部淘汰法 分治法  Hash法 ,這裏就不寫了,有興趣的可以自己實現一下

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章