i所在的层次:
l
o
g
2
(
n
+
1
)
或
[
l
o
g
2
n
]
(
向下取整
)
+
1
log_2(n + 1)或[log_2n](向下取整)+ 1
log2(n+1)或[log2n](向下取整)+1
2.若完全二叉树中共有n个结点,则
判断i是否有左孩子:
2
i
<
=
n
2i<=n
2i<=n
判断i是否有右孩子:
2
i
+
1
<
=
n
2i+1<=n
2i+1<=n
判断i是否是叶子/分支结点?:
i
>
[
n
/
2
]
(
向下取整
)
i > [n/2](向下取整)
i>[n/2](向下取整)
2.建立大根堆
根据大根堆的特性︰
根
≥
左、右
根≥左、右
根≥左、右
1.思路
把所有非终端结点都检查一遍,是否满足大根堆的要求,如果不满足,则进行调整
检查当前结点是否满足根≥左、右,
若不满足,将当前结点与更大的一个孩子互换.
若元素互换破坏了下一级的堆,则采用相同的方法继续往下调整(小元素不断“下坠”)
2.代码实现
//将以k 为根的子树调整为大根堆voidHeadAdjust(int A[],int k,int len){
A[0]= A[k];//A[0]暂存子树的根结点for(int i =2* k; i <= len; i *=2){//汇key较大的子结点向下筛选if(i < len && A[i]< A[i +1])
i++;//取key较大的子结点的下标if(A[0]>= A[i])break;//筛选结束else{
A[k]= A[i];//将A[i]调整到双亲结点上
k = i;//修改k值,以便继续向下筛选}}
A[k]= A[0];//被筛选结点的值放入最终位置}//建立大根堆voidBuildMaxHeap(int A[],int len){for(int i = len /2; i >0; i--)//从后往前调整所有非终端结点HeadAdjust(A, i, len);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
3.基于大根堆进行排序
1.原理
堆排序:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换)
并将待排序元素序列再次调整为大根堆(小元素不断“下坠”)
注意:基于大根堆的堆排序得到“递增序列”,基于小根堆的堆排序得到“递减序列”。
2.代码实现
//堆排序的完整逻辑voidHeapSort(int A[],int len){BuildMaxHeap(A, len);//初始建堆for(int i = len; i >1; i--){// n-1趟的交换和建堆过程swap(A[i], A[1]);//堆顶元素和堆底元素交换HeadAdjust(A,1, i -1);//把剩余的待排序元素整理成堆}}
1
2
3
4
5
6
7
8
9
4.算法效率分析
1.建堆过程的分析
若当前结点下方有两个孩子,则“下坠”一层,需对比关键字2次。
若下方只有一个孩子,则“下坠”一层,只需对比关键字1次。
结论:一个结点,每“下坠”一层,最多只需对比关键字2次
若树高为h,某结点在第i层,则将这个结点向下调整最多只需要“下坠” h-i层,
关键字对比次数不超过2(h-i)
n个结点的完全二叉树树高h=
[
l
o
g
2
n
]
(
向下取整
)
+
1
[log_2n](向下取整) +1
[log2n](向下取整)+1
第i层最多有2-1个结点,而只有第1~(h-1)层的结点才有可能需要“下坠”调整
结论:
将整棵树调整为大根堆,关键字对比次数不超过4n.
建堆过程,关键字对比次数不超过4n,建堆时间复杂度=O(n)
2.堆排序的过程分析
根节点最多下坠h-1层,每下坠一层,
而每“下坠”一层,最多只需对比关键字2次,
因此每一趟排序复杂度不超过
O
(
h
)
=
O
(
l
o
g
2
n
)
O(h)= O(log_2n)
O(h)=O(log2n)
共n-1趟,总的时间复杂度=
O
(
n
l
o
g
2
n
)
O(nlog_2n)
O(nlog2n)
因此堆排序的时间为建堆的时间加上排序的时间:
堆排序的时间复杂度=
O
(
n
)
+
O
(
n
l
o
g
2
n
)
=
O
(
n
l
o
g
2
n
)
O(n)+O(nlog_2n)= O(nlog_2n)
O(n)+O(nlog2n)=O(nlog2n),