堆数据结构
堆(heap),是一种特殊的数据结构。之所以特殊,因为堆的形象化是一个棵完全二叉树,并且满足任意节点始终不大于(或者不小于)左右子节点(有别于二叉搜索树Binary Search Tree)。其中,前者称为小顶堆(最小堆,堆顶为最小值),后者为大顶堆(最大堆,堆顶为最大值)。然而更加特殊的是,通常使用数组去存储堆,而不是二叉树。
/**
* Heap is a sepcial complete binary tree(CBT)
* Heap sketch is a CBT, but stored in Array
* 9 ---> maxtop 7 7
* / \ / \ / \
* / \ / \ / \
* 7 8 4 8 4 5
* / \ / \ / \ / /
* / \ / \ / \ / /
* 5 3 2 4 3 5 3 6
*
* (1) (2) (3)
* maxtop heap not maxtop(mintop) not heap(CBT)
*/
具体而言,对于长度为N的数组A中的任意一个元素i(0 <= i < N / 2),其左右子节点为i * 2 + 1和i * 2 + 2。以大顶堆为例,该堆始终满足:
A[i] >= A[i2 + 1] && A[i] >= A[i2 + 2]。(下文不做特殊说明均以大顶堆为例)
如何创建一个堆呢?对于给定的一个数组arr[]和长度n,一般使用在数组上就地堆化。堆化的过程实际是调整堆的过程。有自上到下和自下到上两种堆化方法。
1)自上到下构建堆
// Method 1
// Create (Initialize) Heap, from top to bottom
public void heapCreate(int arr[], int n) {
int i; // from top to bottom
for (i = 1; i < n; heapAdjust(arr, i++)) ;
}
自上到下很好理解,首先假设当前数组arr的前i个元素已经满足堆性质(arr[0]只有一个元素肯定满足);然后每次在数组之后添加一个元素A[i],使得新的数组A[0~i]满足堆化性质,其中heap_adjust可以调整当前节点i使其满足堆化;直到i为n时,调整完毕,即堆化完毕。其中heap_adjust如下:
public void heapAdjust(int arr[], int c) {
// c - children, p - parent
int p = (c - 1) >> 1, temp;
// heap adjust from maxtop, from bottom to top
for (; arr[p] < arr[c]; c = p, p = (c - 1) >> 1) {
temp = arr[p];
arr[p] = arr[c];
arr[c] = temp;
}
} // Time O(logn)
调整代码也很好理解,首先找到当前节点c的父节点p,如果arr[p]<arr[c],则交换,然后继续寻找p的父节点进行调整;否则,调整完毕(因为前文已经假设,数组的前i-1已经满足堆化,新添一个元素i进行调整)。
很有意思,构建堆时使用自上到下,那么调整堆就必须自下到上。
2)自下到上构建堆
// Method 2
// Create (Initialize) Heap, from bottom to top
void heap_create(int arr[], int n) {
int i; // from bottom to top
for (i = (n >> 1) - 1; i > -1; headjust(arr, i--, n)) ;
}
此处自下到上的“下”,并不是数组最后一个元素,而是最后一个父节点n/2-1。也就是以父节点为线索,逐渐调整该节点的子节点。因此,此处heap_adjust是自上到下的调整,如下
public void heapAdjust(int arr[], int p, int n) {
// c - children, p - parent
int maxid = p, temp;
// heap_adjust for maxtop, from top to bottom
for (; p < (n >> 1); p = maxid)