堆的概念
- 如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元
素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:
Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。 小堆(大堆)中:任一结点的关键码均小于(大于)等于它的左右孩子的关键码,位于堆顶结点的关键码最小(最大),从根节点到每个结点的路径上数组元素组成的序列都是递增(递减)的
堆存储在下标为0开始的数组中,因此在堆中给定下标为i的结点时:
(1)如果i=0,结点i是根节点,没有双亲节点;否则结点i的双亲结点为结点(i-1)/2
(2)如果2 i + 1 <= n - 1,则结点i的左孩子为结点2 i + 1,否则结点i无左孩子
(3)如果2 i + 2 <= n - 1,则结点i的右孩子为结点2 i + 2,否则结
点i无右孩子
堆的实现
将二叉树调整为最小堆的原理:
从最后一个非叶子结点开始调整,一直到根节点为止,将每个结点及其子树调整到满足小堆的性质即可
头文件的定义
1 |
|
堆的初始化
1 | void HeapInit(Heap *pH,int arr[],int size) |
堆的创建
1 | //向下调整 |
堆的插入操作
堆的插入:在已经建成的最小堆的后面插入新元素,插入之后,当树中结点不满足堆的性质时,就需要对堆进行重新调整1
2
3
4
5
6
7//插入
void HeapPush(Heap *pH,int data)
{
assert(pH->size < MAX_SIZE);
pH->array[pH->size++] = data;
HeapAdjustUp(pH, pH->size - 1);
}
堆的插入操作
堆的删除:删除时每次删除堆顶元素
具体方法:将最后一个元素顶替堆顶元素,将堆中元素个数减少一个,相当于将堆中最后一个元素删掉,此时堆结构可能破坏,在向下调整使其满足堆的性质1
2
3
4
5
6
7
8//删除
void HeapPop(Heap *pH)
{
pH->array[0] = pH->array[pH->size - 1];
pH->size--;
HeapAdjustUp(pH, 0);
}
堆排序
1 | //升序 |
查找最小(大)的前K个数
100亿个数中找出最小的前K个数(海量数据top K问题)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29int *TopK(int array[],int size,int k)
{
int *heapArray = (int *)malloc(k * sizeof(int));
assert(heapArray);
//搬前K个数
for (int i = 0; i < k; i++)
{
heapArray[i] = array[i];
}
//建立堆(大堆)
for ( int j = (k-2)/2; j >= 0; j--)
{
ArrayAdjustDown(heapArray,k,j);
}
//将第K个与堆中最大元素比较
for (int i = k; i < size; i++)
{
if (array[i] >= heapArray[0])
{
continue;
}
heapArray[0] = array[i]; //替换堆中最大元素
ArrayAdjustDown(heapArray,k,0);
}
return heapArray;
}
测试部分
1 | void TestHeap() |