Fork me on GitHub

二叉堆

堆的概念

  • 如果有一个关键码的集合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
2
3
4
5
6
7
8
9
#pragma once
#include <stdio.h>

#define MAX_SIZE 100

typedef struct Heap{
int array[MAX_SIZE];
int size;
}Heap;
堆的初始化
1
2
3
4
5
6
7
8
void HeapInit(Heap *pH,int arr[],int size)
{
for (int i = 0; i < pH->size; i++)
{
pH->array[i] = arr[i];
}
pH->size = size;
}
堆的创建
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//向下调整
void HeaoAdjustDown(Heap *pH,int root)
{
int parent = root;

while (1)
{
//当左孩子存在时
int left = parent * 2 + 1;
//判断左孩子是否越界
if (left >= pH->size)
{
return;
}

//当一定有右孩子时
int MaxChild = left;
if (parent*2+2<pH->size && pH->array[parent*2+2]>MaxChild)
{
MaxChild = parent * 2 + 2;
}

if (pH->array[parent]>pH->array[MaxChild])
{
return;
}

int temp = pH->array[parent];
pH->array[parent] = pH->array[MaxChild];
pH->array[MaxChild] = temp;

parent = MaxChild;
}
}

//创建堆
void HeapMake(Heap *pH)
{
for (int i = (pH->size-2)/2; i > 0; i--)
{
HeaoAdjustDown(pH,i);
}
}
堆的插入操作

堆的插入:在已经建成的最小堆的后面插入新元素,插入之后,当树中结点不满足堆的性质时,就需要对堆进行重新调整

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//升序
void HeapSort(int array[],int size)
{
//建立大堆
for ( int i = (size-2)/2; i > 0; i--)
{
ArrayAdjustDown(array,size,i);
}
//开始排序
for (int j = 0; j < size; j++)
{
int s = size - 1 - j;
Swap(array, array + s);
ArrayAdjustDown(array, size - 1 - j, 0);
}
}

查找最小(大)的前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
29
int *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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void TestHeap()
{
int array[] = { 53, 17, 78, 9, 45, 65, 87, 23, 31 };
int size = sizeof(array) / sizeof(int);

Heap heap;
HeapInit(&heap, array, size);
HeapMake(&heap);

printf("建堆完成\n");

//查找最小的前K个数
int array[] = { 1,4,9,4,5,2,7,8,5,3,6,6,2,3 };
int sz = sizeof(array) / sizeof(array[0]);

int *ret = TopK(array, sz, 10);

for (int i = 0; i < 10; i++)
{
printf("%d ",ret[i]);
}

}

本文标题:二叉堆

文章作者:LiuXiaoKun

发布时间:2018年08月28日 - 08:08

最后更新:2018年09月19日 - 08:09

原始链接:https://LiuZiQiao.github.io/2018/08/28/二叉堆/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%