堆
堆都是完全二叉树。
堆分为大(根)堆和小(根)堆。
大堆每个父亲的值都大于等于孩子的值,小堆每个父亲的值都小于等于孩子的值。
因此堆可以用来选数,堆顶(根)就是最大或最小值。
叶子节点可以看作大堆或小堆
完全二叉树中的数学关系
如果完全二叉树中父亲的编号为i:
左孩子的编号为2i+1;
右孩子是2i+2。
如果左孩子的编号是i:
父亲是(i-1)/2;
右孩子同理。
堆的数组表示
堆常用数组表示,数组的下标为堆的编号-1,由此可以利用完全二叉树的数学关系,通过一个节点找到它的父亲、左右儿子,来遍历整个堆。
向下调整算法
以小堆为例。
当一个完全二叉树的两个子树是小堆,而根不满足小堆时,可以使用此算法。
将原堆的根与两个子堆的根中较小(大)的比较,若大于则交换,交换后再与其两个子堆的根中较小(大)的比较,大于交换,循环往复,直到其小于两个子堆的根,或者到达叶子节点。

调整后变为小(大)堆:

void AdjustDown(HeapType* a, int n, int root)
{
int parent = root;
int child = 2 * parent + 1;
while (child < n)
{
if (child + 1 < n && a[child] > a[child + 1])
{
child++;
}
if (a[parent] > a[child])
{
swap(&a[parent], &a[child]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
建堆
从倒数的第一个非叶子节点i(也就是最后一个结点的父节点)作为根开始向下调整,再自减i,循环往复。最后一个混乱的完全二叉树变成了堆。
建堆的时间复杂度为O(n)。
同时我们发现堆的根为所有元素中的最小(大)值。
堆排序
由建堆启发,我们设想是否可以建堆,去除根元素,循环往复,对一系列元素进行排序。其实这种排序方法是一种堆排序,但时间复杂度为O(),有没有什么办法优化呢?
由于堆是由数组表示的,当完成对一个完全二叉树建堆后,把根元素与数组的最后一个元素交换,再把数组长度减小1,再建堆,循环往复。这种方法的时间效率大大提高,为O(n)
以如下的完全二叉树为例:


建小堆:


得到最小值1,与尾值9交换,然后减小数组长度
这样,9为根,其两个子树仍然是小堆,符合向下调整算法,循环往复,实现数值的从大到小排序。

所以得到总结,升序建大堆,降序建小堆。
void AdjustDown(HeapType* a, int n, int root)
{
int parent = root;
int child = 2 * parent + 1;
while (child < n)
{
if (child + 1 < n && a[child] > a[child + 1])
{
child++;
}
if (a[parent] > a[child])
{
swap(&a[parent], &a[child]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void HeapSort(HeapType* a, int n)
{
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
}


198

被折叠的 条评论
为什么被折叠?



