堆的存储结构实际上是一个完全二叉树,通过不断地调整各个节点的位置,可以得到最小堆或者最大堆,最小堆是每一个双亲都比每一个子节点的值要小,但是兄弟节点之间的大小未知,同样,最大堆即是双亲都比每一个子节点要小。对于堆来说,由于相对简便所以采用数组的方式来实现。从下标为 0 开始,如果双亲节点下标为 i ,则左子节点为 2* i + 1,右子节点下标为 2*i + 2。反过来,对于下表为 i 的子节点,其双亲下标为 (i - 1)/2,当然,有的地方是按从下标为 1 的位置开始存的,其实现都相同。
最大堆与最小堆实现完全相同只要改一下大于或小于号即可
首先建立结构体
typedef struct Heap{
int *arr;//动态一维数组
int size;//下标
int capacity;//最大容量
}Heap;
接下来便是对堆初始化、插入、取最小元素的操作
//最小堆初始化
void Init(Heap *heap,int max){
if(max <= 0)
return ;
heap = malloc(sizeof(int) *max);
heap->size = 0;
heap->capacity = max;
}
插入
//堆插入
void Insert(Heap *heap, int x){
//如果堆已满,则对堆的长度 + 5
if(heap->size == heap->capacity)
{
int *p = realloc(heap->arr,sizeof(int)*(heap->capacity + 5));
heap->capacity = heap->capacity + 5;
heap->arr = p;
}
heap->arr[heap->size++] = x;//堆的末尾添加元素
int i = heap->size - 1;//数组对应最大下标
while(i != 0){
int j = (i - 1)/2;// j 为双亲
if(x >= heap->arr[j])
break;
heap->arr[i] = heap->arr[j];//交换双亲与子节点
i = j; //重复以上操作
}
heap->arr[i] = x;//把 x 插到合适的位置
}
移除最小元素
int dele(Heap *heap){
if(heap->size == 0)
return;
int temp = heap->arr[0];//如果就一个节点则直接返回即可
heap->size--;
if(heap->size == 0)
return temp;
int i = 0;//双亲,刚开始指向 0 位置
int j = 2*i+1; //左子节点
int x = heap->arr[heap->size];//待调整的尾部元素
while(j <= heap->size -1){
if(j < heap->size-1 && heap->arr[j] > heap->arr[j+1])//左子节点比右子节点大则 j 指向右节点
j++;
if( x <= heap->arr[j])//如果 x 小于了子节点则不用再调整
break;
heap->arr[i] = heap->arr[j]; //交换子节点与双亲
i = j; //重复以上操作
j = 2*i + 1;
}
heap->arr[i] = x;//把尾部元素插到合适的位置
return temp;
}
以上是最小堆的操作,下面附上完整代码(包含最小堆与最大堆)
#include <stdio.h>
#include <stdlib.h>
typedef struct Heap{
int *arr;
int size;
int capacity;
}Heap;
//最小堆初始化
void Init(Heap *heap,int max){
if(max <= 0)
return ;
heap = malloc(sizeof(int) *max);
heap->size = 0;
heap->capacity = max;
}
//堆插入
void Insert(Heap *heap, int x){
//如果堆已满,则对堆的长度 + 5
if(heap->size == heap->capacity)
{
int *p = realloc(heap->arr,sizeof(int)*(heap->capacity + 5));
heap->capacity = heap->capacity + 5;
heap->arr = p;
}
heap->arr[heap->size++] = x;//堆的末尾添加元素
int i = heap->size - 1;//数组对应最大下标
while(i != 0){
int j = (i - 1)/2;// j 为双亲
if(x >= heap->arr[j])
break;
heap->arr[i] = heap->arr[j];//交换双亲与子节点
i = j; //重复以上操作
}
heap->arr[i] = x;//把 x 插到合适的位置
}
//移除堆的最小元素,也即是根节点
int dele(Heap *heap){
if(heap->size == 0)
return;
int temp = heap->arr[0];//如果就一个节点则直接返回即可
heap->size--;
if(heap->size == 0)
return temp;
int i = 0;//双亲,刚开始指向 0 位置
int j = 2*i+1; //左子节点
int x = heap->arr[heap->size];//待调整的尾部元素
while(j <= heap->size -1){
if(j < heap->size-1 && heap->arr[j] > heap->arr[j+1])//左子节点比右子节点大则 j 指向右节点
j++;
if( x <= heap->arr[j])//如果 x 小于了子节点则不用再调整
break;
heap->arr[i] = heap->arr[j]; //交换子节点与双亲
i = j; //重复以上操作
j = 2*i + 1;
}
heap->arr[i] = x;//把尾部元素插到合适的位置
return temp;
}
int isEmpty(Heap *heap){
if(heap->size == 0)
return 1;
return 0;
}
//以下为最大堆
void InitMax(Heap *heap, int max){
if(max <= 0)
return ;
heap = malloc(sizeof(int) *max);
heap->size = 0;
heap->capacity = max;
}
void InsertMax(Heap *heap, int x){
if(heap->size == heap->capacity){
int *p = realloc(heap->arr,sizeof(int)*(heap->capacity + 5));
heap->arr = p;
heap->capacity = heap->capacity + 5;
}
heap->arr[heap->size++] = x;
int i = heap->size - 1;
while(i != 0){
int j = (i - 1)/2;
if(x <= heap->arr[j])
break;
heap->arr[i] = heap->arr[j];
i = j;
}
heap->arr[i] = x;
}
int deleMax(Heap *heap){
if(heap->size == 0)
return ;
int temp = heap->arr[0];
heap->size--;
if(heap->size == 0)
return temp;
int x = heap->arr[heap->size];
int i = 0;
int j = 2*i + 1;
while(j <= heap->size - 1){
if(j < heap->size - 1 && heap->arr[j] < heap->arr[j+1])
j++;
if(heap->arr[j] <= x)
break;
heap->arr[i] = heap->arr[j];
i = j;
j = 2*i + 1;
}
heap->arr[i] = x;
return temp;
}
int main(){
Heap *heap;
Init(heap,5);
int a[8] = {5,6,8,2,1,3,9,7};
int i;
for(i = 0; i < 8; i++){
Insert(heap,a[i]);
}
while(!isEmpty(heap)){
printf("%d ",dele(heap));
}
// int i;
// int a[8] = {5,6,8,2,1,3,9,7};
// Heap *heap2;
// InitMax(heap2,5);
// for(i = 0; i < 8; i++){
// InsertMax(heap2,a[i]);
// }
//
// while(!isEmpty(heap2)){
// printf("%d ",deleMax(heap2));
// }
}
注意:最小堆与最大堆的测试请分开测试