二叉堆定義
二叉堆是一棵完全二叉樹,除最後一層外,每一層都是滿的,且最後一層的節點從左到右依次生長。
此處實現的是最小二叉堆。
任何一個父節點的關鍵碼總是小於、等於其子節點的關鍵碼。
對於節點 ,其左孩子爲 ,右孩子爲 ,父節點爲 。
import java.util.ArrayList;
import java.util.List;
public class BinaryHeap<T extends Comparable<? super T>> {
private List<T> list;
public BinaryHeap() {
list = new ArrayList<>();
list.add(null); // 索引爲 0 的節點爲哨兵,真正的節點數據從索引 1 處開始
}
// 節點數
public int size() {
return list.size() - 1;
}
...
}
插入元素
設待插入元素爲 e。
首先在最後一個位置創建一個空穴,並暫時用 e 填充它,然後執行上濾操作。
上濾
設空穴節點之父爲 p 。若 e 小於 p 的關鍵碼,則將 p 下沉至空穴處,空穴上升至 p 處。繼續此過程,直至 e 能夠放入空穴中。
注意:在執行上濾操作之前,首先將索引爲 0 的哨兵節點的關鍵碼設爲 e 。該哨兵節點可看成是二叉堆的根節點之父。這樣可使空穴最多上升至根節點(因爲根節點之父的關鍵碼爲 e ,不小於 e)。
// 插入元素
public void insert(T e) {
list.add(e); // 在末尾添加一個空洞,暫時用 e 填充它
int hole = size();
percolateUp(hole);
}
// 上濾
private void percolateUp(int hole) {
T e = list.get(hole);
list.set(0, e); // 哨兵
// 只要 e 比空洞的父節點小,便將父節點往下拉,然後空洞上升
while (e.compareTo(list.get(hole >> 1)) < 0) {
list.set(hole, list.get(hole >> 1));
hole >>= 1;
}
list.set(hole, e);
}
刪除最小元素
最小元素在二叉堆的根節點處。
刪除根節點後,將在根節點處產生一個空穴,且堆的大小將減少一。
可以首先刪除最後一個節點,並暫時用它填充空穴,然後執行下濾操作。
下濾
設最後一個節點(刪除操作之前)的關鍵碼爲 e 。
設空穴所在節點的最小孩子爲 c,若 c 的關鍵碼小於 e,則 c 上升至空穴處,空穴下沉至 c 處。重複此過程,直至 e 可放入空穴。
// 刪除最小元素
public T removeMinimum() {
// 堆爲空
if (size() < 1) {
return null;
}
T minimum = list.get(1); // 最小元素
T last = list.remove(size());
// 刪除之後,堆不爲空:原先堆至少有兩個節點
if (size() > 0) {
list.set(1, last);
percolateDown(1);
}
return minimum;
}
// 下濾
private void percolateDown(int hole) {
T e = list.get(hole);
int n = size();
while ((hole << 1) <= n) {
// 空穴節點之最小孩子:開始時設爲左孩子
int child = hole << 1;
// child 有右兄弟,且右兄弟比 child 還要小
if (child != n && list.get(child + 1).compareTo(list.get(child)) < 0) {
child++;
}
// 子節點比 e 要小,則子節點上升,空穴下降
if (list.get(child).compareTo(e) < 0) {
list.set(hole, list.get(child));
hole = child;
} else {
break;
}
}
list.set(hole, e);
}
構建堆
從最後一個內部節點()開始,依次對每個內部節點執行下濾操作:即,依次以每個內部節點爲根,構建一棵最小二叉堆。
時間複雜度爲 O(n) :可以通過計算虛線的最大條數來得到。
// 構建堆
public void buildHeap(List<T> list) {
this.list.clear();
this.list.add(null);
for (int i = 0; i < list.size(); i++) {
this.list.add(list.get(i));
}
// 從最後一個內部節點開始,執行下濾操作
for (int i = size() >> 1; i > 0; i--) {
percolateDown(i);
}
}