二叉堆是一種應用很廣的數據結構,今天,咱們就來簡單講講二叉堆。算法
往期回顧:數組
【算法與數據結構專場】BitMap算法基本操做代碼實現bash
二叉堆是一種特殊的堆。具備以下的特性:優化
根據第二條特性,咱們又能夠把二叉堆分紅兩類:ui
一、最大堆:父節點的值大於等於左右孩子節點的值。spa
二、最小堆:父節點的值小於等於左右孩子節點的值。3d
咱們把二叉堆的根節點稱之爲堆頂。根據二叉堆的特性,堆頂要嘛是整個堆中的最大元素,要嘛是最小元素。code
今天,咱們主要來說講二叉堆的三個主要操做:cdn
不過這裏須要注意的是,在二叉堆這種結構中,對於刪除一個節點,咱們通常刪的是根節點。
下面咱們以最小堆爲例子,來說講這些操做。
剛纔咱們說二叉堆具備徹底二叉樹的特性,所以,咱們在插入一個節點的時候,應該先保證節點插入後,它仍然是一顆徹底二叉樹,而後再來進行調整,使它知足二叉堆的另外一個特性。
因此,在插入的時候,咱們把新節點插到徹底二叉樹的最後一個位置。例如:
插入0。
以後咱們再來進行調整,調整的原則是:讓新插入的節點與它的父節點進行比較,若是新節點小於父節點,則讓新節點上浮,即和父節點交換位置。
上浮以後繼續和它的父節點進行比較,直到父節點的值小於或等於該節點,才中止上浮,即插入結束。例如:
0比5小,上浮。
0比2小於,上浮。
0比1小,上浮。
已經到達堆頂了,插入結束。
前面說了,刪除節點通常刪除的是根節點。
和插入同樣,因爲二叉堆具備徹底二叉樹的特性,由於刪除時候,首先咱們是要立刻恢復它具備徹底二叉樹的特性,因此咱們是採起這樣的策略:把根節點刪除以後,用二叉堆的最後一個元素頂替上來,而後在進行調整恢復。例如:
把0刪除了以後,5替換上來。
以後再來調整,調整的規則和插入差很少相似,採起下沉的策略:讓5和左右孩子節點相比較,若是左右節點有小於5的,則讓那個較小的孩子代替5的位置,而後5下沉。
下沉以後,5繼續和左右孩子相比,直到左右孩子都大於或等於5才結束。
如:5比1,3都大,1代替5的位置
5比4,2都大,2代替5的位置。
5已經不在具備左右孩子了,刪除結束。
所謂構建,就是給你一個有n個節點的無序的徹底二叉樹,而後把它構建成二叉堆。
剛纔咱們在刪除一個節點的時候,把最後一個元素補到根元素的位置上去,而後再讓這個元素依次下沉,實際上,在這個元素尚未下沉以前,它就能夠看做是一顆無序的徹底二叉樹了。
也就是說,要把一個無序的徹底二叉樹調整爲二叉堆,咱們可讓全部非葉子節點依次下沉。不過下沉的順序不是從根節點開始下沉(想一下相必你就 知道不能從根節點開始下沉),而是從下面的非葉子節點下稱,在依次往上。舉個例子:
對於這樣一顆無序的徹底二叉樹
8進行下沉。
接着,5進行下沉。
2沒問題,以後讓7進行下沉
調整完成,構建結束。
不過這裏須要說明的是,咱們二叉樹通常是採用鏈表的方式來實現的,但二叉堆咱們是採用數組的方式來存儲的。
若是知道了一個節點的位置,如何知道一個節點的左右孩子節點的位置呢?
這其實不難,根據徹底二叉樹的特色,假如一個節點的下標爲n,則能夠求得它左孩子的下標爲:2 * n+1;右孩子下標爲:2 * n+2。
下面是構建代碼的實現:
public class BinaryHeap {
//上浮操做,對插入的節點進行上浮
/**
*
* @param arr
* @param length :表示二叉堆的長度
*/
public static int[] upAdjust(int arr[], int length){
//標記插入的節點
int child = length - 1;
//其父親節點
int parent = (child - 1)/2;
//把插入的節點臨時保存起來
int temp = arr[child];
//進行上浮
while (child > 0 && temp < arr[parent]) {
//其實不用進行每次都進行交換,單向賦值就能夠了
//當temp找到正確的位置以後,咱們再把temp的值賦給這個節點
arr[child] = arr[parent];
child = parent;
parent = (child - 1) / 2;
}
//退出循環表明找到正確的位置
arr[child] = temp;
return arr;
}
/**
*/
/**
* 下沉操做,執行刪除操做至關於把最後
* * 一個元素賦給根元素以後,而後對根元素執行下沉操做
* @param arr
* @param parent 要下沉元素的下標
* @param length 數組長度
*/
public static int[] downAdjust(int[] arr, int parent, int length) {
//臨時保證要下沉的元素
int temp = arr[parent];
//定位左孩子節點位置
int child = 2 * parent + 1;
//開始下沉
while (child < length) {
//若是右孩子節點比左孩子小,則定位到右孩子
if (child + 1 < length && arr[child] > arr[child + 1]) {
child++;
}
//若是父節點比孩子節點小或等於,則下沉結束
if (temp <= arr[child])
break;
//單向賦值
arr[parent] = arr[child];
parent = child;
child = 2 * parent + 1;
}
arr[parent] = temp;
return arr;
}
/**
* 構建操做
*
* @param arr
*/
public static int[] buildHead(int[] arr,int length) {
//從最後一個非葉子節點開始下沉
for (int i = (length - 2) / 2; i >= 0; i--) {
arr = downAdjust(arr, i, length);
}
return arr;
}
}
複製代碼
本次講解到此結束。下篇繼續講解和堆有關的知識點。至於bitmap算法優化的那篇,會在以後給出。
獲取更多原創文章,能夠關注下個人公衆號:苦逼的碼農,我會不按期分享一些資源和軟件等。後臺回覆禮包送你一份時下熱門的資源大禮包,同時也感謝把文章介紹給更多須要的人