Binary Heap(二叉堆)
阿新 • • 發佈:2019-01-02
Binary Heap (二叉堆)
1. 二叉堆的定義
在電腦科學中,二叉堆是二叉樹形狀的堆結構。二叉堆是最常見的實現優先順序佇列的方法,它與優先順序佇列緊密相連,一起應用到諸多地方,在很多主流語言的標準演算法庫中都能看到它們的身影。同時它也是很多演算法中需要用到的底層資料結構,能夠快速地掌握這些已有的標準庫和類,能夠很高效地實現諸多演算法。
- 其空間複雜度和相關操作的時間複雜度如下表所示:
Algorithm | Average | Worst Case |
---|---|---|
Space | O(n) | O(n) |
Search | O(n) | O(n) |
Insert | O(1) | O(log n) |
Delete | O(log n) | O(log n) |
Peek | O(1) | O(1) |
2. 二叉堆的性質
二叉堆的是一個具有堆性質的完全二叉樹,因此二叉堆具備了完全二叉樹和堆的全部特性,實現起來非常容易,也是程式設計師必須要掌握的基本資料結構。
最大堆示例
完全二叉樹: 完全二叉樹是由滿二叉樹而引出來的。對於深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹。只有最下面的兩層結點度能夠小於2,並且最下面一層的結點都集中在該層最左邊的若干位置。完全二叉樹是一種效率很高的資料結構,通常採用陣列形式儲存,可以快速計算出一個節點的父子節點,同時不需要額外儲存索引資訊。
堆性質: 堆性質是指樹中的任意節點的取值,均比其位元組點的取值大,稱為最大堆。(或小,稱為最小堆)這個是基於二叉堆獲得節點取值對應關係的重要依據,是體現優先順序的地方。
3. C語言實現二叉堆的基本操作(指標操作)
#include <stdio.h>
#include <stdlib.h>
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0]))
int int_less(const void *key1, const void *key2) {
if (*((int *) key1) < *((int *) key2)) return 1;
else return 0;
}
void int_swap(void *a, void *b) {
int temp;
temp = *(int*)a;
*(int*)a = *(int*)b;
*(int*)b = temp;
}
void **array_to_data(int *array, int n) {
void **data = (void **) malloc(sizeof(void *) * n);
for (int i = 0; i < n; i++) {
data[i] = (array+i);
}
return data;
}
void output_int_array(int *data, int n) {
int *p = data;
for (int i=0; i<n; i++) {
printf("%d, ", *(p++));
}
printf("\n");
}
void output_int_data(void **data, int n) {
void **p = data;
for (int i=0; i<n; i++) {
printf("%d, ", *((int *) *(p++)));
}
printf("\n");
}
// adds an element to a heap
void push_heap(void **data, int *len, const int capacity, void *x,
int (*compare)(const void *key1,
const void *key2),
void (*swap)(void *a, void *b)) {
if (*len >= capacity) return;
// from bottom shift to top
int n = *len; *len = *len + 1;
data[n] = x;
while (n != 0) {
int p = (n-1) / 2;
if (! compare(data[p], data[n])) {
swap(data[p], data[n]);
n = p;
} else {
break;
}
}
}
// creates a heap out of a range of elements
void make_heap(void **data, const int n,
int (*compare)(const void *key1,
const void *key2),
void (*swap)(void *a, void *b)) {
int len = 0;
while (len < n) {
push_heap(data, &len, n, data[len], compare, swap);
}
}
// removes the largest element from a max heap
void *pop_heap(void **data, int *len,
int (*compare)(const void *key1,
const void *key2),
void (*swap)(void *key1, void *key2)) {
if (*len <= 0) return NULL;
// from top to bottom
int n = 0;
void *x = data[n]; *len = *len - 1;
data[n] = data[*len];
int c, c1, c2;
while ((c1 = 2*n + 1) < *len) {
c = c1;
if (((c2 = 2 * (n+1)) < *len) &&
! compare(data[c1], data[c2])) {
c = c2;
}
if (! compare(data[n], data[c])) {
swap(data[n], data[c]);
n = c;
} else {
break;
}
}
return x;
}
void test_make_heap() {
int array[] = {3, 5, 8, 2, 7, 4, 1, 6, 9};
int n = ARRAY_LENGTH(array);
output_int_array(array, n);
void **data = array_to_data(array, n);
output_int_data(data, n);
make_heap(data, n, int_less, int_swap);
output_int_data(data, n);
int len = n;
void *x = NULL;
while (len > 0) {
x = pop_heap(data, &len, int_less, int_swap);
printf("%d, ", *(int *)x);
}
printf("\n");
free(data);
}
int main(void) {
test_make_heap();
return 0;
}
4. 標準演算法庫與工具類
5. 二叉堆的應用
二叉堆非常適合解決在連續的插入和刪除操作中,快讀定位最大值或最小值的問題。在很多貪心演算法中,都需要取得當前時刻的最大值或最小值,二叉堆與貪心演算法的結合尤其緊密。
1). 堆排序
2). 優先順序佇列
3). Top K Problem