数据结构与算法 (最大)堆
堆是一个二叉树结构,如下图:
堆又分为两种:1.最大堆 2.最小堆
堆的表示:使用数组。
堆(最大堆)的特点:
1.每个节点最多可以有两个节点(如上图,一个⚪代表一个节点)。
2.根节点的值是所有堆节点的值中最大的,且每个节点的值都比其孩子(子节点)的值大。
3.除了根节点没有兄弟节点外,最后一个左子节点可以没有兄弟节点,其他节点必须有兄弟节点。
最小堆的特点与最大堆基本一样,只是特点2稍作修改:
2.根节点的值是所有堆节点的值中最小的,且每个节点的值都比其孩子(子节点)的值小。
《注意》:如果不满足上述三个条件,那么就不是堆。
左子节点:
兄弟节点:
因为堆由数组实现,所以堆的子节点和父节点寻找方式如下:
i 为数组下标:
i 的左子节点:2i+1
i 的右子节点:2i+2
i 的父节点: (i-1)/2
数组存储与堆的关系图如下:
最大堆实现:
初始化时,通过(heap.size / 2 - 1)找出最小(最下层)父节点。找出后判断子节点是否比父节点大,若比父节点大,则交换位置,否则不动。大小位置调整好后,i值减1,从下一个父节点开始判断,依次循环。
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_CAPCITY 128 //数组默认大小
typedef struct _Heap {
int* arr; //表示存储堆元素的数组
int size; //当前已存储的元素个数
int capacity; //当前的存储容量
}Heap;
//将当前节点和子节点调整成最大堆
void adjustDown(Heap& heap, int index) {
int cur = heap.arr[index]; //当前待调整的节点
int parent, child;
/*
*判断是否存在大于当前节点的字节点,若不存在,则堆本身是平衡的,不需要调整;
*若存在,则将最大的子节点与之交换,交换后,如果这个子节点下还有子节点
*则要继续按照同样的步骤对这个子节点进行调整
*/
for (parent = index; (parent * 2 + 1) < heap.size; parent = child) {
child = parent * 2 + 1;
//取两个子节点中的最大的节点
if (((child + 1) < heap.size) && (heap.arr[child] < heap.arr[child + 1])) {
child++;
}
//判断最大的节点是否大于当前的父节点
if (cur >= heap.arr[child]) {
//不大于,则不需要调整,跳出循环
break;
}
else {
//大于当前的父节点,进行交换,然后从子节点位置继续向下调整
heap.arr[parent] = heap.arr[child];
heap.arr[child] = cur;
}
}
}
//从最后一个父节点(size/2 - 1的位置)逐个往前调整所有父节点(直到根节点),
//确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆
void buildHeap(Heap& heap) {
int i;
for (i = heap.size / 2 - 1; i >= 0; i--) {
adjustDown(heap, i);
}
}
//初始化数组
bool initHeap(Heap& heap, int* orginal, int size) {
//orginal为数组,size为数组大小
//如果size比默认值小,则取默认值
int capacity = DEFAULT_CAPCITY > size ? DEFAULT_CAPCITY : size;
heap.arr = new int[capacity];//分配内存空间
if (!heap.arr) return false; //内存分配失败
heap.capacity = capacity; //数组存储容量
heap.size = 0;
//如果存在原始数据则构建堆
if (size > 0) {
//将原有数组中的数据复制到堆数组中
memcpy(heap.arr,orginal,size*sizeof(int));
heap.size = size; //原始数组元素个数
//建堆
buildHeap(heap);
}
return true;
}
int main(void) {
Heap hp;
int i;
int origVals[] = {
3,8,54,68,72,88,97,42,30};
if (!initHeap(hp, origVals, sizeof(origVals) / sizeof(origVals[0]))) {
fprintf(stderr,"初始化堆失败!\n");
exit(-1);
}
for (i = 0; i < hp.size; i++) {
printf("第%d个元素为:%d\n", i, hp.arr[i]);
}
system("pause");
return 0;
}