一、堆
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:(1)堆中某个节点的值总是不大于或不小于其父节点的值;
(2)
堆总是一棵完全二叉树。
堆的实现:
(1)
堆向下调整算法
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整
成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
int
array
[]
=
{
27
,
15
,
19
,
18
,
28
,
34
,
65
,
49
,
25
,
37
};
从父节点以此向下进行调整:直到调整完成。
void AdjustDown(HPDataType* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] > a[child])
child++;
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
(2)向上调整
同向下调整一样,但向上调整是从最后一个叶子结点,向上进行调整,直至根节点。
void AdjustUp(HPDataType* 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;
}
}
}
(3)堆的插入
先插入一个数到
数组的尾上,再进行向上调整算法,直到满足堆。
(4)堆的删除
删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调 整算法。
堆的代码实现
《1》头文件部分:
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
//以数组的方式构建一个堆
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
void swap(HPDataType* a1, HPDataType* a2);//交换
void AdjustDown(HPDataType* a, int n, int parent); //建堆 向下调整
void AdjustUp(HPDataType* a,int child);//向上调整
void HeapPrint(HP* hp);//打印
void HeapInit(HP* hp);//初始化
void HeapDestroy(HP* hp);//销毁
void HeapGreate(HP* hp, HPDataType* a, int n); //一次性创建n个大小的堆,以数组的方式进行创建
void HeapPush(HP* hp, HPDataType x);
void HeapPop(HP* hp);
HPDataType HeapTop(HP* hp);
int HeapSize(HP* hp);
bool HeapEmpty(HP* hp);
《2》实现部分
#include"Heap.h"
void swap(HPDataType* a1, HPDataType* a2)
{
HPDataType tmp = *a1;
*a1 = *a2;
*a2 = tmp;
}
void AdjustDown(HPDataType* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] > a[child])
child++;
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void AdjustUp(HPDataType* 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 HeapPrint(HP* hp)//打印
{
assert(hp);
for (int i = 0;i < hp->size;i++)
{
printf("%d ", hp->a[i]);
}
printf("\n");
}
void HeapInit(HP* hp)//初始化
{
assert(hp);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
void HeapDestroy(HP* hp)//销毁
{
assert(hp);
free(hp->a);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
void HeapGreate(HP* hp, HPDataType* a, int n) //一次性创建n个大小的堆,每个的值为x
{
assert(hp);
hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
if (hp->a == NULL)
{
perror("malloc fail");
exit(-1);
}
memcpy(hp->a, a, sizeof(HPDataType) * n);//拷贝数据
hp->size = hp->capacity = n;
//建堆算法
for (int i = (n - 1 - 1) / 2;i >= 0;i--)
{
AdjustDown(hp->a, n, i);
}
}
void HeapPush(HP* hp, HPDataType x)
{
assert(hp);
if (hp->size == hp->capacity)
{
int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(hp->a, newcapacity*sizeof(HPDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
hp->a = tmp;
hp->capacity = newcapacity;
}
hp->a[hp->size] = x;
hp->size++;
AdjustUp(hp->a, hp->size - 1);
}
void HeapPop(HP* hp)
{
assert(hp);
assert(hp->size > 0);
swap(&hp->a[0], &hp->a[hp->size - 1]);
hp->size--;
AdjustDown(hp->a, hp->size, 0);
}
HPDataType HeapTop(HP* hp)
{
assert(hp);
assert(hp->size > 0);
return hp->a[0];
}
int HeapSize(HP* hp)
{
assert(hp);
return hp->size;
}
bool HeapEmpty(HP* hp)
{
assert(hp);
return hp->size == 0;
}
堆排序:
对于,需要一个升序时,建一个大堆,依次将堆顶的元素放在最后,整个的大小减一,依次循环。
需要降序时,相反即可。
void HeapSort(int* a, int sz)
{
//堆排序,分为向下建堆和向上建堆
向下建堆 时间复杂度0(N)
//for (int i = (sz - 1 - 1) / 2;i >= 0;i--)
//{
// AdjustDown(a, sz, i);
//}
//向上建堆 时间复杂度为 O(NlogN)
for (int i = 1;i < sz;i++)
{
AdjustUp(a, i);
}
//利用堆排序,建一个升序
int end = sz - 1;
while (end)
{
swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
end--;
}
}
TOP-K问题
《1》
用数据集合中前
K
个元素来建堆
前
k
个最大的元素,则建小堆 ;前k
个最小的元素,则建大堆
《2》用剩余的
N-K
个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余
N-K
个元素依次与堆顶元素比完之后,堆中剩余的
K
个元素就是所求的前
K
个最小或者最大的元素。