赞
踩
所有元素按完全二叉树的顺序存储方式存储在一个一维数组中, 并满足:
K
i
<
=
K
2
i
+
1
K_i <= K2_i+1
Ki<=K2i+1 且
K
i
<
=
K
2
i
+
2
K_i<= K2_i+2
Ki<=K2i+2 (
K
i
>
=
K
2
i
+
1
K_i >= K2_i+1
Ki>=K2i+1 且
K
i
>
=
K
2
i
+
2
K_i >=K2_i+2
Ki>=K2i+2) i = 0 、1、 2… ,则 称为小堆 ( 或大堆) (即双亲比孩子的数值小(大)——小(大)堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
1、基本思想(升序为例):将序列通过向下调整,使其成为大堆,此时堆的根部放的时最大值,再将根部元素与待排序列末尾位置的元素交换,不包含最后这个元素,继续进行向下调整–保证根部元素一直是待排序列中最大的,再与倒数第二个元素交换,直到待排序列为空。
2、过程:
- 先将待排序列通过向下调整成为大堆:再从最后一个父节点(最后一个叶子结点的父节点)从下往上进行向下调整
- 交换根和序列末尾元素交换完后再进行向下调整(保证剩余元素成堆 )
- 更新末尾位置end 重复第二步,直到所有元素均排序完毕。
3、时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
这下我们知道了,要实现堆排序 首先我们需要通过向下调整算法,将待排序列建立成为一个堆
基本思想:
1.从根结点处开始,选出左右孩子中值较大的孩子。
2.让大的孩子与其父亲进行比较。
- 若大的孩子比父亲还大,则该孩子与其父亲的位置进行交换。并将原来大的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。
- 若大的孩子比父亲小,则不需处理了,调整完成,整个树已经是大堆了。
void AdjustDown(int* a, int size, int parent)//向下调整(O(logn)) { //假设法 int child = parent * 2 + 1;//假设左孩子大 while (child < size) { //大堆中 向下调整 找大的 if (child + 1 < size && a[child] < a[child + 1])//假设错误 { child++;//更新为右子 } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); //更新父节点和子节点 parent = child; child = parent * 2 + 1; } else { break; } } }
这里仔细思考一下,向下调整的会有一个潜在的条件:根的左右子树都应该是堆,这样执行向下调整后就是堆了
向下调整算法有了,那么如何将一个待排序列,通过向下调整成堆呢?
根据下图:我们从下往上走(序列就是从后往前走),从倒数第一个非叶子结点(
a
[
(
n
−
1
−
1
)
/
2
a[(n - 1 - 1) / 2
a[(n−1−1)/2])开始,依次作为根去向下调整即可。
//调整成堆
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);//此处向上调整 形成大堆 依次将最大、次大……找出放于根部
}
这下建成堆了,可以进行堆排序了
再次声明一下堆排序的基本思想:
1、将堆顶数据与堆的最后一个数据交换,然后对根位置进行一次堆的向下调整,但是调整时被交换到最后的那个最大的数不参与向下调整。
2、完成步骤1后,这棵树除最后一个数之外,其余数又成一个大堆,然后又将堆顶数据与堆的最后一个数据交换,这样一来,第二大的数就被放到了倒数第二个位置上,然后该数又不参与堆的向下调整…反复执行下去,直到堆中只有一个数据时便结束。此时该序列就是一个升序。
//堆排序 void AdjustDown(int* a, int size, int parent)//向下调整(O(logn)) { //假设法 int child = parent * 2 + 1; while (child < size) { //大堆中 向下调整 找大的 if (child + 1 < size && a[child] < a[child + 1])//假设错误 { child++;//更新为右子 } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); //更新父节点和子节点 parent = child; child = parent * 2 + 1; } else { break; } } } void HeapSort(int* a, int n)//排成升序 { //调整成堆 for (int i = (n - 1 - 1) / 2; i >= 0; --i) { AdjustDown(a, n, i);//此处向上调整 形成大堆 依次将最大、次大……找出放于根部 } int end = n - 1;//用于表示数组尾部结点的下标 数值表示此前数字个数 while (end > 0) { Swap(&a[0], &a[end]);//将根部数据排在最后 达到升序的效果 AdjustDown(a, end, 0); --end;//将从根部换下的值不当作堆中数据 对堆重复以上操作 } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。