堆排序
一、排序介绍
堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
- 子结点小于父结点就是大顶堆
- 反之就是小顶堆
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。且是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
二、堆排序实现
堆数据结构实现方式,不了解的可以看看往前博客,这边主要来讲排序的实现方式(其实并没有多出什么内容)
给出一个如下数组:Integer[] a = {0,-3,-7,-1,9,7,5,8,11,-5,-8};
- 思路:
- 对给出的数组序列,重新排序,使其该数组实现大顶堆性质。
- 根据堆性质取出最大值,存放到数组最后,当然该数组的最后,下一次就不用再去处理了
代码实现:
- 转换成堆思路
- 从序列中间位置开始往前操作,每次取一个值与他的左右子树进行比较,大的值上来,最终该数组序列将为一个大顶堆
- 堆排序思路
- 首先取顶部结点与最后一个结点交换位置,此时最后一个结点位置肯定是最大的
- 排好序的位置就不必要在处理了
- 然后让此时的顶部节点进行下沉到正确位置,大的就浮上来
- 继续执行以上逻辑,直到剩两个元素时,就可以结束排序了
package datastructure.heap;
import jdk.nashorn.internal.ir.CallNode;
public class HeapSort {
public static void sort (Comparable[] array) {
Comparable[] heap = conversionHeap(array);
heapSort(heap);
}
// 转换成堆
public static Comparable[] conversionHeap (Comparable[] heap) {
int index = heap.length / 2 - 1;
for (int i = index; i >= 0; i--) {
int k = i; // 作为父结点
for (int j = k; j <= heap.length / 2 - 1;) {
if (j * 2 + 2 < heap.length) {
// 比较左右子结点
if (less(heap,2*j+1,2*j+2)) {
j = 2 * j + 2;
} else j = 2 * j + 1;
}else j = 2 * j + 1;
if (less(heap,k,j))
swap(heap,k,j);
k = j;
}
}
return heap;
}
// 对堆进行排序
public static void heapSort (Comparable[] heap) {
int endItem = heap.length - 1; // 排序所有元素
for (int topItem = 0; endItem >= topItem;) {
// 到只剩两个元素时就不可以不讲道理直接换,
// 否则可能出现: [31, 10, 34, 36, 45, 45, 60, 60, 69, 76]
if (endItem == 1) {
if (!less(heap,topItem,endItem)) {
swap(heap,topItem,endItem);
}
break;
}
swap(heap, topItem, endItem);
endItem--; // 排好序的位置,就不必再排序
int k = topItem; // 对原本最后的结点进行下沉
// 下沉
for (int j = k; j < endItem / 2;) {
if (heap[2 * j + 2] != null) {
if (less(heap, 2 * j + 1, 2 * j + 2)) {
j = 2 * j + 2;
} else j = 2 * j + 1;
} else j = 2 * j + 1;
if (less(heap,k,j))
swap(heap,k,j);
k = j;
}
}
}
// 交换两个位置上的数据
private static void swap (Comparable[] heap, int i, int j) {
Comparable t = heap[i];
heap[i] = heap[j];
heap[j] = t;
}
// 比较堆中两个索引位置上的数据
private static boolean less (Comparable[] heap, int i, int j) {
return heap[i].compareTo(heap[j]) < 0;
}
}
- 上面两个方法对于下沉算法都进行了具体实现,大伙可以尝试封装到一个方法中!
- 具体感觉还有点瑕疵,但是说不上来,有问题的话欢迎讨论
三、对以上堆排序进行测试
public class TestHeapSort {
public static void main(String[] args) {
Integer[] a = {0,-3,-7,-1,9,7,5,8,11,-5,-8};
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
HeapSort.sort(a);
System.out.println(Arrays.toString(a));
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
}
}
输出:
普通家用笔记本测试80万条随机数据:
public class TestHeapSort {
public static void main(String[] args) {
Integer[] a = new Integer[800000];
for(int i = 0;i < a.length;i++){
a[i] = (int)(Math.random()*800000);
}
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
HeapSort.sort(a);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
}
}
输出: