1。堆的创建,及声明相关函数
#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 Swap(HPDataType*ps,HPDataType*py); //交换避免冗余 void HeapInit(HP*hp); //初始化 void HeapDestroy(HP*hp); //销毁 void HeapPush(HP*hp,HPDataType X); //插入X void HeapPop(HP*hp); //删除 void HeapPrint(HP*hp); //打印 bool HeapEmpty(HP*hp); //检测是否为空 int HeapSize(HP*hp); //看有多少个
堆和顺序表是很像的,但是要注意他们的逻辑结构是不一样的,要表达的东西也不同
另外补充一句:assert断言是用来检测不该发生的情况,也就是传入数据可能失败等等。
2.堆的初始化与其销毁
//初始化 void HeapInit(HP*hp){ assert(hp); hp->a=NULL; hp->size=hp->capacity=0; } //销毁 void HeapDestroy(HP*hp){ assert(hp); free(hp->a); hp->capacity=hp->size=0; }
3.向上调整及插入X元素 (此时默认堆是大堆)
void AdjustUp(int *a,int child){ //向上调整下面还是详解,注意的是他这个实际本质还是拿数组存 assert(a); int parent=(child-1)/2; while(child>0){ if(a[child]>a[parent]){ Swap(&a[child],&a[parent]); child=parent; parent=(child-1)/2; } else break; } } void HeapPush(HP*hp,HPDataType x){ //HeapPush要求,插入一个X保证原有的堆,原来大堆插入后还得是大堆 assert(hp); if(hp->size==hp->capacity){ size_t newCapacity=hp->capacity==0?4:hp->capacity*2; //由于开始时capacity有可能是0 HPDataType*tmp=realloc(hp->a,sizeof(HPDataType)*newCapacity);//后面放realloc用法的细节 if(tmp==NULL){ printf("realloc fail\n"); exit(-1); } hp->a=tmp; hp->capacity=newCapacity; } hp->a[hp->size]=x; hp->size++; AdjustUp(hp->a,hp->size-1); }
在插入的时候,不能破坏他的堆结构,如原来是大堆也就是要求,插入末尾元素之后,要与其调整,插入的时候只影响画圈的那一列,所以需要判断插入的X与56,70相比,让他依然保持父亲大,儿子小的结构
向上调整
这里要了解一个知识 parent=(child-1)/2。 可以带入一下试试,不管左右节点都行,毕竟下标是整数
看尾元素和他的父节点比较,然后是交换值与下标
4.返回堆元素的个数
int HeapSize(HP*hp){ assert(hp); return hp->size; }
5.判断是不是空堆
bool HeapEmpty(HP*hp){ assert(hp); return hp->size==0; }
6.移除堆顶元素,以及向下调整(默认是小堆的情况,大堆就改变找大的)
void AdjustDown(int *a,int n,int parent){ int child=parent*2+1; while(child<n){ //左孩子是否到了叶节点也就是底 //选择左右孩子哪个才是最小的 if(child+1<n&&a[child+1]<a[child]){ //child+1是右孩子,右孩子可能不存在,所以也要判断一下。 ++child; } //如果最小的孩子小于父亲,交换,并且接着向下面调整,否则推出 if(a[child]<a[parent]){ Swap(&a[child],&a[parent]); //换值 parent=child; //换下标 child=parent*2+1; } else{ break; } } } //删除堆顶数据(删除树的根,也就是找最值) void HeapPop(HP*hp){ assert(hp); assert(!HeapEmpty(hp)); //看hp有没有值是不是空 Swap(&hp->a[0],&hp->a[hp->size-1]); //堆顶和最后的交换位置 hp->size--; //删除堆顶 AdjustDown(hp->a,hp->size,0); }
如果是大堆就是要找两边最大的
一些杂碎的知识
1.realloc函数是将数组扩容的一个函数
看是否申请成功 要是成功则realloc函数会调用,否则realloc会调用malloc函数申请开辟下一个数组空间,要是开辟新的数组空间成功,将原数组中的数据拷贝在新的数组中,释放掉原数组,并返回一个数组首地址,需要用一个指针来接。
2.堆的逻辑结构是完全二叉树,因此用数组基本不会空间浪费。
注:数据结构的栈和堆与操作系统的不同