1. 程式人生 > >Binary Heap(二叉堆)

Binary Heap(二叉堆)

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

6. leetcode 刷題