目录
堆的定义
堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。(来自百度百科)
最大堆
- 每个节点最多可以有两个节点
- 根节点的键值是所有堆节点键值中最大者,且每个父节点的值都比其左右孩子结点的值大
- 除了根节点、最后一个左子节点可以没有兄弟节点,其余节点必须有兄弟节点。如图
堆的一般规律
- i 的左子节点:2 * i + 1 ,i 的右子节点 2 * i + 2
- i 的父节点:( i - 1 ) / 2
- 最后一个父节点 size /2 -1 (注:size为总节点数,根节点从0开始计数
在数组中快速创建堆
- 首先我们需要找到最后一个结点的父结点如图(a),我们找到的结点是 87,然后找出该结点的最大子节点与自
己比较,若该子节点比自身大,则将两个结点交换.图(a)中,87 比左子节点 95 小,则交换之.如图(b)所示
2. 我们移动到前一个父结点 93,如图(c)所示.同理做第一步的比较操作,结果不需要交换
3. 继续移动结点到前一个父结点 82,如图(d)所示,82 小于右子节点 95,则 82 与 95 交换,如图(e)所示,82 交换
后,其值小于左子节点,不符合最大堆的特点,故需要继续向下调整,如图(f)所示
4. 所有节点交换完毕,最大堆构建完成
堆的算法实现
数据结构
#define DEFAULT_CAPACITY 128
typedef struct _Heap
{
int *arr;//堆数组
int size;//当前存储的元素个数
int capacity;//当前存储的容量
}Heap;
建(最大)堆
/ 堆.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//Author:See QQ:3492625357 交流群:894356239
//代码为本人手写,若有错误或不当之处,欢迎指正。
#include <iostream>
#define DEFAULT_CAPACITY 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);
int main()
{
Heap hp;
int origVals[] = { 1,2,3,87,93,82,92,86,95 };
if (!initHeap(hp,origVals,sizeof(origVals)/sizeof(int)))
{
fprintf(stderr, "初始化堆失败!\n");
return -1;
}
//输出最大堆 检测结果是否正确
for (int j = 0; j < hp.size; j++)
{
std::cout << hp.arr[j] << " ";
}
std::cout << std::endl;
}
//堆的初始化
bool initHeap(Heap & heap, int * orginal, int size)
{
int capacity = DEFAULT_CAPACITY > size ? DEFAULT_CAPACITY : 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;
}
/* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节 点),
确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */
void buildHeap(Heap & heap)
{
for (int i = heap.size / 2 - 1; i >= 0; i--) //堆的最后一个父节点是 size/2 -1
{
adjustDown(heap, i);
}
}
void adjustDown(Heap &heap, int index)
{
int lchild,rchild,maxchild;
for (int parent=index;(parent*2+1)<heap.size;parent=maxchild)
{
lchild = parent * 2 + 1;
rchild= parent * 2 + 2;
maxchild = lchild;
//如果右子节点存在
if (rchild <heap.size)
{
//取最大子节点
maxchild = heap.arr[lchild] >= heap.arr[rchild] ? lchild : rchild;
}
//如果父节点小于子节点则交换
if (heap.arr[parent] <heap.arr[maxchild])
{
int tmp = heap.arr[parent];
heap.arr[parent] = heap.arr[maxchild];
heap.arr[maxchild] = tmp;
}
}
}
最大堆中插入元素
将数字 99 插入到上面大顶堆中的过程如下:
1. 原始的堆,如图 a ,对应的数组:{95, 93, 87, 92, 86, 82}
2. 将新进的元素插入到大顶堆的尾部,如下图 b 所示,对应的数组:{95, 93, 87, 92, 86, 82, 99}
3. 此时最大堆已经被破坏,需要重新调整, 因加入的节点比父节点大,则新节点跟父节点调换即可,如图 c
所示;调整后,新节点如果比新的父节点小,则已经调整到位,如果比新的父节点大,则需要和父节点重新进
行交换,如图 d, 至此,最大堆调整完成。
算法实现:
bool insertHeap(Heap &heap, int e)
{
if (heap.size == heap.capacity) return false;
int index = heap.size;
heap.arr[heap.size++] = e;
adjustUp(heap, index);
}
/*将当前节点和父节点调整成最大堆*/
static void adjustUp(Heap &heap, int index)
{
if (index < 0 || index >= heap.size) return;
while (index>0)
{
int parent = (index - 1) / 2;
if (parent>=0)
{
if (heap.arr[parent]<heap.arr[index])
{
int temp = heap.arr[index];
heap.arr[index] = heap.arr[parent];
heap.arr[parent] = temp;
index = parent;
}
else
{
//如果父节点比当前节点大 则无须继续变更下去
break;
}
}
else
{
//如果当前节点已经比根节点小或者等于跟节点则无须继续
break;
}
}
}
堆顶元素出列
当插入节点的时候,我们将新的值插入数组的尾部。现在我们来做相反的事情:我们取出数组中的最后一个元
素,将它放到堆的顶部,然后再修复堆属性。
算法实现
static bool popMaxHeap(Heap &heap, int &e)
{
if (heap.size < 1) return false;
e = heap.arr[0];
heap.arr[0] = heap.arr[--heap.size];
adjustDown(heap, 0);
}
所有操作代码整合如下↓
//Author:See QQ:3492625357 交流群:894356239
//代码为本人手写,若有错误或不当之处,欢迎指正。
#include <iostream>
#define DEFAULT_CAPACITY 128
typedef struct _Heap
{
int *arr;//堆数组
int size;//当前存储的元素个数
int capacity;//当前存储的容量
}Heap;
bool initHeap(Heap &heap, int *orginal, int size); //堆的初始化
bool insertHeap(Heap &heap, int e);
static void buildHeap(Heap &heap);
static bool popMaxHeap(Heap &heap, int &e);
static void adjustDown(Heap &heap, int index);
static void adjustUp(Heap &heap, int index);
int main()
{
Heap hp;
int origVals[] = { 1,2,3,87,93,82,92,86,95 };
if (!initHeap(hp,origVals,sizeof(origVals)/sizeof(int)))
{
fprintf(stderr, "初始化堆失败!\n");
return -1;
}
//输出最大堆 检测结果是否正确
for (int j = 0; j < hp.size; j++)
{
std::cout << hp.arr[j] << " ";
}
std::cout << std::endl;
insertHeap(hp, 100);
std::cout << "插入元素 100 后:" << std::endl;
for (int j = 0; j < hp.size; j++)
{
std::cout << hp.arr[j] << " ";
}
std::cout << std::endl;
int value;
popMaxHeap(hp, value);
std::cout << "插入元素" << value <<" 后:" << std::endl;
for (int j = 0; j < hp.size; j++)
{
std::cout << hp.arr[j] << " ";
}
std::cout << std::endl;
}
//堆的初始化
bool initHeap(Heap & heap, int * orginal, int size)
{
int capacity = DEFAULT_CAPACITY > size ? DEFAULT_CAPACITY : 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;
}
/* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节 点),
确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */
void buildHeap(Heap & heap)
{
for (int i = heap.size / 2 - 1; i >= 0; i--) //堆的最后一个父节点是 size/2 -1
{
adjustDown(heap, i);
}
}
void adjustDown(Heap &heap, int index)
{
int lchild,rchild,maxchild;
for (int parent=index;(parent*2+1)<heap.size;parent=maxchild)
{
lchild = parent * 2 + 1;
rchild= parent * 2 + 2;
maxchild = lchild;
//如果右子节点存在
if (rchild <heap.size)
{
//取最大子节点
maxchild = heap.arr[lchild] >= heap.arr[rchild] ? lchild : rchild;
}
//如果父节点小于子节点则交换
if (heap.arr[parent] <heap.arr[maxchild])
{
int tmp = heap.arr[parent];
heap.arr[parent] = heap.arr[maxchild];
heap.arr[maxchild] = tmp;
}
}
}
bool insertHeap(Heap &heap, int e)
{
if (heap.size == heap.capacity) return false;
int index = heap.size;
heap.arr[heap.size++] = e;
adjustUp(heap, index);
}
static bool popMaxHeap(Heap &heap, int &e)
{
if (heap.size < 1) return false;
e = heap.arr[0];
heap.arr[0] = heap.arr[--heap.size];
adjustDown(heap, 0);
}
/*将当前节点和父节点调整成最大堆*/
static void adjustUp(Heap &heap, int index)
{
if (index < 0 || index >= heap.size) return;
while (index>0)
{
int parent = (index - 1) / 2;
if (parent>=0)
{
if (heap.arr[parent]<heap.arr[index])
{
int temp = heap.arr[index];
heap.arr[index] = heap.arr[parent];
heap.arr[parent] = temp;
index = parent;
}
else
{
//如果父节点比当前节点大 则无须继续变更下去
break;
}
}
else
{
//如果当前节点已经比根节点小或者等于跟节点则无须继续
break;
}
}
}