前言:前面我们已经学习了二叉树,今天我们来学习堆,堆也是一个二叉树,堆有大堆有小堆,大堆父节点大于子节点,小堆父节点总小于子节点,我们在学习C语言的时候也有一个堆的概念,那个堆是操作系统中的堆,与我们今天所学的堆全然不同。我们就来实现下小堆。
堆的实现:
1.堆的创建
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
前面我们已经知道了二叉树用数组来储存比较简单,而且便于访问,所以我们用数组来实现。
2.堆的一些接口
void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
// 规定删除堆顶(根节点)
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
size_t HeapSize(HP* php);
bool HeapEmpty(HP* php);
3.堆接口的实现
初始化堆:
void HeapInit(HP* php)
{
assert(php);
php->a = NULL;
php->size = 0;
php->capacity = 0;
}
堆的销毁:
void HeapDestroy(HP* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->size = php->capacity = 0;
}
交换实现:
void Swap(HPDataType* p1, HPDataType* p2)
{
HPDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
交换在我们我们对堆进行上下调整的时候会用到,我们定义出来使用就比较方便。
向上调整:
void AdjustUp(HPDataType* a, int child)
{
int parent = (child - 1) / 2;
//while (parent >= 0)
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
//child = (child - 1) / 2;
//parent = (parent - 1) / 2;
}
else
{
break;
}
}
}
我们在给堆插入数据时,为了保持小堆就要进行向上调整,如果子节点比父节点要小的话就要和父节点互换。
插入数据:
void HeapPush(HP* php, HPDataType x)
{
assert(php);
if (php->size == php->capacity)
{
int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
php->a = tmp;
php->capacity = newCapacity;
}
php->a[php->size] = x;
php->size++;
AdjustUp(php->a, php->size - 1);
}
我们插入数据是在最后的位置插入,那这个时候我们就需要向上调整了,将我们刚插入的节点和它的父节点相比较,如果比它的父节点小的话就和它的父节点交换。
向下调整:
void AdjustDown(int* a, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
// 假设左孩子小,如果解设错了,更新一下
if (child + 1 < size && 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 HeapPop(HP* php)
{
assert(php);
assert(php->size > 0);
Swap(&php->a[0], &php->a[php->size - 1]);
php->size--;
AdjustDown(php->a, php->size, 0);
}
如果我们直接删除根节点的话我们后续调整的时间复杂度非常的大,但是我们先将根节点和最后一个节点交换在让交换完的节点向下调整,就比较简单实现了,最后在删除交换完的最后一个节点就可以了。
访问根节点:
HPDataType HeapTop(HP* php)
{
assert(php);
assert(php->size > 0);
return php->a[0];
}
储存数据的大小:
size_t HeapSize(HP* php)
{
assert(php);
return php->size;
}
判断是否为空:
bool HeapEmpty(HP* php)
{
assert(php);
return php->size == 0;
}
3.测试代码
#include"Heap.h"
int main()
{
int a[] = { 4,6,2,1,5,8,2,9};
HP hp;
HeapInit(&hp);
for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
{
HeapPush(&hp, a[i]);
}
while (!HeapEmpty(&hp))
{
printf("%d ", HeapTop(&hp));
HeapPop(&hp);
}
printf("\n");
return 0;
}
我们创建一个数组将它放入小堆里存储,然后我们实现给堆里的数据打印出来。
打印小堆里的前k个数据:
#include"Heap.h"
int main()
{
int a[] = { 4,6,2,1,5,8,2,9};
HP hp;
HeapInit(&hp);
for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
{
HeapPush(&hp, a[i]);
}
int k = 3;
while (k--)
{
printf("%d\n", HeapTop(&hp));
HeapPop(&hp);
}
return 0;
}
我们只要循环去访问堆的根节点就可以了。
4.代码的完整实现
Heap.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
// 规定删除堆顶(根节点)
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
size_t HeapSize(HP* php);
bool HeapEmpty(HP* php);
Heap.c:
#include"Heap.h"
// 小堆
void HeapInit(HP* php)
{
assert(php);
php->a = NULL;
php->size = 0;
php->capacity = 0;
}
void HeapDestroy(HP* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->size = php->capacity = 0;
}
void Swap(HPDataType* p1, HPDataType* p2)
{
HPDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustUp(HPDataType* a, int child)
{
int parent = (child - 1) / 2;
//while (parent >= 0)
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
//child = (child - 1) / 2;
//parent = (parent - 1) / 2;
}
else
{
break;
}
}
}
void HeapPush(HP* php, HPDataType x)
{
assert(php);
if (php->size == php->capacity)
{
int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
php->a = tmp;
php->capacity = newCapacity;
}
php->a[php->size] = x;
php->size++;
AdjustUp(php->a, php->size - 1);
}
void AdjustDown(int* a, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
// 假设左孩子小,如果解设错了,更新一下
if (child + 1 < size && 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 HeapPop(HP* php)
{
assert(php);
assert(php->size > 0);
Swap(&php->a[0], &php->a[php->size - 1]);
php->size--;
AdjustDown(php->a, php->size, 0);
}
HPDataType HeapTop(HP* php)
{
assert(php);
assert(php->size > 0);
return php->a[0];
}
size_t HeapSize(HP* php)
{
assert(php);
return php->size;
}
bool HeapEmpty(HP* php)
{
assert(php);
return php->size == 0;
}
test.c:
#include"Heap.h"
int main()
{
int a[] = { 4,6,2,1,5,8,2,9};
HP hp;
HeapInit(&hp);
for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
{
HeapPush(&hp, a[i]);
}
/*int k = 3;
while (k--)
{
printf("%d\n", HeapTop(&hp));
HeapPop(&hp);
}*/
while (!HeapEmpty(&hp))
{
printf("%d ", HeapTop(&hp));
HeapPop(&hp);
}
printf("\n");
return 0;
}
如果对大家有帮助的话,就狠狠支持一下吧!