堆的概念:
堆是一棵完全二叉树,堆中的元素存储到一维数组中,对于任意节点,如果该节点小于(大于)其左右孩子,就是小(大)堆
堆的特性:
<1>.堆顶元素一定是堆中所有元素最大的(最小的)
<2>.从根节点到任意一条支路的最后一个节点都是一个有序的序列,如果是小堆,就是升序,如果是大堆,就是降序
堆的创建:
堆中的元素是储存在一维数组中的,所以先提供一个数组,完成堆的初始化
初始化:
void InitHeap(Heap* hp, HpDataType* array, int size, Compare compare)
{
assert(hp);
try
{
hp->_array = new HpDataType[size];
}
catch (...)
{
cout << "申请空间错误!" << endl;
}
hp->_capacity = size;
for (int i = 0; i < size; ++i)
{
hp->_array[i] = array[i];
}
hp->_size = size;
}
调整:
通过刚才的代码,我们把堆初始化好了,但是还不满足堆的性质,所以我们还需要进一步调整
向下调整:
向下调整算法有一个前提:左右子树必须是一个堆,才能调整
针对比较随机的数组,改进向下调整算法,int array[] = { 2, 3, 8, 0, 9, 1, 7, 4, 6, 5 };
如果根节点的左右子树不满足堆的性质,我们从倒数第一个叶子节点开始向下调整
这样把最后一组根节点和子树都处理完毕,然后处理上一个叶子节点,直到处理到根节点
代码如下:
//空间申请完毕,数据拷贝完毕,开始调整,满足堆的性质
int lastleaf = (size - 2) >> 1;
while (lastleaf >= 0)
{
AdjustDown(hp->_array, hp->_size, lastleaf, hp->compare);
lastleaf--;
}
向上调整:
堆的向上调整主要是用在插入元素,插入元素只会影响到它的双亲
所以将插入元素一直与双亲比较,一直比较到满足堆序或者到根节点
代码如下:
void AdjustUp(HPDataType* a, int size, int child,PCom compare)
{
int parent = (child - 1) / 2;
while (child != 0)
{
if (compare(a[child] , a[parent]))
{
swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
return;
}
}
}
排序:
有了前面的向上和向下排序,那么堆排就可以实现了
方法是先将数组中的元素构建成堆,向下调整为大堆,首尾交换,头部最大的元素就跑到最后的位置
然后元素个数自减,再把剩下的元素调成大堆,直到没有可以调的,就完成了排序(降序)
int array[] = { 2, 3, 8, 0, 9, 1, 7, 4, 6, 5 };
代码如下:
void HeapAdjust(int* array, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size && array[child + 1] > array[child])
child += 1;
if (array[child] > array[parent])
{
swap(array[child], array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
return;
}
}
}
void HeapSort(int* array, int size)
{
int root = ((size - 2) >> 1);
for (root; root >= 0; --root)
{
HeapAdjust(array, size, root);
}
int end = size - 1;
while (end != 0)
{
swap(array[0], array[end]);
HeapAdjust(array, end, 0);
end--;
}
}