1.概念
是一种树形选择排序,在排序过程中,将待排序的记录r[1..n]看成是一颗完全二叉树的顺序存储结构,
利用完全二叉树中的双节点和孩子节点之间的内在关系,在当前无序的序列中选择关键字最大或最小的记录。
2.思路
实现堆排序需解决两个问题:
(1)如何将n 个待排序的数建成堆;
(2)输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。
建堆方法(小根堆):
对初始序列建堆的过程,就是一个反复进行筛选的过程。
n 个结点的完全二叉树,则最后一个结点是第n/2个结点的子树。
筛选从第n/2个结点为根的子树开始(n/2是最后一个有子树的结点),使该子树成为堆。
之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。
3.代码实现
/**
* 调整为小根堆(排序后结果为从大到小)
* @param arr 待调整的堆数组
* @param index 是待调整的数组元素的位置
* @param length 数组的长度
*/
public void minHeap(int[] arr, int length,int index){
int temp = arr[index];
int child = 2 * index + 1;
System.out.println("待调整父节点:arr["+index+"] ="+temp);
while (child < length){
if(child + 1 < length && arr[child + 1] < arr[child]){
child = child + 1;
}
System.out.println("将与子孩子 array[" + child + "] = " + arr[child] + " 进行比较");
if(arr[child] < arr[index]){
System.out.println("子孩子比父节点小,交换位置");
int flag = arr[child];
arr[child] = arr[index];
arr[index] = flag;
index = child;
child = 2 * index + 1;
if(child >= length){
System.out.println("没有子孩子了,调整结束");
break;
}else{
System.out.println("继续与新的子孩子进行比较");
}
}else{
System.out.println("子孩子都比父节点大,调整结束");
break;
}
}
}
/**
* 调整为大根堆(排序后结果为从小到大)
* @param arr 待调整的堆数组
* @param index 是待调整的数组元素的位置
* @param length 数组的长度
*/
public void maxHeap(int[] arr, int length,int index){
int temp = arr[index];
int child = 2 * index + 1;
System.out.println("待调整父结点为:array[" + index + "] = " + temp);
while (child < length){
if(child + 1 < length && arr[child + 1] > arr[child]){
child = child + 1;
}
System.out.println("将与子孩子 array[" + child + "] = " + arr[child] + " 进行比较");
if(arr[child] > arr[index]){
System.out.println("子孩子比父节点大,交换位置");
int flag = arr[child];
arr[child] = arr[index];
arr[index] = flag;
index = child;
child = 2 * index + 1;
if(child >= length){
System.out.println("没有子孩子了,调整结束");
break;
}else{
System.out.println("继续与新的子孩子进行比较");
}
}else{
System.out.println("子孩子都比父节点小,调整结束");
break;
}
}
}
/**
* 堆排序算法
* @param arr
* @param inverse true 为倒序排列,false 为正序排列
*/
public int[] heapSort(int[] arr,boolean inverse){
System.out.println("初始化堆开始");
for (int i = (arr.length - 1) / 2; i>=0; i--) {
if(inverse){
minHeap(arr,arr.length,i);
}else{
maxHeap(arr,arr.length,i);
}
}
System.out.println("初始化堆结束");
for (int i = arr.length - 1; i > 0 ; i--) {
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
if(inverse){
minHeap(arr,i,0);
}else{
maxHeap(arr,i,0);
}
}
return arr;
}
4.测试
@Test
public void test(){
int arr[] = {6,3,8,2,9,1};
System.out.println("排序前");
printArr(arr);
int[] data = heapSort(arr,false);
System.out.println("排好序后");
printArr(data);
}
public void printArr(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
5.总结
特点:
(1)是不稳定的排序
(2)只能用于顺序结构,不能用于链式结构
(3)初始建堆所需要的比较次数较多,因此记录数较少时不宜采用。
堆排序在最坏情况下的时间复杂度是O(nlog2n),相对于快速最坏情况下的O(n*n)而言是一个优点,当记录较多时较为高效。