1.堆排序概念基本介绍:
重要:
2.堆排序算法思想:
3.堆排序代码实现:
堆就是一颗完全二叉树,又分为大顶堆和小顶堆,堆排序的时间复杂度为O(n*logn)线性对数阶
800万的数据只需要3秒就能完成排序。
堆排序完整代码如下:
package com.bruce.sorting;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Random;
/**
* 用大(小)顶堆,给无序数组排序,整个过程中,是没有见过树的。时间复杂度O(nlogn),并且其时间复杂度是不稳定的
* 大顶堆: 升序排序
* 小顶堆: 降序排序
* @author bwang018
* 思想简述:以升序排列为例,把一个数组(例如arr[ 0~10])看成一个完全二叉树,将这个完全二叉树调成为一个大顶堆,然后将堆顶元素与数组最后一个元素交换;
* 然后将arr[0~9]重复以上过程,直到最后剩下2个元素,变成大顶堆后再交换。
*/
public class BigPileTop {
public static void main(String[] args) {
// //升序排列
// int arr[]= {4,6,8,5,9};
// heapSort(arr);
// System.out.println(Arrays.toString(arr));
//要求将数组进行升序排序
//创建有800万数据的随机无序数组
int [] arr = new int[8000000];
for (int i=0; i < arr.length;i++){
arr[i] = (int) (Math.random() * 8000000); //生成一个[0,8000000)的随机无序数组
}
System.out.println("排序前");
Date dataStart = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
String dataStartStr = format.format(dataStart);
System.out.println("排序前的时间是:" + dataStartStr);
//开始排序
heapSort(arr);
Date dataEnd = new Date();
String dataEndStr1= format.format(dataEnd);
System.out.println("排序后的时间是:" + dataEndStr1);
// System.out.println("排序后:" + Arrays.toString(arr));
}
//堆排序方法
public static void heapSort(int[] arr) {
int temp=0;
//根据升序降序要求,先将数组转换为一个大或小顶堆的顺序。
//这里是升序排序,所以用大顶堆
//****这里每次调整一个非叶子节点对应的子树,从最左侧的非叶子节点依次调整****。
//举例,数组arr[4,6,8,5,9],第一次调整arr[1]对应的子树,第二次调整arr[0]对应的子树
for(int i=arr.length/2-1;i>=0;i--) {
adjustHeap(arr,i,arr.length);
}
//将堆顶元素arr[j]与末尾元素交换,将最大元素沉到数组末端。
//重新调整结构,使其满足堆定义,然后继续交换栈顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
for(int j=arr.length-1;j>0;j--) {
temp=arr[j];
arr[j]=arr[0];
arr[0]=temp;
adjustHeap(arr,0,j); //为什么直接为对堆顶元素调整??每次调整都是一步到位
}
}
/*
* 将以数组元素i对应的非叶子节点的子树调整为大顶堆
* 将数组调整成大顶堆的方法:将以i对应的非叶子节点树(子树)调整成大顶堆
*
* arr:待调整数组
* i:非叶子节点在数组中索引
* length:对数组中前多少个元素进行调整,length是在逐渐的减少
*/
public static void adjustHeap(int arr[],int i,int length) {
int temp=arr[i];//取出当前元素,保存在临时变量
//k=i*2+1,k是i节点的左子节点
for(int k=i*2+1;k<length;k=k*2+1) {
if(k+1<length && arr[k]<arr[k+1]) { //比较左、右子节点的大小
k++;//k指向右子节点
}
if(arr[k]>temp) {//如果子节点大于父节点
arr[i]=arr[k];//把较大的值赋值给当前节点,保证父节点比子节点大
i=k;//重要:i指向k,继续循环比较(比较难以理解,其实就是跟自己的左或右子树交换之后,左、右子树又不能保证自己比自己的子节点大,所以需要处理)
}else {
break; //如果不交换,那么就无需做什么了,因为是从左到右,从下到上调整的,下面的子树本来就是大顶堆了
}
}
//当for循环结束后,我们已经将i为父节点的这个树的最大值,放在了最顶上(i的位置),这颗子树已经是大顶堆
arr[i]=temp;//将temp赋值放到调整后的位置
}
}
其中,adjustHeap(...)方法是将每个非叶子节点对应的子树调整为一个大顶堆。
例如下图中的树,第一次调用adjustHeap(...)方法,先将节点6(arr[1])所在的子树调整为大顶堆;然后第二次调用adjustHeap(...)方法,将4(arr[0])做为根节点的整棵树调整为大顶堆。
调整之后:
调用adjustHeap(...),第一次将整棵树调整为大顶堆:
第一次调整的完整代码:将数组调整为9,6,8,5,4