证明
因为 n 是编号最大的结点,故 n 肯定是叶结点,且是编号最大的叶结点。故 n 的双亲 parent[n]=⌊n/2⌋ 也是编号最大的非叶结点。故区间 (parent[n],n] 范围内的结点编号都是叶结点。即 ⌊n/2⌋+1,⌊n/2⌋+2,⋯,n 。
对于包含 n 个元素的堆最多包含 ⌈n/2h+1⌉ 个高度为 h 的结点。
证明
归纳法证明如下:
对于高度为 0 的结点(即叶结点),因为堆是一棵完全二叉树,故叶结点个数 l 满足 l0=⌊(n+1)/2⌋=⌈n/20+1⌉ 。
假设对于高度为 h 的结点,满足结点数 lh=⌈n/2h+1⌉,由于堆是一棵完全二叉树,故对于高度为 h+1 的结点,有
lh+1=⌈lh/2⌉=⌈n/2(h+1)+1⌉
堆可分为最大堆和最小堆,二者分别满足最大堆性质和最小堆性质。
最大堆性质:除了根结点以外的所有结点 i 满足
A[parent(i)]≥A[i]
最小堆性质:除了根结点以外的所有结点 i 满足
A[parent(i)]≤A[i]
2. 堆排序
堆排序是原址的、不稳定的。以下仅以最大堆为例。
2.1 maxHeapify
该过程用于维护最大堆性质。即根据输入的结点 i 维护以 i 为根的堆的最大堆性质。
1 2 3 4 5 6 7 8 9 10 11 12 13
// 伪代码 maxHeapify(A, i) { l = left(i) // 左孩子结点 r = right(i) // 右孩子结点 largest = i // 记录左孩子、右孩子、根三者中值最大的一者 if l ≤ A.heapsize and A[l] > A[largest] largest = l if r ≤ A.heapsize and A[r] > A[largest] largest = r if largest != i swap(A[i], A[largest]) maxHeapify(A, largest) }
// 比较函数 template <typename T> boolcompare(const T & t1, const T & t2){ return t1 < t2; } // 最大堆 template <typename T> structMaxHeap { #ifndef _MAXHEAP_ #define _MAXHEAP_ #define ll long long #endif ll length; ll heapsize; MaxHeap() {} // 维护最大堆性质 voidmaxHeapify(T *A, ll i, bool (*cmp)(const T &, const T &)){ ll l = 2*i, r = 2*i + 1; ll largest = i; if(l <= heapsize && cmp(A[largest], A[l])) { largest = l; } if(r <= heapsize && cmp(A[largest], A[r])) { largest = r; } if(largest != i) { swap(A[i], A[largest]); maxHeapify(A, largest, cmp); } } // 构建最大堆 voidbuildMaxHeap(T *A, bool(*cmp)(const T &, const T &)){ heapsize = length; for(ll i = heapsize/2; i; --i) { maxHeapify(A, i, cmp); } } // 堆排序 voidheapSort(T *bA, T *eA, bool(*cmp)(const T &, const T &) = compare){ T *A = bA - 1; length = eA - bA; buildMaxHeap(A, cmp); for(ll i = length; i; --i) { swap(A[1], A[i]); heapsize--; maxHeapify(A, 1, cmp); } } };
3. 优先队列
以下优先队列以最大堆为底层实现。
3.1 maxHeapifyUp
该过程用于从当前结点向根结点的路径上维护最大堆性质。
1 2 3 4 5 6 7
// 伪代码 maxHeapifyUp(A, i) { p = parent(i) // 双亲结点 if p > 0and A[p] < A[i] swap(A[i], A[p]) maxHeapifyUp(A, p) }
由于堆高为 ⌊lgn⌋,故 maxHeapifyUp 复杂度为 O(lgn) 。
3.2 maxHeapifyDown
该过程用于从当前结点向叶结点的路径上维护最大堆性质。
1 2 3 4 5 6 7 8 9 10 11 12 13
// 伪代码 maxHeapifyDown(A, i) { l = left(i) // 左孩子结点 r = right(i) // 右孩子结点 largest = i // 记录左孩子、右孩子、根三者中值最大的一者 if l ≤ A.heapsize and A[l] > A[largest] largest = l if r ≤ A.heapsize and A[r] > A[largest] largest = r if largest != i swap(A[i], A[largest]) maxHeapifyDown(A, largest) }