堆
堆再逻辑上可以看成一颗完全二叉树。
堆根据形状可以分为大根堆和小根堆。
故名思意,堆顶最小为小根堆,堆顶最大为大根堆,且根的其他子树也必须满足这一条件。
如图
小根堆:
大根堆:
堆的常用结论:
对于一个具有n个节点的完全二叉树,如果按照从上到下从左至右的数组顺序对所有节点从 0 开始编号,则对于序号为 i 的节点有如下结论:
- 若 i > 0,i 节点的父节点为 ( i - 1)/2; i == 0 则 i 为根节点,无父节点。
- 若 2i + 1 < n, 左孩子节点为 2i + 1, 2i + 1 >= n 则无左孩子。
- 若 2i + 2 < n,右孩子节点为 2i + 2,2i + 2 >= n 则无右孩子。
注意点:
降序建立小根堆,然后交换堆顶尾元素并刨去尾部元素,再进行向下调整建立小堆。
升序建立大根堆,然后交换堆顶尾元素并刨去尾部元素,再进行向上调整建立大堆。
堆结构体:
typedef int HpDataType;
typedef struct Heap
{
HpDataType* _a;
size_t _size;
size_t _capacity;
}Heap;
堆的向下调整算法
条件:左右子树都必须是堆
void AdjustDown(HpDataType* a, size_t n, int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
// 找出小的那个孩纸
if (child + 1 < n && a[child+1] < a[child]) //1处 改成 a[child + 1] > a[child]
{
++child;
}
// 1、孩纸比父亲小,则交换,继续往下调
// 2、孩纸比父亲大,则终止调整
if (a[parent] > a[child]) //此时是调小堆,若改成 < 并且改 1 处 则为调大堆
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
向上调整算法:
同上,可调大堆也可调小堆,下面的代码是调小堆。
void AdjustUp(int* a, 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* hp, HpDataType* a, size_t n)
{
hp->_a = (HpDataType*)malloc(sizeof(HpDataType)*n);
memcpy(hp->_a, a, sizeof(HpDataType)*n); //将数组a 中的元素copy到 hp->_a
hp->_size = n;
hp->_capacity = n;
// 建小堆
for (int i = (n-1-1)/2; i >= 0; --i)
{ //循环遍历每一个父亲节点,进行调整
AdjustDown(hp->_a, hp->_size, i);
}
堆的插入:
原理是先把堆调好,然后将新数值插入到堆尾,然后进行向上排序。
void HeapPush(Heap* hp, HpDataType x)
{
// 空间不够->增容
if (hp->_size == hp->_capacity)
{
size_t newcapacity = hp->_capacity * 2;
hp->_a = (HpDataType*)realloc(hp->_a, sizeof(HpDataType)*newcapacity);
hp->_capacity = newcapacity;
}
hp->_a[hp->_size] = x;
hp->_size++;
// 向上调整,调成堆
AdjustUp(hp->_a, hp->_size - 1);
}
堆的删除:
因为堆删除的是堆顶元素,所以首先交换堆顶和堆尾元素,再刨去堆尾元素(其实是交换过来的堆顶元素),然后进行向下排序。
void HeapPop(Heap* hp)
{
Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
hp->_size--;
AdjustDown(hp->_a, hp->_size, 0);
}
完整的代码:
Heap_.h:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
// 小堆
typedef int HpDataType;
typedef struct Heap
{
HpDataType* _a;
size_t _size;
size_t _capacity;
}Heap;
//向下调整算法
void AdjustDown(HpDataType* a, size_t n, int root);
//创堆
void HeapCreate(Heap* hp, HpDataType* a, size_t n);
//插入
void HeapPush(Heap* hp, HpDataType x);
//删除
void HeapPop(Heap* hp);
//输出堆顶元素
HpDataType HeapTop(Heap* hp);
//判空
int HeapEmpty(Heap* hp);
//打印
void HeapPrint(Heap* hp);
// 最大十个数
void PrintTopK(int* a, int n, int k);
//向上调整算法
void AdjustUp(int* a, int child);
//销毁堆
void HeapDestory(Heap* hp);
//交换
void Swap(int* a, int* b);
Heap_.c
#include "Heap_.h"
void Swap(int* a, int* b)
{
int x = *a;
*a = *b;
*b = x;
}
// 小堆
// ->条件:左右子树都是堆
void AdjustDown(HpDataType* a, size_t n, int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
// 找出小的那个孩纸
if (child + 1 < n && a[child+1] < a[child])
{
++child;
}
// 1、孩纸比父亲小,则交换,继续往下调
// 2、孩纸比父亲大,则终止调整
if (a[parent] > a[child])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void HeapCreate(Heap* hp, HpDataType* a, size_t n)
{
hp->_a = (HpDataType*)malloc(sizeof(HpDataType)*n);
memcpy(hp->_a, a, sizeof(HpDataType)*n);
hp->_size = n;
hp->_capacity = n;
// 建小堆
for (int i = (n-1-1)/2; i >= 0; --i)
{
AdjustDown(hp->_a, hp->_size, i);
}
}
void HeapDestory(Heap* hp)
{
free(hp->_a);
hp->_size = hp->_capacity = 0;
hp->_a = NULL;
}
void AdjustUp(int* a, int child)
{
int parent = (child - 1) / 2;
//while (parent >= 0) // 没起作用,巧合借助break跳出的
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapPush(Heap* hp, HpDataType x)
{
// 空间不够-> 增容
if (hp->_size == hp->_capacity)
{
size_t newcapacity = hp->_capacity * 2;
hp->_a = (HpDataType*)realloc(hp->_a, sizeof(HpDataType)*newcapacity);
hp->_capacity = newcapacity;
}
hp->_a[hp->_size] = x;
hp->_size++;
// 向上调整,调成堆
AdjustUp(hp->_a, hp->_size - 1);
}
void HeapPop(Heap* hp)
{
Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
hp->_size--;
AdjustDown(hp->_a, hp->_size, 0);
}
HpDataType HeapTop(Heap* hp)
{
return hp->_a[0];
}
int HeapEmpty(Heap* hp)
{
return hp->_size == 0 ? 1 : 0;
}
void HeapPrint(Heap* hp)
{
for (int i = 0; i < hp->_size; ++i)
{
printf("%d ", hp->_a[i]);
}
printf("\n");
}
// 最大十个数
void PrintTopK(int* a, int n, int k)
{
// k个数的小堆
Heap hp;
HeapCreate(&hp, a, k);
for (int i = k; i < n; ++i)
{
// 比堆顶的数据要大,就替代它
if (HeapTop(&hp) < a[i])
{
HeapPop(&hp);
HeapPush(&hp, a[i]);
}
}
HeapPrint(&hp);
}