向上建堆:
向上调整的思想就是我们把原数组的第一个数据看成是一个堆 后续的数据是要插入的数据 然后利用这种思想使用我们之前写的 向上调整 函数
这就是我们向上调整建堆的的时间复杂度的计算 大家看到这里是不是也会觉得 我们的向下调整建堆 也是这个时间复杂度呢 其实不是的 他们之间是有差别的 并且比较之下 向下调整建堆会更好一些 大家来看:
向下建堆:
我们说过 向下建堆其实就是找到我们的最后一个非叶子节点 什么意思呢? 其实就是我们最后以个节点的父亲节点 我们从最后一个非叶子节点开始往上遍历每一个非叶子节点使用我们的向下调整函数 这样我们也就能保证我们使用向下调整函数的条件(该节点的左右子树都是堆且相同)
所以我们的向下调整建堆的时间复杂度是O(N)
由此呢 我们得出 我们平常建堆得话还是使用 向下调整建堆 会好一点 虽说两者相差不多
堆排序:
我们刚刚了解了 向上建堆 和 向下建堆 的思想 现在就让我们运用这两种思想来实现我们的堆排序
现在假如我们要排一个升序 我们是要建大堆还是小堆呢?
假设我们我们排的是小堆 那我们的思想就是排一个小堆 接着在第二个数据那里开始排小堆 这样的想法是没错的 但是有个问题大家有没有想过就是我们每一次建完堆 从根节点的下一个数据的位置建小堆的时候我们之前排的堆的结构是不是整体就被破坏了 又要重新地完完全全地建一次堆 如果这样的话我们是不是就要排 n^2(n-1+n-2....2+1) 次 这样我们还不如去一次次遍历找树来的简单 反正都是 n^2
所以我们要排升序的话 建大堆的效果会更好 这与这种方法的思路其实就是和我们前面写的HeapPop 堆删除 的思路是一样一样的
大家看到我们的向下调整是可以控制数量的 所以呢我们每次排完只需要将根节点和最后一个叶子节点就是最后一个数据 进行一次交换
在下一次调整的时候 不把最后一个数据看在堆里面
我们上面不是说了如果排升序 建小堆的话 每次选数建堆 都要完完整整的建一次堆嘛 那我们的向下调整建堆需要 这样完全的建堆嘛?
当不是的哦:
不知道大家有没有想过如果我们排了一个数据后 以后还会遇到比他更大的嘛?
当然答案是不会的啦 我们每次建堆都是大堆 我们把最大的换过去 留下的都是比他小的 而且我们在作向下调整建大堆的时候 我们也是要筛选左右孩子节点的 我们每次都是选出较大的换上去 这样我们既保证了 我们堆的结构没有破坏 同时内 换上来的数也是之前交换过最大的数的次小的数
所以我们我们的堆排序 是不是就是一个选择排序呢? 后面的文章会说到
那堆排序的时间复杂度是不是就是 N*logN 呢?
:我们有N个数 每次排都是≈logN次
while (end > 0) { swap(hp._a, hp._a + end); Adjustdown(hp._a, --end , 0); }