一 初識堆
堆 數據結構是一種數組,它可以視為一顆完全二叉樹。如下圖:
圖中的樹是數組,A={16, 14, 10, 8, 7, 9, 3, 7},圈內表示數值,圈外紅色的數字表示數組的下標。
array_size是數組的大小(此時是8),heap_size是構建堆的元素的多少。滿足heap_size<= array_size
給定某結點的下表i,其父結點下標為PARENT(i), 左兒子下標為LEFT(i), 右兒子下標為RIGHT(i)。滿足
PARENT(i) = int((i-1)/2) ; LEFT(i) = 2*i+1; RIGHT(i)=2*i+2
最大堆滿足:對於任何結點(除根節點)PARENT(i) > i
最小堆滿足:對於任何結點(除根節點)PARENT(i) <i
葉子節點(下標):int(i/2), int(i/2)+1......array_size
下面的介紹以最大堆為例
二 保持堆的性質
最大堆的性質為 PARENT(i) > i,因此對於特定的結點,應滿足比左右兒子都大
、
在上圖中下標為1的結點值為2,左孩子為4,右孩子為1,1結點比左孩子小,就讓1結點和3結點數值換過來。若此時4結點大於4,就把1結點和4結點數值換過來。
參考程序:
void MAX_HEAPIFY(int *A, int heap_size, int i) //i 為待處理保持性質的結點下標 { int l = 2 * i + 1; //左孩子 int r = 2 * i + 2; //右孩子 int largest = i; if(l < heap_size && A[largest] < A[l]) // 左孩子數值大 { largest = l; } if(r < heap_size && A[largest] < A[r]) //右孩子數值大
{ largest = r; } if(largest != i) { int tmp = A[i]; A[i] = A[largest]; A[largest] = tmp; MAX_HEAPIFY(A, heap_size, largest); //再從數值大的結點繼續往下遞歸處理 } }
三 構建堆
從底向上使得每個結點保持堆的性質就可以構建堆,因為葉子節點就自己,無兒女,因此從倒數第一個非葉子節點開始依次往上構建,直到樹根位置,而葉子節點開始的位置是int(i/2),因此第一個令其保持性質的結點下標為int((i-1)/2)。接着是int((i-1)/2-1)一直到0為止。下圖表示了構建堆的詳細過程:
圖中有9個結點,int(9/2)= 4, 從4-1=3開始(依次是4 3 2 1 0)。注意是從后往前,為何不是從前往后,顯然如果是上圖中,從0開始,是6和3交換,我們知道在最大堆中根是最大的,可是從前往后的話,最大值7在無出頭之日。
參考程序:
void BUILD_MAX_HEAP(int *A, int array_size, int heap_size) { int i; for (i=array_size/2-1; i>=0; i--) { MAX_HEAPIFY(A, heap_size, i); } }
四 堆排序
最大堆把最大值排到了首個位置A[0],這是如果把最大值和最后一個值A[heap_size-1]換過來,再使A[0]保持堆的性質,再使heap_size自建。重復以上過程,就是個非遞減排序。下圖是一個事例過程:
參考程序:
void HEAP_SORT(int *A, int array_size, int heap_size) { int tmp; BUILD_MAX_HEAP(A, array_size, heap_size); while(heap_size > 1) { tmp = A[0]; A[0] = A[heap_size-1]; A[heap_size] = tmp; heap_size--; MAX_HEAPIFY(A, heap_size, 0); } }
測試
#include <iostream> #include <vector> using namespace std; void maxHeapify(int A[], int lens, int i) { if (A == NULL || i >= lens) return; int l = 2 * i + 1; int r = 2 * i + 2; int largest = i; if (l < lens || A[i] > A[largest]) largest = l; if (r < lens && A[r] > A[largest]) largest = r; if (largest != i) { int tmp = A[largest]; A[largest] = A[i]; A[i] = tmp; maxHeapify(A, lens, largest); } } void buildHeap(int A[], int lens) { if (A == NULL || lens <= 0) return; int mid = (lens-2) / 2; for (int i = mid; i >= 0; --i) maxHeapify(A, lens, i); } void heapSort(int A[], int lens) { buildHeap(A, lens); while (lens > 1) { int tmp = A[lens-1]; A[lens-1] = A[0]; A[0] = tmp; --lens; maxHeapify(A, lens, 0); } } void tranverse(int A[], int lens) { if (A == NULL || lens <= 0) return; for (int i = 0; i < lens; ++i) cout << A[i] << " "; cout << endl; } int main() { int A[] = {3, 2, 8, 7, 0, 11}; int lens = sizeof(A) / sizeof(*A); tranverse(A, lens); // buildHeap(A, lens); // tranverse(A, lens); heapSort(A, lens); tranverse(A, lens); }
五 堆操作
1.HEAP_MAXIMUM(A) 找出最大的元素
int HEAP_MAXIMUM(int *A) { return A[0]; }
2.int HEAP_EXTRACT_MAX(int *A, int heap_size) 找出堆中最大元素,並刪除,保持堆
int HEAP_EXTRACT_MAX(int *A, int heap_size) { int max; max = A[0]; A[0] = A[heap_size-1]; heap_size--; MAX_HEAPIFY(A, heap_size, 0); return max; }
3. void HEAP_INCREASE_KEY(int *A, int heap_size, int i, int key) 第i個位置出若key比原來大,就改成key,保持堆
void HEAP_INCREASE_KEY(int *A, int heap_size, int i, int key) { int tmp; if(A[i] > key) { printf("The key is smaller than A[i]"); } else { A[i] = key; while(i>=1 && A[(i-1)/2] < A[i]) { tmp = A[i]; A[i] = A[(i-1)/2]; A[(i-1)/2] = tmp; i = (i-1)/2; MAX_HEAPIFY(A, heap_size, i); } } }
4.void MAX_HEAP_INSERT(int *A, int array_size, int heap_size, int key) 堆中插入元素
void MAX_HEAP_INSERT(int *A, int array_size, int heap_size, int key) { array_size++; heap_size++; A[heap_size] = -32768; HEAP_INCREASE_KEY(A, heap_size, heap_size-1, key); }