堆(heap)也被稱爲優先隊列(priority queue)。隊列中允許的操作是先進先出(FIFO),在隊尾插入元素,在隊頭取出元素。而堆也是一樣,在堆底插入元素,在堆頂取出元素,但是堆中元素的排列不是按照到來的先後順序,而是按照一定的優先順序排列的。這個優先順序可以是元素的大小或者其他規則。如圖一所示就是一個堆,堆優先順序就是大的元素排在前面,小的元素排在後面,這樣得到的堆稱爲最大堆。最大堆中堆頂的元素是整個堆中最大的,並且每一個分支也可以看成一個最大堆。同樣的,我們可以定義最小堆,如圖二所示。
來源:http://blog.qiji.tech/archives/4855
堆的存儲
堆可以看成一個二叉樹,所以可以考慮使用二叉樹的表示方法來表示堆。但是因爲堆中元素按照一定的優先順序排列,因此可以使用更簡單的方法——數組——來表示,這樣可以節省子節點指針空間,並且可以快速訪問每個節點。堆得數組表示其實就是堆層級遍歷的結果,如下圖所示:
來源:http://rock3.info/blog/category/algorithm-and-data-structures/
這樣對於每一個下標爲i的節點,其左子節點在下標爲2*i的位置,其右子節點在下標爲2*i+1的位置,而其父節點在下標爲 floor{i / 2},其中i從1開始。通過數組的存儲方式,可以通過計算下標,直接獲取到相關節點。
typedef struct heap
{
int capacity;
int size;
int *arr;
} Heap;
#define MIN -1
Heap* CreateEmptyHeap (int max) {
Heap *heap = (Heap*)malloc(sizeof(Heap));
if (heap == NULL) {
printf("out of space!\n");
return NULL;
}
heap->capacity = max;
heap->size = 0;
heap->arr = (int*)malloc(sizeof(int) * (heap->capacity + 1));
heap->arr[0] = MIN;
return heap;
}
以最小堆爲例,說明堆得插入、刪除等操作。
插入
堆還可以看成一個完全二叉樹,每次總是先填滿上一層,再在下一層從左往右依次插入。堆的插入步驟:
- 將新元素增加到堆的末尾
- 按照優先順序,將新元素與其父節點比較,如果新元素小於父節點則將兩者交換位置。
- 不斷進行第2步操作,直到不需要交換新元素和父節點,或者達到堆頂
- 最後通過得到一個最小堆
通過將新元素與父節點調整交換的操作叫做上濾(percolate up)。
來源:http://blog.csdn.net/tuke_tuke/article/details/50357939
Heap* Insert (Heap* heap, int val) {
if (IsFull(heap)) {
printf("heap is full!\n");
return heap;
}
// percolate up new element
int i;
for (i = ++heap->size; heap->arr[i] > val; i /= 2)
heap->arr[i] = heap->arr[i/2];
heap->arr[i] = val;
return heap;
}
bool IsFull (Heap* heap) {
return (heap->capacity == heap->size);
}
刪除
堆的刪除操作與插入操作相反,插入操作從下往上調整堆,而刪除操作則從上往下調整堆。
- 刪除堆頂元素(通常是將堆頂元素放置在數組的末尾)
- 比較左右子節點,將小的元素上調。
- 不斷進行步驟2,直到不需要調整或者調整到堆底。
上述調整的方法稱爲下濾(percolate down)。
來源:http://blog.csdn.net/tuke_tuke/article/details/50357939
bool IsEmpty (Heap* heap) {
return (heap->size == 0);
}
Heap* DeleteMin (Heap* heap) {
if (IsEmpty(heap)) {
printf("empty heap!\n");
return NULL;
}
int minElement = heap->arr[1];
int lastElement = heap->arr[heap->size--];
int i, childIndex;
for (i = 1; 2*i <= heap->size; i = childIndex)
{
childIndex = 2*i;
//get the bigger child node
if (heap->arr[childIndex] != heap->size
&& heap->arr[childIndex] > heap->arr[childIndex+1])
childIndex++;
if (lastElement > heap->arr[childIndex])
heap->arr[i] = heap->arr[childIndex];
else
break;
}
heap->arr[i] = lastElement;
return heap;
}