数据结构中的堆通常指的是一种二叉树,利用顺序结构的数组存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
一、堆的概念
堆总是一棵完全二叉树,如果子节点的值总是大于等于父节点,则称为小堆;反之则称为大堆。
二、堆的实现
(1)堆的结构
(2)堆的初始化
先利用断言函数判断传来的值是否为空,再将arr置为空,size和capacity设为0
(3)数据入堆(小堆为例)
先判断arr是否有足够的空间容纳新的数据,如果没有,利用realloc函数进行扩容,并用newcapacity覆盖掉原先的capacity,之后在数组尾部插入新的数据,++size
需要注意的是,在数组尾部插入新的数据后,新的数组在尾部是否符合堆的规则(孩子节点大于父亲节点)呢?由此我们引入向上调整算法(AdjustUp)
我们知道,如果孩子节点的下标为child(无论左孩子还是右孩子),那么父亲节点的下标为(child-1)/2,因此我们利用循环不断判断孩子节点是否小于父亲节点,如果是,则交换孩子节点和父亲节点,并再次向上判断
(4)堆的删除(小堆为例)
需要注意的是,堆的删除指的是删除堆顶的数据,那么我们直接删除掉堆顶的数据可行吗?
答案是否。
假设有一小堆如下: 直接去掉堆顶数据变为:
可以看出,直接删除后留下来的数据不一定还能构成堆,同时,所有的父子兄弟关系全部乱套了,那么这时候我们应该如何处理呢?
答案是将堆顶数据和堆尾数据进行交换,并删除数组尾部数据(只要size--即可),并利用向下调整算法(AdjustDown),重新构建堆。
(5)取堆顶数据
(6)堆中元素个数
、
(7)判断堆是否为空
三、完整代码
Heap.h
#pragma once
#include<assert.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* arr;
int size;
int capacity;
}HP;
void AdjustUp(HPDataType* arr, int child);
void AdjustDown(HPDataType* arr, int n, int parent);
void swap(HPDataType* a, HPDataType* b);
//堆的初始化
void HeapInit(HP* php);
// 堆的销毁
void HeapDestory(HP* hp);
// 堆的插入
void HeapPush(HP* hp, HPDataType x);
// 堆的删除
void HeapPop(HP* hp);
// 取堆顶的数据
HPDataType HeapTop(HP* hp);
// 堆的数据个数
int HeapSize(HP* hp);
// 堆的判空
bool HeapEmpty(HP* hp);
Heap.c
#include "Heap.h"
void swap(HPDataType* a, HPDataType* b)
{
HPDataType tmp = *a;
*a = *b;
*b = tmp;
}
//堆的初始化
void HeapInit(HP* php)
{
assert(php);
php->arr = NULL;
php->size = php->capacity = 0;
}
// 堆的销毁
void HeapDestory(HP* php)
{
assert(php);
free(php->arr);
php->arr = NULL;
php->capacity = php->size = 0;
}
void AdjustUp(HPDataType* arr, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (arr[child] < arr[parent])
{
swap(&arr[child], &arr[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
// 堆的插入
void HeapPush(HP* php, HPDataType x)
{
assert(php);
if (php->capacity == php->size)
{
int newcapacity = (php->capacity == 0) ? 4 : 2 * php->capacity;
HPDataType* tmp = (HPDataType*)realloc(php->arr,sizeof(HPDataType) * newcapacity);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
php->arr = tmp;
php->capacity = newcapacity;
}
php->arr[php->size] = x;
php->size++;
AdjustUp(php->arr, php->size - 1);
}
//向下调整
void AdjustDown(HPDataType* arr, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child+1 < n && arr[child] > arr[child + 1])//找到左孩子和右孩子中小的那一个
{
child++;
}
if (arr[parent] > arr[child])
{
swap(&arr[parent], &arr[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
// 堆的删除(删除堆顶数据)
void HeapPop(HP* php)
{
assert(php);
assert(php->size > 0);
swap(&php->arr[0], &php->arr[php->size - 1]);
php->size--;
AdjustDown(php->arr, php->size, 0);
}
// 取堆顶的数据
HPDataType HeapTop(HP* php)
{
assert(php);
assert(php->size);
return php->arr[0];
}
// 堆的数据个数
int HeapSize(HP* php)
{
assert(php);
return php->size;
}
// 堆的判空
bool HeapEmpty(HP* php)
{
assert(php);
return php->size == 0;
}