在前面的学习中,我们已经学习了数据结构中的顺序表,链表,栈,队列。这些数据结构都是线性数据结构,从这篇文章开始,我们将要研究树这种非线性数据结构。
堆的介绍
首先介绍一下什么是树
树是有限个节点构成的具有层次关系的集合。因为这种关系的逻辑图看起来像一颗倒挂的树,故叫做树。
其次介绍一下树的种类
树又分为二叉树和非二叉树,二叉树又分为完全二叉树和满二叉树。这篇文章主要详细介绍堆,堆是一种完全二叉树。
其次介绍一下堆的种类
堆又分为大堆,小堆。大堆:父节点大于等于子节点 小堆:父节点小于等于子节点
堆的实现
堆的实现:堆的逻辑结构是完全二叉树,存储结构是顺序结构。下面以小堆的实现为例。
要实现堆,首先明白堆的逻辑结构是完全二叉树,存储结构是顺序表。
在完全二叉树中,有如下结论
父节点找左孩子:parent*2+1
父节点找右孩子:parent*2+2
孩子找父节点:(child-1)/2
heap.h
heap.c
堆的初始化
void IntiHeap(Hp* heap) { assert(heap); //malloc一块空间,用来初始化顺序表 DateType* tmp = (DateType*)malloc(4 * sizeof(DateType)); assert(tmp);//保证malloc成功 heap->arr = tmp;//初始化顺序表 heap->size = 0;//有效数据个数0 heap->capcity = 4;//初始容量4 }
堆的销毁
void DestoryHeap(Hp* heap) { assert(heap); free(heap->arr);//释放顺序表空间 heap->arr = NULL;//将指针置空,避免成为野指针 heap->size = 0;//有效数据置0 heap->capcity = 0;//容量置0 }
堆的插入
堆的插入是这里的关键,这里以小堆为例,欲在小堆里插入一个元素,这里的步骤为:
step1.直接插入到堆尾 step2.向上调整。
void HeapPush(Hp* heap, DateType n) { assert(heap); if (heap->size == heap->capcity)//检查容量 { //扩容 DateType* tmp = realloc(heap->arr, 2 * heap->capcity * sizeof(DateType)); assert(tmp); heap->arr = tmp; heap->capcity *= 2; } //直接插入,未做调整step1 heap->arr[heap->size] = n; heap->size++; //向上调整step2 UpAdjust(heap->arr, heap->size - 1);//参数为堆的存储顺序表和堆尾节点下标(孩子) }
向上调整算法
void UpAdjust(DateType* arr, int child)//向上调整算法 { int parent = (child - 1) / 2;//找到父亲节点 while (child != 0)//直到child成为0,不需要向上调整 { if (arr[child] < arr[parent])//如果不满足小堆定义 { //交换 swap(&arr[child],&arr[parent]); } else//满足就不需要向上调整了 { break; } //向上迭代child,parent child = parent; parent = (child - 1) / 2; } }
堆顶元素删除
堆顶元素删除的步骤为:
step1.交换堆顶元素和堆尾元素,直接删除堆尾元素 step2.向下调整堆。
void HeapPop(Hp* heap) { assert(heap); assert(!HeapEmpty(heap));//保证堆不为空 //step1:先交换,再size--,再调整堆 swap(&heap->arr[0], &heap->arr[heap->size - 1]); heap->size--; //step2:调堆,向下调整算法 DownAdjust(heap->arr, heap->size, 0); }
向下调整算法:
step2
//堆的存储结构 堆的有效数据个数 向下调整起始父节点 void DownAdjust(DateType* arr, int size, int parent) { int child = parent * 2 + 1;//找到左孩子 while (child<size) { //防止没有右孩子,越界 if (child+1<size&&arr[child] > arr[child + 1])//调整并找到数据较小的child { child++;//由左孩子变为右孩子 } if (arr[child] < arr[parent])//如果不满足小堆定义 { swap(&arr[child], &arr[parent]);//交换 //迭代 parent = child; child = parent * 2 + 1; } else { break; } } }
判断堆是否为空
bool HeapEmpty(Hp* heap) { assert(heap); return heap->size == 0; }
获取堆顶元素
DateType TopHeap(Hp* heap) { assert(heap); return heap->arr[0]; }
获取堆有效数据
int HeapSize(Hp* heap) { return heap->size; }