例
最小堆
1
/ \
2 3
/ \ / \
4 5 6 7
上面的樹是 Min-Heap,因為根是樹中存在的所有節點中的最小值。樹中的所有節點都遵循相同的屬性。
最大堆
7
/ \
6 5
/ \ / \
4 3 2 1
上面的樹是 Max-Heap,因為根是樹中存在的所有節點中的最大值。樹中的所有節點都遵循相同的屬性。
Min-Heap 支援的操作
-
getMin()
- 它返回根元素。因為它是陣列中的第一個元素,我們可以檢索O(1)
中的最小元素 -
extractMin()
- 它從 heap 中刪除最小元素。從刪除元素後,樹必須滿足 Min-Heap 屬性,因此執行操作( 堆化 )以維護樹屬性。這需要O(logn)
-
dereaseKey()
- 它減少了 key 的值。這個操作的時間複雜度是O(logn)
-
insert()
- 金鑰總是插在樹的末尾。如果新增的金鑰不遵循堆屬性,那麼我們需要滲透,以便樹滿足堆屬性。這一步需要花費時間 4。 -
delete()
- 此步驟需要O(logn)
時間。要刪除鍵,我們首先需要將鍵值減小到最小值,然後提取此最小值。
堆的應用
- 堆排序
- 優先順序佇列
堆使用了許多圖形演算法,如 Dijkstra’s Shortest Path
和 Prim’s Minimum Spanning Tree
。
用 Java 實現
public class MinHeap {
int hArr[];
int capacity;
int heapSize;
public MinHeap(int capacity){
this.heapSize = 0;
this.capacity = capacity;
hArr = new int[capacity];
}
public int getparent(int i){
return (i-1)/2;
}
public int getLeftChild(int i){
return 2*i+1;
}
public int getRightChild(int i){
return 2*i+2;
}
public void insertKey(int k){
if(heapSize==capacity)
return;
heapSize++;
int i = heapSize-1;
hArr[i] = k;
while(i!=0 && hArr[getparent(i)]>hArr[i]){
swap(hArr[i],hArr[getparent(i)]);
i = getparent(i);
}
}
public int extractMin(){
if(heapSize==0)
return Integer.MAX_VALUE;
if(heapSize==1){
heapSize--;
return hArr[0];
}
int root = hArr[0];
hArr[0] = hArr[heapSize-1];
heapSize--;
MinHeapify(0);
return root;
}
public void decreaseKey(int i , int newVal){
hArr[i] = newVal;
while(i!=0 && hArr[getparent(i)]>hArr[i]){
swap(hArr[i],hArr[getparent(i)]);
i = getparent(i);
}
}
public void deleteKey(int i){
decreaseKey(i, Integer.MIN_VALUE);
extractMin();
}
public void MinHeapify(int i){
int l = getLeftChild(i);
int r = getRightChild(i);
int smallest = i;
if(l<heapSize && hArr[l] < hArr[i])
smallest = l;
if(l<heapSize && hArr[r] < hArr[smallest])
smallest = r;
if(smallest!=i){
swap(hArr[i], hArr[smallest]);
MinHeapify(smallest);
}
}
public void swap(int x, int y){
int temp = hArr[x];
hArr[x] = hArr[y];
hArr[y] = temp;
}
}