-
堆的概念及结构
如果有一个关键码的集合K= {k0,k1,k2, .... kn-1}, 把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: Ki<= K2i+1且Ki<= K2i+2(Ki>= K2i+1且Ki>= K2i+2)i=0, 1, 2...则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
-
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;堆总是一棵完全二叉树。
-
堆的向下调整算法
给一个数组,逻辑上看做一颗完全二 叉树。通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
-
堆的创建
下面我们给出一个数组,这个数组逻辑上可以看做一颗完全= 叉树,但是还不是一个堆, 现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。
具体实现代码如下:
-
Heap.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* _array;
int _size;
int _capacity;
}Heap;
void HeapInit(Heap* pHeap);
void HeapCreate(Heap* pHeap, HPDataType* a, int n);
void HeapDestroy(Heap* pHeap);
void HeapPush(Heap* pHeap, HPDataType x);
void HeapPop(Heap* pHeap);
HPDataType HeapTop(Heap* pHeap);
int HeapSize(Heap* pHeap);
int HeapEmpty(Heap* pHeap);
void HeapSort(HPDataType* a, int n);//堆排序
void HeapPrint(Heap* pHeap);
-
Heap.c
#include "Heap.h"
void Swap(HPDataType* pd1, HPDataType* pd2)
{
HPDataType d = *pd1;
*pd1 = *pd2;
*pd2 = d;
}
void HeapInit(Heap* pHeap)
{
assert(pHeap);
pHeap->_array = NULL;
pHeap->_size = 0;
pHeap->_capacity = 0;
}
void AdjustDown(int* a, int n, int root)//大堆的向下调整
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
//选左右孩子中较大的
if (child + 1 < n &&a[child + 1] > a[child])//默认child为最大的,如果child+1为最大,child就要加1才能变为最大
{
child++;
}
if (a[parent] < a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//void AdjustDown(int* a, int n, int root)//小堆的向下调整
//{
// int parent = root;
// int child = parent * 2 + 1;
// while (child < n)
// {
// //选左右孩子中较小的
// if (child + 1 < n &&a[child + 1] < a[child])
// {
// child++;
// }
// if (a[parent] >a[child])
// {
// Swap(&a[parent], &a[child]);
// parent = child;
// child = parent * 2 + 1;
// }
// else
// {
// break;
// }
// }
//}
void AdjustUp(int* a, int n, int child)//向上调整
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapCreate(Heap* pHeap, HPDataType* a, int n)
{
assert(pHeap);
pHeap->_array = (HPDataType*)malloc(sizeof(HPDataType)*n);
memcpy(pHeap->_array, a, sizeof(HPDataType)*n);
pHeap->_size = pHeap->_capacity = n;
for (int i = (n - 2) / 2; i >= 0; i--)
{
AdjustDown(pHeap->_array, pHeap->_size, i);
}
}
void HeapDestroy(Heap* pHeap)
{
assert(pHeap);
free(pHeap->_array);
pHeap->_array = NULL;
pHeap->_size = 0;
pHeap->_capacity = 0;
}
void HeapPush(Heap* pHeap, HPDataType x)
{
assert(pHeap);
size_t newcapacity = pHeap->_capacity == 0 ? 3 : pHeap->_capacity * 2;
if (pHeap->_size == pHeap->_capacity)
{
pHeap->_capacity =(HPDataType)realloc(pHeap->_array, sizeof(HPDataType)*newcapacity);
pHeap->_capacity = newcapacity;
}
pHeap->_array[pHeap->_size++] = x;
AdjustUp(pHeap->_array, pHeap->_size, pHeap->_size - 1);
}
void HeapPop(Heap* pHeap)
{
assert(pHeap);
Swap(&pHeap->_array[0], &pHeap->_array[pHeap->_size - 1]);//一定要记得取地址!!!
pHeap->_size--;
AdjustDown(pHeap->_array, pHeap->_size, 0);
}
HPDataType HeapTop(Heap* pHeap)
{
assert(pHeap);
return pHeap->_array[0];
}
int HeapSize(Heap* pHeap)
{
assert(pHeap);
return pHeap->_size;
}
int HeapEmpty(Heap* pHeap)
{
assert(pHeap);
return pHeap->_size == 0 ? 0 : 1;
}
void HeapSort(HPDataType* a, int n)
{
assert(a);
for (int i = (n - 2) / 2; i >= 0; i--)//建堆
{
AdjustDown(a, n, i);
}
int end = n - 1;
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
end--;
}
}
void HeapPrint(Heap* pHeap)
{
assert(pHeap);
for (int i = 0; i < pHeap->_size; i++)
{
printf("%d ", pHeap->_array[i]);
}
printf("\n");
}
-
Test.c
# include "Heap.h"
HeapTest()
{
int a[] = { 27, 15, 19, 18, 28, 34, 65, 49, 25, 37 };
Heap hp;
HeapInit(&hp);
HeapCreate(&hp, a, sizeof(a) / sizeof(HPDataType));
HeapPrint(&hp);
HeapPush(&hp, 70);
HeapPush(&hp, 45);
HeapPrint(&hp);
HeapPop(&hp);
HeapPrint(&hp);
HeapPop(&hp);
HeapPrint(&hp);
HeapPop(&hp);
HeapPrint(&hp);
}
int main()
{
HeapTest();
system("pause");
return 0;
}
堆里面还有一个常见的top k问题:
假设要找100万个数里面的top100,应该如何完成这一操作?
这时候,我们可以建一个100数大小的堆,因为要找前100个数,我们第一想到的是要建大堆,但是实际上建大堆只能选出其中最大的一个,假设第一个数就是最大的,剩下的所有数就进不了堆,所以我们应该建的是小堆,因为建小堆,大的数才能够进来,那么相反,找最小的100个数建的就是大堆了。因为建大堆,小的数才能够进来,就能找到最小的100个数了。