堆排序
描述:
堆排序利用堆这种数据结构设计的一种排序算法,堆是一个近似完全二叉树的结构,并同时满足堆积的性质。即子节点的键值或索引总是小于(或者大于)它的父节点。
堆排序每次取大顶堆中的最大元素,取出最大元素后,剩余元素重新构成大顶堆,再取最大元素,以此类推。n个元素取n-1次即可完成排序。(小顶堆类似)
代码如下:
public static void sortAscend(int[] array) {
int heapSize = array.length;
//buildMaxHeap方法见下面的堆结构分析
Heap.buildMaxHeap(array, heapSize);
for (int i = array.length - 1; i > 0; i--) {
//exchange方法交换array数组中第0个和第i个下标的值
Heap.exchange(array, 0, i);
heapSize--;
//maxHeapify见下面的堆结构分析
Heap.maxHeapify(array, heapSize, 0);
}
}
算法分析:
第3行buildMaxHeap建立最大堆的时间代价为 O ( n ) O(n) O(n),maxHeapify的时间代价为 O ( lg n ) O(\lg{n}) O(lgn),for循环的时间代价为 O ( n lg n ) O(n\lg{n}) O(nlgn),堆排序的总的时间代价为 O ( n ) + O ( n lg n ) = O ( n lg n ) O(n)+O(n\lg{n})=O(n\lg{{n}}) O(n)+O(nlgn)=O(nlgn)。、
时间复杂度: O ( n lg n ) O(n\lg{n}) O(nlgn)
空间复杂度: O ( 1 ) O(1) O(1)
堆结构分析
1. maxHeapify分析
维护最大堆性质的重要过程
前提:假定了index的左子树和右子树均为最大堆,此时array[index]可能小于其孩子节点,这违背了最大堆的性质,maxHeapify让array[index]的值在最大堆中逐级下降,使下标为index,根节点的子树重新遵循最大堆的性质
代码如下:
public static void maxHeapify(int[] array, int heapSize, int index) {
/*获取index儿子节点的下标*/
int left = index << 1;
int right = (index << 1) + 1;
/*获取index 左儿子 右儿子三个节点中值最大节点的下标*/
int maxIndex = index;
if (left < heapSize && array[left] > array[index]) {
maxIndex = left;
}
if (right < heapSize && array[right] > array[maxIndex]) {
maxIndex = right;
}
/*最大值节点置于父节点位置*/
if (maxIndex != index) {
//exchange方法交换array数组中第0个和第i个下标的值
exchange(array, index, maxIndex);
maxHeapify(array, heapSize, maxIndex);
}
}
算法分析:
调整index和index的左右子节点的时间代价为 O ( 1 ) O(1) O(1),加上maxHeapify在index孩子节点上运行的时间代价(递归调用的时间代价)。每个孩子节点的子树的大小至多为 2 n / 3 2n/3 2n/3(最坏情况发生在树的最底层恰好半满的时候)。
运行时间:
T ( n ) < = T ( 2 n / 3 ) + O ( 1 ) T(n)<=T(2n/3)+O(1) T(n)<=T(2n/3)+O(1)
根据主定理计算得: T ( n ) = O ( lg n ) T(n)=O(\lg{n}) T(n)=O(lgn)
在高度为h的树中,maxHeapify的时间复杂度为 O ( h ) O(h) O(h)
时间复杂度: O ( lg n ) O(\lg{n}) O(lgn)
空间复杂度: O ( 1 ) O(1) O(1)
2. buildMaxHeap分析
建立最大堆分析
代码如下:
public static void buildMaxHeap(int[] array, int heapSize) {
for (int i = (heapSize - 1) / 2; i >= 0; i--) {
maxHeapify(array, heapSize, i);
}
}
算法分析:
每次调用maxHeapify需要
O
(
log
n
)
O(\log{n})
O(logn),buildMaxHeap需要调用
O
(
n
)
O(n)
O(n)次,因此总的时间复杂度是
O
(
n
lg
n
)
O(n\lg{n})
O(nlgn),此上界虽正确,但不是渐进紧确的。
不同节点运行maxHeapify的时间与该节点的树高有关,大部分节点的高度都很小,因此可以获得一个更加紧确的界。
运行时间:
包含n个元素的堆的高度为 └ lg n ┘ \llcorner\lg{n}\lrcorner └lgn┘,高度为h的堆最多包含 ┌ n / 2 h + 1 ┐ \ulcorner n/2^{h+1}\urcorner ┌n/2h+1┐个节点。
高度为h的节点上运行maxHeapify的代价是 O ( h ) O(h) O(h),buildMaxHeap的总代价为:
∑ h = 0 └ lg n ┘ ┌ n 2 h + 1 ┐ O ( h ) = O ( n ∑ h = 0 └ lg n ┘ h 2 h ) \sum_{h=0}^{\llcorner\lg{n}\lrcorner}{\ulcorner \frac{n}{2^{h+1}}\urcorner}O(h)=O(n\sum_{h=0}^{\llcorner\lg{n}\lrcorner}{\frac{h}{2^h}}) ∑h=0└lgn┘┌2h+1n┐O(h)=O(n∑h=0└lgn┘2hh)
∵ ∑ h = 0 ∝ h 2 h = 1 / 2 ( 1 − 1 / 2 ) 2 = 2 \because \sum_{h=0}^{\propto}{\frac{h}{2^h}}=\frac{1/2}{(1-1/2)^2}=2 ∵∑h=0∝2hh=(1−1/2)21/2=2
∴ O ( n ∑ h = 0 └ lg n ┘ h 2 h ) = O ( n ∑ h = 0 ∝ h 2 h ) = O ( n ) \therefore O(n\sum_{h=0}^{\llcorner\lg{n}\lrcorner}{\frac{h}{2^h}})=O(n\sum_{h=0}^{\propto}{\frac{h}{2^h}})=O(n) ∴O(n∑h=0└lgn┘2hh)=O(n∑h=0∝2hh)=O(n)
因此,把一个无序数组构造成一个最大堆需要 O ( n ) O(n) O(n)
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)