简单选择排序是蛮力算法,时间复杂度为O(n*(n-1)/2)=O(n2)。
选择排序做了大量重复性的无用功。如{5, 6,7,2}将5与6、7、2比较和换位得{2, 6,7,5},而第二趟时6与5将会再次比较,显然重复。
可以借鉴锦标赛的一些作法使每次比较都有意义。树形选择排序(tree selection sort)或锦标赛排序(tournament sort)核心是利用树形结构将已经进行过的比较信息保留,但是用数组保存比较信息,需要辅助空间。1964年J.willioms提出的堆(heap),(注:这个概念和日常英语中的heap和内存分配中的heap相差甚远)通过调整arr元素的顺序,让它包含比赛过程的信息。
堆是一种重要的数据结构,这里使用int[]构建最大堆(排序时通常使用最大堆)。堆(heap):树形为完全二叉树,若任一父节点保存的值大于等于孩子的值,称为最大(二叉)堆(max heap)。
首先,将int[] arr的元素想象/构造成一个完全二叉树。arr[0]在完全二叉树第一层,arr[1]和arr[2]在第二层,arr[3 4 5 6]在第三层...
任何数组按照1、2、4、8个元素一层(最下一层可以不足),可以想象/构造成一个完全二叉树。构成完全二叉树后,父节点与它的孩子结点的索引之间,存在如下关系:
- 树根的索引1;注意:为了方便下面的计算,完全二叉树采用基于1的索引。故编程时必须注意基于1的索引与数组的基于0的索引之间的转换。
- 索引为i的结点,它的左孩子的索引为2i,右孩子的索引为2i+1;
- 任一孩子结点(索引为i)的父节点的索引为i/2(Java整除)。(再次提醒:索引基于1)
堆排序Heap Sort就是将待排序的arr构建成一个最大堆,(最大堆意味着经过一系列的比赛产生了冠军——堆顶),将堆顶元素与最后元素交换,堆顶获得其最终的位置;剩下的元素重新建最大堆,将堆顶元素与本堆最后元素交换,重复上述步骤。
堆化Heapify
private static void buildHeap(int[] array){
for(int i = array.length/2;i>0;i--){//基于1的索引,所以i>0而非等于0。从最后一个的中间结点开始直到根结点
maxHeapify(array, i);p(i+" ");pln(array);
}
}
maxHeapify(int[] arr,int index)的工作,对index为根的子树,比较父节点与左右孩子节点的3个值,将最大的值交换到父节点的位置,并对受影响的孩子节点,递归检查是否满足最大堆的性质。
/**
* 大堆化。
* 当前根节点与左右孩子节点的key相比较,将大的值放在树根的位置;
* 并递归地对受影响的孩子节点大堆化。
* T (n) = O(lgn) ,因为T (n) ≤ T(2n/3) + Θ(1)
* @ array 待大堆化的数组。
* @ index 当前根节点的索引,基于1.
*/
private static void maxHeapify(int[] array, int index){
//int heapSize = array.length;//递归时,将被注释掉。
int iL = index * 2;//index of left child
int iR = index * 2 + 1;
int iMax = index;//树根与左右孩子节点的key相比较,保存最大值的索引
if( iL<=heapSize && array[iL-1]>array[index-1]){//基于1 Vs 0
iMax = iL;
}
if( iR<=heapSize && array[iR-1]>array[iL-1]){
iMax = iR;
}
//获得了iMax
if (iMax != index) {
IntSort.swap(array ,index-1,iMax-1 );
maxHeapify(array, iMax);//只处理受影响的孩子节点
}
}
可以测试:
int[] arr = new int[] { 4,1,3,2,16,9,10,14,8,7 };
pln(arr);
buildHeap(arr);
}
5 [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
4 [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
3 [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
2 [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
1 [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
package algorithm.sorting;
import static tips.Print.*;
public class HeapSort extends IntSort{
static int heapSize;
/**
* 时间复杂度:平均O(nlgn),最坏情况O(nlgn)。
*/
@Override public int[] sort(int[] arr){
heapSize = arr.length;
buildHeap(arr);
for(int i = arr.length;i>0;i--){//i to 1.基于1
IntSort.swap(arr , 0, i-1 );//基于0
heapSize--;
maxHeapify(arr, 1);
}
return arr;
}
private static void buildHeap(int[] array){
//
}
private static void maxHeapify(int[] array, int index){
//
}
}