资料参考:https://www.cnblogs.com/chengxiao/p/6129630.html
升序排序总体思路:
- 对无序数组构建大顶堆,此时数组的最大元素位于数组的第一个位置。
- 将数组的第一个元素与数组的最后一个元素进行交换,此时数组的最大元素位于数组的最后一个位置。
- 忽略数组的最后一个元素,对数组的剩余元素进行调整,使得数组的剩余元素构成一个新的大顶堆,此时这些剩余元素中的最大元素位于数组的倒数第二个位置。
- 反复执行2、3所描述的交换+调整策略,直到整个数组有序。
代码
public static void heapSort(int[] arr) {
buildMaxHeap(arr);
for(int i = 1; i < arr.length; i++) {
swap(arr, 0, arr.length-i);
heapify(arr, 0, arr.length-i);
}
}
public static void buildMaxHeap(int[] arr) {
for(int i = arr.length/2-1; i >= 0; i--) {
heapify(arr, i, arr.length);
}
}
public static void heapify(int[] arr, int i, int len) {
int temp = arr[i];
for(int k = 2*i+1; k < len; k = 2*k+1) {
if(k+1 < len && arr[k] < arr[k+1]) {
k += 1;
}
if(arr[k] > temp) {
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;
}
性能分析
时间复杂度:
以最差情况为例,假设堆中共有
n
n
n个结点,堆的高度为
k
k
k。
(1) 建堆
在第
i
(
1
≤
i
≤
k
−
1
)
i(1 \leq i \leq k-1)
i(1≤i≤k−1)层,最多有
2
(
i
−
1
)
2^{(i-1)}
2(i−1)个非叶子结点,每个非叶子结点最多要与自己子树中的结点比较
(
k
−
i
)
(k-i)
(k−i)次,那么第
i
i
i层的所有非叶子结点在比较上需要花费的时间为
t
(
k
)
=
(
k
−
i
)
∗
2
i
−
1
t(k) = (k-i) * 2^{i-1}
t(k)=(k−i)∗2i−1,现我们只关注比较次数,而忽略移动次数(因为移动次数是比较次数的常数倍),那么建堆需要花费的时间为
T
1
(
k
)
=
1
∗
2
k
−
2
+
2
∗
2
k
−
3
+
⋯
+
(
k
−
1
)
∗
2
0
T_{1}(k) = 1 * 2^{k-2} + 2 * 2^{k-3} + \cdots + (k-1) * 2^{0}
T1(k)=1∗2k−2+2∗2k−3+⋯+(k−1)∗20
等式两边乘以2,可得
2
T
1
(
k
)
=
1
∗
2
k
−
1
+
2
∗
2
k
−
2
+
⋯
+
(
k
−
1
)
∗
2
1
2T_{1}(k) = 1 * 2^{k-1} + 2 * 2^{k-2} + \cdots + (k-1) * 2^{1}
2T1(k)=1∗2k−1+2∗2k−2+⋯+(k−1)∗21
两式相减可得
T
1
(
k
)
=
2
k
−
1
+
2
k
−
2
+
⋯
+
2
1
−
(
k
−
1
)
T_{1}(k) = 2^{k-1} + 2^{k-2} + \cdots + 2^{1} - (k-1)
T1(k)=2k−1+2k−2+⋯+21−(k−1)
应用等比数列求和公式可得
T
1
(
k
)
=
2
k
−
k
−
1
T_{1}(k) = 2^{k} - k -1
T1(k)=2k−k−1
又因为堆是完全二叉树,所以
k
=
l
o
g
n
+
1
k = logn + 1
k=logn+1,即
T
1
(
n
)
=
2
n
−
l
o
g
n
−
2
=
O
(
n
)
T_{1}(n) = 2n - logn -2 = O(n)
T1(n)=2n−logn−2=O(n)
(2) 重建堆
因为每次重建堆都从根结点开始调整,所以重建一次堆需要花费的时间为
l
o
g
(
N
)
log(N)
log(N),
N
N
N为待排序序列中元素的个数。因此重建堆需要花费的总时间为:
T
2
(
n
)
=
l
o
g
(
n
−
1
)
+
l
o
g
(
n
−
2
)
+
⋯
+
l
o
g
(
2
)
<
n
l
o
g
(
n
)
T_{2}(n) = log(n-1) + log(n-2) + \cdots + log(2) < nlog(n)
T2(n)=log(n−1)+log(n−2)+⋯+log(2)<nlog(n)
T
2
(
n
)
=
n
l
o
g
(
n
)
T_{2}(n) = nlog(n)
T2(n)=nlog(n)
综上所述,在最差情况下,堆排序的时间复杂度
T
(
n
)
=
T
1
(
n
)
+
T
2
(
n
)
=
O
(
n
l
o
g
n
)
T(n) = T_{1}(n) + T_{2}(n) = O(nlogn)
T(n)=T1(n)+T2(n)=O(nlogn)
空间复杂度:
O
(
1
)
O(1)
O(1)
稳定性: 不稳定,如果左子节点和右子结点的值相等,那么左子节点更早进入堆顶,从而被移到数组末尾。