C++ 数据结构算法 学习笔记(13) - 堆
堆的原理
从上图看 (A) 以及 (B) 并不是堆, 而©是堆
堆里面重要的方程式
i的子节点左以及i的父节点这两个方程式都很重要!后续建堆的时候都需要用到这两个方程式
如何在数组中快速的创建堆?
- 首先我们需要找到最后一个结点的父结点如图(a),我们找到的结点是 87,然后找出该结点的最大子节点与自 己比较,若该子节点比自身大,则将两个结点交换. 图(a)中,87 比左子节点 95 小,则交换之.如图(b)所示
- 我们移动到前一个父结点 93,如图©所示.同理做第一步的比较操作,结果9不需要交换.
- 继续移动结点到前一个父结点 82,如图(d)所示,82 小于右子节点 95,则 82 与 95 交换,如图(e)所示,82 交换 后,其值小于左子节点,不符合最大堆的特点,故需要继续向下调整,如图(f)所示
- 所有节点交换完毕,最大堆构建完成
堆的完整代码
完整代码
#include <iostream>
#include <string>
using namespace std;
#define DEFAULT_CAPCITY 128
typedef struct _Heap {
int* arr;
int size;
int capacity;
}Heap;
bool initHeap(Heap& heap, int* orginal, int size);
static void buildHeap(Heap& heap);
static void adjustDown(Heap& heap, int index);
bool initHeap(Heap& heap, int* orginal, int 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);
}
else {
heap.size = 0;
}
return true;
}
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;
}
}
}
void buildHeap(Heap& heap) {
int i;
for (i = (heap.size-2/)2; i >= 0; i--) {
adjustDown(heap, i);
}
}
void adjustUp(Heap& heap, int index) {
if (index < 0 || index >= heap.size)
{
return;
}
while (index > 0) {
int temp = heap.arr[index];
int parent = (index - 1) / 2;
if (parent >= 0) {
if (temp > heap.arr[parent]) {
heap.arr[index] = heap.arr[parent];
heap.arr[parent] = temp;
index = parent;
}
else {
break;
}
}
else {
break;
}
}
}
bool insert(Heap& heap, int value) {
if (heap.size == heap.capacity) {
fprintf(stderr, "Stack SPACE is EMPTY!\n");
return false;
}
int index = heap.size;
heap.arr[heap.size++] = value;
adjustUp(heap, index);
return true;
}
int main(void) {
Heap hp;
int origVals[] = { 1, 2, 3, 87, 93, 82, 92, 86, 95 };
int i = 0;
if (!initHeap(hp, origVals, sizeof(origVals) / sizeof(origVals[0]))) {
fprintf(stderr, "Initial STACK failed!\n");
exit(-1);
}
for (i = 0; i < hp.size; i++) {
printf("the %dth element:%d\n", i, hp.arr[i]);
}
insert(hp, 99);
printf("In STACK insert a new element 99,Insert result:\n");
for (i = 0; i < hp.size; i++) {
printf("the %dth element:%d\n", i, hp.arr[i]);
}
system("pause");
return 0;
}
上面代码有一个比较需要注意的点,就是在built_heap
里的这一行的代码
for (i = (heap.size-2/)2; i >= 0; i--)
为什么这个代码是-2
呢? 这个原因是heap.size
其实是下一个节点的值,所以需要-1回到指向最后一个节点的值,然后需要再-1因为这是堆寻找父节点的方程式里需要的-1.
堆的数据定义
上述代码包括了对数据的定义
#define DEFAULT_CAPCITY 128
typedef struct _Heap{
int *arr;
int size;
int capacity;
}Heap;
插入新元素
插入新元素演示.例子:将数字99插入到上面大顶堆中的过程如下:
-
原始的堆,如图 a
对应的数组:{95, 93, 87, 92, 86, 82}
-
将新进的元素插入到大顶堆的尾部,如下图 b 所示
对应的数组:{95, 93, 87, 92, 86, 82,99}
- 此时最大堆已经被破坏,需要重新调整, 因加入的节点比父节点大,则新节点跟父节点调换即可,如图 c 所示;调整后,新节点如果比新的父节点小,则已经调整到位,如果比新的父节点大,则需要和父节点重新进 行交换,如图 d, 至此,最大堆调整完成。
-
堆定元素出列
如果我们将堆顶的元素删除,那么顶部有一个空的节点,怎么处理? 当插入节点的时候,我们将新的值插入数组的尾部。现在我们来做相反的事情:我们取出数组中的最后一个元 素,将它放到堆的顶部,然后再修复堆属性。