目录
前言
向下调整算法(默认小堆)
利用向下调整算法对数组建堆
向上调整建堆和向下调整建堆的区别编辑
向下调整建堆的时间复杂度:
向上调整建堆的时间复杂度:
结论
前言
在上一章讲解到了利用向上调整算法对数组进行建堆
再利用首尾交换和向下调整建堆算法对数组调整成升序或者降序
数据结构 ——— 向上/向下调整算法将数组调整为升/降序-CSDN博客
接下来要学习的是:利用向下调整算法建堆,然后再比较区别
向下调整算法(默认小堆)
static void AdjustDown(HPDataType* a, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
// 先找到左右孩子中小的那个
if ((child + 1 < size) && (a[child + 1] < a[child]))
child++;
if (a[parent] > a[child])
{
// 交换
Swap(&a[parent], &a[child]);
// 迭代
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
推理过程已经在上一章讲解了,这里就不过多赘述
利用向下调整算法对数组建堆
有以下整型数组a:
int a[] = { 5,7,3,9,1,8,4,6,2 };
如何利用向下调整算法对数组建堆呢?
使用向下调整算法的前提是:需要向下调整的数据的左右子树都是堆,才能使用向下调整算法,否则就算使用向下调整算法,也不能建堆
现在的主要问题就是数组 a 不是堆,且数组首元素的左右子树也不是堆
解决办法:
从数组最后一个元素开始往前向下调整建堆
因为向下建堆的前提是要左右子树是一个堆,那么就先把左右子树建堆,左右子树中又会有左右子树,直到根节点,就停止建堆
所以只需要从数组最后一个元素开始往前依次利用向下建堆算法进行建堆,那么就能把数组 a 建立成大堆/小堆
但是必须要从数组的最后一个元素开始建堆吗?
其实不用,因为最后一个元素是叶子节点,叶子节点的特点就是没有孩子节点,那么叶子节点本身就是一个堆
5
/ \
7 3
/ \ / \
9 1 8 4/ \
6 2
6、2、8、4 都属于叶子节点,那么就不用对这些节点进行向下调整算法
那么只需要从最后一个叶子节点的父亲节点开始往前进行向下调整算法
假设最后一个叶子节点 2 的下标为 child
那么父亲节点就可以通过 (child-1)/2 这个公式进行计算,2 的父亲节点也就是 9
对 9 进行了向下调整算法后,就需要对 3 这个子树进行向下调整算法
而且找 3 这个节点只需要 9 节点的下标减一即可
依次往前向下调整,最后调整到数组的首元素,那么就完成了对数组 a 进行建堆
代码验证:
1
/ \
2 3
/ \ / \
5 7 8 4/ \
6 9
向上调整建堆和向下调整建堆的区别
向下调整建堆的时间复杂度:
最后一层是叶子节点,所以不需要使用向下调整算法
从倒数第二层开始,使用向下调整算法进行建堆
倒数第二层有 2^(h-2) 个节点,每个节点最多向下调整 1 次
那么倒数第二层一共向下调整 2^(h-2)*1 次
倒数第三层有 2^(h-3) 个节点,每个节点最多向下调整 2 次
那么倒数第三层一共向下调整 2^(h-2)*2 次
………………
第二层有 2^1 个节点,每个节点最多向下调整 h-2 次
那么第二层一共向下调整 2^1*(h-2) 次
第二层有 2^0 个节点,每个节点最多向下调整 h-1 次
那么第一层一共向下调整 2^0*(h-1) 次
得出时间复杂度表达式:
F(h) = 2^(h-2)*1 + 2^(h-3)*2 + …… + 2^1*(h-2) + 2^0*(h-1)
通过错位相减法简化以上公式得出:
F(h) = 2^h - 1 - h
且假设节点的总个数为 N ,那么高度h和节点N的关系是: 2^h - 1 == N
最后得出向下调整建堆整体复杂度为:
F(N) = N - log(N+1)
大O渐进表示法:O(N) ,(log(N+1)可以忽略不计)
向上调整建堆的时间复杂度:
除了根节点,其他节点都需要使用向上调整算法
第二层有 2^1 个节点,每个节点最多向上调整 1 次
那么第二层一共向上调整 2^1*1 次
………………
最后一层有 2^(h-1) 个节点,每个节点最多向上调整 (h-1) 次
那么最后一层一共向上调整 2^(h-1)*(h-1) 次
得出时间复杂度表达式:
F(h) = 2^1*1 + 2^2*2 + …… + 2^(h-2)*(h-2) + 2^(h-1)*(h-1)
通过错位相减法简化以上公式得出:
F(h) = 2^h*(h-2)+2
且假设节点的总个数为 N ,那么高度h和节点N的关系是: 2^h - 1 == N
最后得出向下调整建堆整体复杂度为:
F(N) = (N+1) * (log(N+1)-2) + 2
大O渐进表示法:O(N*longN)
结论
向下调整算法的时间复杂度低于向上调整算法的时间复杂度
那么也就是向下调整算法的效率高于向上调整算法的效率
所以要对数组进行建堆的话,推荐使用向下调整建堆算法