堆的定义
如果一串存储在数组中的数据,以完全二叉树的形式能形成
k[i]>k[2i+1] && k[i]>k[2i+2] 或者
k[i]<k[2i+1] && k[i]<k[2i+2] 前者称为大堆 后者称为小堆
堆的数据是储存在一维数组中来实现的。
所以说对于堆的每个数据下标都是有规律的。
对于一个下标为N的节点来说
它的父节点的下标为(N-1)/2
一个下标为N父节点可能会有两个子节点
左节点为(N*2)+1
右节点为(N*2)+2
堆的实现(大堆)
我们要实现堆,首先要明确我们要使用啥结构来实现。
通过前面的定义我们知道
堆是存储在一维数组中的数据
所以顺序表成为了我们实现堆的不二之选
创建堆(顺序表)
//顺序表的结构体创建
typedef int datatype;
typedef struct
{
datatype* data;
int size;
int capacity;
}heap;
//顺序表定义
heap* HPinit()
{
heap* HP=(heap*)malloc(sizeof(heap));
assert(HP);
datatype* arr=malloc(sizeof(datatype)*10);
assert(arr);
HP->data = arr;
HP->size = 0;
HP->capacity = 10;
return HP;
}
插入数据
前面的数据插入和顺序表别无二致
void HPpush(heap* HP,datatype x)
{
assert(HP);
if (HP->capacity == HP->size)
{
datatype* arr = (datatype*)realloc(HP->data,sizeof(datatype)*HP->capacity*2);
assert(arr);
HP->data = arr;
HP->capacity *= 2;
}
HP->data[HP->size] = x;
HP->size++;
adjustup(HP,HP->size-1);
}
但是我们在插入一个数据后,要将其进行向上调整,使其符合大根堆的规律。
所以我们要在插入函数的最后,添加一个adjustup函数
向上调整
基本思路:
由前面的父系节点和孩子节点的下标的关系我们就能写出代码了。
//交换函数
void swap(heap* HP, int child, int parent)
{
assert(HP);
int tmp = HP->data[child];
HP->data[child]=HP->data[parent];
HP->data[parent] = tmp;
}
//调整函数
void adjustup(heap* HP, int position)
{
int child = position;
int parent = (child - 1) / 2;
while (child > 0)
{
if (HP->data[child] > HP->data[parent])
{
//判断大于父系节点时交换
swap(HP, child, parent);
child = parent;
parent = (child - 1) / 2;
}
else
//小于父系节点,表示已到合适位置,故跳出循环
break;
}
}
删除数据
不同的数据结构,我们需要考虑他们不同的作用
对于队列和栈他们删除数据都要遵从他们的结构性质
对于堆来说,最有意义的数据是堆顶的数据。
对于大根堆来说,堆顶的数据是最大数
对于小根堆来说,堆顶的数据是最小数
所以我们删除堆的数据函数,都是删除堆顶的数据。
明白了这点以后,我们就可以来看具体实现的步骤了。
向下调整
当我们删除头部数据以后,就需要进行向下调整。
因为我们不能直接进行数据的删除,如果直接将数据删除移除,这样就破坏了原本的数据结构
void HPPOP(heap* HP)
{
assert(HP);
if (Heapempty(HP))
{
return;
}
else
{
swap(HP, HP->size - 1, 0);
HP->size--;//size自减,就代表删除了一个数据
adjustdown(HP);//交换完向下调整
}
}
这个时候就需要用-交换删除法并且结合向下调整
大致思路有了
接下来就是代码实现了
void adjustdown(heap* HP)
{
assert(HP);
int parent = 0;
int child = parent*2+1;
while (child< HP->size - 1)
//当前下标大于数组中最大下标停止
{
if (HP->data[child] < HP->data[child + 1]&& child+1<HP->size-1)
//假设child为左孩子节点,与右孩子进行比较,如果右孩子大,则交换
{
child += 1;
}
if (HP->data[parent] < HP->data[child])
{
//父节点小于孩子节点交换
swap(HP, parent, child);
parent = child;
child = (child * 2) + 1;
}
else
//否则退出循环
break;
}
}
接下来都是顺序表的经典操作了,就直接上代码了。
返回堆顶元素
datatype HPtop(heap* HP)
{
assert(HP);
return HP->data[0];
}
判断堆是否为空
bool Heapempty(heap* HP)
{
assert(HP);
return (!HP->size);
}
整体代码
头文件部分
#pragma once
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
typedef int datatype;
typedef struct
{
datatype* data;
int size;
int capacity;
}heap;
heap* HPinit();
void adjustup(heap* HP,int position);
void HPpush(heap*HP,datatype x);
void HPPOP(heap* HP);
void adjustdown(heap* HP);
void swap(heap* HP, int child, int parent);
bool Heapempty(heap* HP);
datatype HPtop(heap* HP);
函数实现部分
#include"Heap.h"
heap* HPinit()
{
heap* HP=(heap*)malloc(sizeof(heap));
assert(HP);
datatype* arr=malloc(sizeof(datatype)*10);
assert(arr);
HP->data = arr;
HP->size = 0;
HP->capacity = 10;
return HP;
}
void swap(heap* HP, int child, int parent)
{
assert(HP);
int tmp = HP->data[child];
HP->data[child]=HP->data[parent];
HP->data[parent] = tmp;
}
void adjustup(heap* HP, int position)
{
int child = position;
int parent = (child - 1) / 2;
while (child > 0)
{
if (HP->data[child] > HP->data[parent])
{
swap(HP, child, parent);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
void HPpush(heap* HP,datatype x)
{
assert(HP);
if (HP->capacity == HP->size)
{
datatype* arr = (datatype*)realloc(HP->data,sizeof(datatype)*HP->capacity*2);
assert(arr);
HP->data = arr;
HP->capacity *= 2;
}
HP->data[HP->size] = x;
HP->size++;
adjustup(HP,HP->size-1);
}
void adjustdown(heap* HP)
{
assert(HP);
int parent = 0;
int child = parent*2+1;
while (child<= HP->size - 1)
{
if (HP->data[child] < HP->data[child + 1]&& child+1<HP->size-1)
{
child += 1;
}
if (HP->data[parent] < HP->data[child])
{
swap(HP, parent, child);
parent = child;
child = (child * 2) + 1;
}
else
break;
}
}
void HPPOP(heap* HP)
{
assert(HP);
if (Heapempty(HP))
{
return;
}
else
{
swap(HP, HP->size - 1, 0);
HP->size--;
adjustdown(HP);
}
}
datatype HPtop(heap* HP)
{
assert(HP);
return HP->data[0];
}
bool Heapempty(heap* HP)
{
assert(HP);
return (!HP->size);
}
测试部分(用户自己随便引用)
#include"Heap.h"
int main()
{
heap* HP = HPinit();
HPpush(HP, 1);
HPpush(HP, 2);
HPpush(HP, 3);
HPpush(HP, 4);
HPpush(HP, 5);
HPpush(HP, 6);
HPpush(HP, 23);
HPpush(HP, 223);
HPpush(HP, 423);
HPPOP(HP);
while (!Heapempty(HP))
{
printf("%d ", HP->data[0]);
HPPOP(HP);
}
}