介绍
堆,英文名heap。这个数据结构是来自于堆排序的,其结构近似于一颗完全二叉树,通常用数组来存储表示。堆是实现堆排序以及优先队列的基础数据结构。根据任何子树根节点都比左右孩子大或小,堆可以分为最大堆(又叫大根堆)和最小堆(又叫小根堆)。最大堆具有性质:
任何结点大于等于它左右孩子结点。最小堆则恰好相反:任何结点小于等于它左右孩子结点。
最小堆和最大堆如图所示:
![](https://img-my.csdn.net/uploads/201211/07/1352265671_1438.jpg)
如何建堆
对于给定的数组序列A,其长度为length,将它调整成符合堆的性质,成为建堆过程。下面以建立最大堆为例,讨论如何建堆。
由于最大堆需要满足任何结点都大于等于左右孩子结点,那么定义操作max_heapify。其作用是将结点调整为大于等于左右孩子结点,也就是从该节点,其左孩子,其右孩子中选取最大值作为该结点值,并与之原先结点进行交换,使得满足最大堆的性质。例如某结点值为5,其左右孩子分别为10, 3。经过max_heapfiy操作之后,该结点为10,其左右孩子为5, 3。如果该结点是与其左孩子交换的,继续调整左孩子,使得左孩子也满足该性质。这样递归调整下去,则使得整个结构满足最大堆。
由于数组的从[length/2] 到 length所对应的元素最后肯定是堆的叶子结点。则从[length/2]到1这些元素,必然是叶子结点的祖先,其中还肯定会有父亲结点。那么从[length、2]到1的过程中,利用max_heapify操作,从下到上动态调整堆,那么整个过程就是建堆的思路了。其伪代码如下
for i = [length/2] downto 1
max_heapify(A, i); // A 数组
时间复杂度分析
由于含有n个节点的完全二叉树最大高度为logn,则每一次调整时间复杂度为O(logn)。一共需要调整O(n)次,则时间复杂度为O(nlogn)。当然对于高度来说还不是最精确的计算,因为位于不同高度的结点,需要调整的最大次数是不一样的。因此还需要对不同高度的点进行讨论求和来计算精确的时间复杂度。
因为含有n个结点的堆最大高度为logn,而高度为h的堆最多包含
个结点。因此有如下计算方法:
![](http://images.cnitblog.com/blog/446173/201309/13231951-6cca53d77caf4937a4592083b6849890.png)
因此我们可以在线性时间内把一个无序数组构建成最大堆
实现代码
#include<stdio.h>
#define BLANK -9999
#define LENGTH 10
void max_heapify(int heap[], int i)
{
int left = 2*i;
int right = 2*i+1;
int largest = i;
if(left <= LENGTH && heap[left] > heap[i])
largest = left;
if(right <= LENGTH && heap[right] > heap[largest])
largest = right;
if(largest != i){
int temp = heap[largest];
heap[largest] = heap[i];
heap[i] = temp;
max_heapify(heap, largest);
}
}
void build_heap(int heap[])
{
for(int i=LENGTH/2; i>=1; i--)
max_heapify(heap, i);
}
void print_heap(int heap[])
{
for(int i=1; i<=LENGTH; i++)
printf("%d ", heap[i]);
}
int main(void)
{
int init_heap[LENGTH+1] = {BLANK, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7}; // 第0个位置不存元素
build_heap(init_heap);
print_heap(init_heap);
return 0;
}