-
概念
二叉堆是一种特殊的堆,二叉堆是完全二元树或者是近似完全二元树。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
- 作用:增加存储效率
一个数插入到10000个数的数组中,在顺序排列结构的数组里,挨个比较的方法来确定新插入数的位置,最坏的情况得比较10000次;如果用二叉堆的结构,节点比较,最坏的情况则需要比较13次。
有时候,我们需要的仅仅是最大值(最小值),比如在A*寻路里面,取最小F值,就可以应用到二叉堆。
- 存储
二叉堆一般用数组来表示。例如,根节点在数组中的位置是0,第n个位置的子节点分别在2n+1和 2n+2。因此,第0个位置的子节点在1和2,1的子节点在3和4。以此类推。这种存储方式便於寻找父节点和子节点。
- 基本操作
#pragma once
#include<assert.h>
#include <string.h>
#include "assert.h"
#include <stdio.h>
void Swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
//向下调整
void AdjustDownBig(int array[], int size, int root)
{
int max = 0;
while (1)
{
int left = 2 * root + 1;
int right = 2 * root + 2;
if (left >= size)
{
return;
}
if (right < size && array + right > array + left)
{
max = right;
}
else
{
max = left;
}
if (array + root >= array + max )
{
return;
}
Swap(array + root, array + max);
root = max;
}
}
void AdjustDownSmall(int array[], int size, int root)
{
while (2 * root + 1 < size)
{
int min;
if (2 * root + 2 < size && array[2 * root + 2] < array[2 * root + 1])
{
min = 2 * root + 2;
}
else
{
min = 2 * root + 1;
}
if (array [root] <= array[min])
{
return;
}
Swap(array + root, array + min);
root = min;
}
}
//向上调整O(logN)
void AdjustUpBig(int array[], int size, int child)
{
while (child > 0)
{
int parent = (child - 1) / 2;
if (array + child <= array + parent)
{
return;
}
Swap(array + child, array + parent);
child = parent;
}
}
//------------------------------------------//
//创建堆及堆的操作
//建大堆
void CreateHeapBig(int array[], int size)
{
for (int i = (size - 2) / 2; i >= 0; i--)
{
AdjustDownBig(array, size, i);
}
}
//建小堆
void CreateHeapSmall(int array[], int size)
{
for (int i = (size - 2) / 2; i >= 0; i--)
{
AdjustDownSmall(array, size, i);
}
}
typedef struct Heap
{
int array[100];
int size;
}Heap;
void HeapInit(Heap* pH,int array[],int size)
{
assert(size <= 100);
memcpy(pH->array, array, sizeof(int)*size);
pH->size = size;
CreateHeapBig(pH->array, pH->size);
}
//取最值
int HeapTop(Heap* pH)
{
assert(pH->size > 0);
return pH->array[0];
}
//o(logn)
void HeapPop(Heap* pH)
{
assert(pH->size > 0);
pH->array[0] = pH->array[pH->size - 1];
pH->size--;
AdjustDownBig(pH->array, pH->size, 0);
}
void HeapPush(Heap* pH,int data)
{
assert(pH->size < 100);
pH->array[pH->size] = data;
pH->size++;
AdjustUpBig(pH->array, pH->size, pH->size - 1);
}
#include <stdio.h>
void test()
{
int array[] = { 53, 17, 78, 9, 45, 65, 87, 23, 31 };
int size = sizeof(array) / sizeof(int);
CreateHeapBig(array, size);
for (int i = 0; i < size; i++)
{
printf("%d ", array[i]);
}
printf("\n");
Heap heap;
HeapInit(&heap,array,size);
printf("%d\n", HeapTop(&heap));
HeapPush(&heap, 90);
printf("%d\n", HeapTop(&heap));
HeapPop(&heap);
printf("%d\n", HeapTop(&heap));
}
void test2()
{
int array[] = { 53, 17, 78, 9, 45, 65, 87, 23, 31 };
int size = sizeof(array) / sizeof(int);
CreateHeapSmall(array, size);
for (int i = 0; i < size; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
- 应用
海量数据TopK问题
100亿个数据中找出最大的前k个?
思路:用打擂台的思想,把100亿个数的前k个建一个小堆,然后用小堆的根节点的数据和其余的海量数据比较,如果比根节点大那么久替换根节点,并且小堆重新排序。否则继续和海量数据比较。
代码如下:由于电脑性能问题,只用10万数据测试。
#pragma once
#include "Heap.h"
#include <stdlib.h>
//海量数据TopK问题,
void TopK(int array[], int size, int k)
{
int *heap = (int*)malloc(sizeof(int)*k);
for (int i = 0; i < k; i++)
{
heap[i] = array[i];
}
CreateHeapSmall(heap, k);
for (int j = k; j < size; j++)
{
if (array[j] < heap[0])
{
continue;
}
else
{
heap[0] = array[j];
AdjustDownSmall(heap, k, 0);
}
}
for (int z = 0; z < k; z++)
{
printf("%d ", heap[z]);
}
printf("\n");
free(heap);
}
void test3()
{
int array[100000];
for (int i = 0; i < 100000; i++)
{
array[i] = i;
}
TopK(array, 100000, 10);
}