首先,对于堆来说:1.堆是一个完全二叉树;
2.堆有两种:大堆和小堆;
3.以小堆为例, 这个树的根节点是这个树中的最小的元素
// 对于任意一个子树来说, 子树的根节点, 小于左右孩子节点的值.
4. 以大堆为例, 这个树的根节点是这个树种的最大元素
// 对于任意一个子树来说, 子树的根节点, 大于左右孩子节点的值.
堆的初始化:
堆是用数组来定义的,并且在初始化时,指明此堆是大堆还是小堆,这里用一个函数指针来定义;
堆的插入:
思路:与树的插入相同,直接创建结点,然后插入到数组的尾部,不同的是,堆在插入完成时,仍要保证符合堆的性质,即大堆或者小堆,所以需要进行调整,这里采用上浮式调整方法;
取堆顶元素:
思路:因为是数组,所以数组下标为0的元素即是对顶元素;
删除堆顶元素:
思路:让堆顶元素与最后一个元素进行交换,再size减一,此时将对顶元素已经删掉,但是堆已经被破坏,所以要对堆进行调整,这里采用下沉式调整方法;
堆的判空:
思路:判断size是否为0;
堆的销毁:
思路:将堆的大小设置为0,函数指针指向空即可;
堆排序:
思路:在我们不想开辟额外的空间, 或者消耗额外的时间的前提下,
// 如果我们想进行从小到大排序, 就需要一个大堆
// 如果我们想进行从大到小排序, 就需要一个小堆
大堆:当我们循环删除对顶元素时,最后将所有都删除完了,此时的循序就是升序
小堆:与大堆的做法和思想完全相同,这个思路重点理解;
具体代码实现如下:
#pragma once
#include <stddef.h>
#define HeapMaxSize 1024
typedef char HeapType;
//如果a和b满足比较关系,返回1
//如果不满足比较关系,就返回0
//所谓的比较关系,对于小堆来说,就是a<b
//对于大堆来说,a>b
typedef int(*Compare)(HeapType a,HeapType b);
typedef struct Heap{
HeapType data[HeapMaxSize];
size_t size;
Compare cmp;
}Heap;
//在对堆进行初始化时,决定堆是大堆还是小堆
void HeapInit(Heap* heap,Compare cmp);
//销毁
void HeapDestory(Heap* heap);
//往堆中插入元素
void HeapInsert(Heap* heap,HeapType value);
//取堆顶元素
int HeapRoot(Heap* heap,HeapType* value);
//删除堆顶元素
void HeapErase(Heap* heap);
//根据arry数组,构建一个堆,这个堆用heap来表示
void HeapCreate(Heap* heap,HeapType arry[],size_t size);
//实现堆排序
void HeapSort(HeapType arryp[],size_t size);
heap.c
#include <stdio.h>
#include <string.h>
#include "heap.h"
//小堆
int Less(HeapType a,HeapType b){
return a<b?1:0;
}
//大堆
int Greater(HeapType a, HeapType b){
return a>b?1:0;
}
void HeapInit(Heap* heap,Compare cmp){
if(heap == NULL){
//非法输入
return;
}
heap->size = 0;
heap->cmp = cmp;
return;
}
void HeapDestory(Heap* heap){
if(heap ==NULL){
//非法输入
return;
}
heap->size = 0;
heap->cmp = NULL;
return;
}
void Swap(HeapType* a,HeapType* b){
HeapType tmp = *a;
*a = *b;
*b = tmp;
return;
}
void AdjustUp(Heap* heap,size_t index){
size_t child = index;
size_t parent = (child - 1)/2;
while(child > 0){
if(!heap->cmp(heap->data[parent],heap->data[child])){
Swap(&heap->data[parent],&heap->data[child]);
}else{
//如果发现某一时刻child 与 parent 符合堆的比较要求
//此时就可以停止上浮,因为上面的元素肯定也是满足堆的要求的
break;
}
child = parent;
parent = (child - 1)/2;
}
return;
}
void HeapInsert(Heap* heap,HeapType value){
if(heap == NULL){
//非法输入
return;
}
if(heap->size >= HeapMaxSize){
//堆满了
return;
}
heap->data[heap->size++] = value;
//对堆进行上浮式调整
//调整的起始位置是size - 1
AdjustUp(heap,heap->size - 1);
return;
}
int HeapRoot(Heap* heap,HeapType* value){
if(heap == NULL){
//非法输入
return 0;
}
if(heap->size == 0){
//空堆
return 0;
}
*value = heap->data[0];
return 1;
}
void AdjustDown(Heap* heap,size_t index){
if(heap == NULL){
return;
}
size_t parent = index;
size_t child = 2 * parent + 1;
while(child < heap->size){
if(child + 1 < heap->size && heap->cmp(heap->data[child+1],heap->data[child])){
//如果右子树存在,并且右子树比左子树更符合“堆的要求”
//假设我们是小堆,就要求说:
//如果右子树比左子树小,那么就让child指向右子树
child = child + 1;
}
//child就指向了左右子树中更小的那个元素
if(heap->cmp(heap->data[child],heap->data[parent])){
Swap(&heap->data[parent],&heap->data[child]);
}else{
break;
}
parent = child;
child = 2 * parent + 1;
}
return;
}
void HeapErase(Heap* heap){
if(heap == NULL){
//非法输入
return;
}
if(heap->size == 0){
//空堆
return;
}
//交换堆顶元素与最后一个元素
Swap(&heap->data[0],&heap->data[heap->size - 1]);
//size--
heap->size--;
//调整堆
//从根结点出发,进行下沉式调整
AdjustDown(heap,0);
return;
}
void HeapCreate(Heap* heap,HeapType arry[],size_t size){
if(heap == NULL){
return;
}
//遍历arry数组,将数组中的元素插入到堆中
size_t i = 0;
for(;i<size;i++){
HeapInsert(heap,arry[i]);
}
return;
}
//假设我们要进行升序排序,我们要建立一个大堆
void HeapSort(HeapType arryp[],size_t size){
//把这个数组构建成一个堆
Heap heap;
HeapInit(&heap,Greater);
HeapCreate(&heap,arryp,size);
//循环的对堆进行删除操作
while(heap.size > 0){
HeapErase(&heap);
}
//循环结束之后堆排序就完成了
memcpy(arryp,heap.data,size*sizeof(HeapType));
return;
}