本文我准备用Java实现堆排序。其实我以前在用二叉大顶堆实现优先队列的时候,就已经顺便实现了堆排序,今天把其中堆排序的代码提取出来,专门作为排序的一篇博文,并附上以前用二叉大顶堆实现的优先队列,以及顺便实现堆排序的博文地址:点我查看。具体的排序算法过程已经在注释里面了,大家可以复制代码到IDE里面,用DEBUG模式研究算法的过程:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
* @author LiYang
* @ClassName HeapSort
* @Description 堆排序算法
* @date 2019/11/5 16:04
*/
public class HeapSort {
//本例的堆排序,用优先队列来实现
//下面是二叉大顶堆的底层数据存储
private List<Integer> elementData;
/**
* 堆排序的带初始值的构造方法
* 也就是先拿待排序的数组的所有数据先初始化一个二叉大顶堆,
* 然后在这个大顶堆的基础上再进行一系列poll操作,创建有序序列
* @param initialArray 初始数据
*/
public HeapSort(int[] initialArray){
//调用初始化大顶堆的方法
elementData = initialPriorityHeap(initialArray);
}
/**
* 将已经存在的乱序数组,初始化为大顶堆elementData,用作构造方法
* 也就是说,效果就像乱序数组都add到了优先队列那样
* 如果是泛型,则接受ArrayList<T>,然后用compare方法实现
* @param arr 刚开始存在的数据,乱序的
* @return 已经放入arr数组的数字的优先队列
*/
private List<Integer> initialPriorityHeap(int[] arr){
//先将数组转化为ArrayList,方便后续操作
List<Integer> elementData = new ArrayList(arr.length);
//遍历加入
for (int i = 0; i < arr.length; i++) {
elementData.add(arr[i]);
}
//如果只有一个元素,甚至没有元素,就直接返回
if (elementData.size() <= 1){
return elementData;
}
//我们从最后一个元素的父元素开始,一个个尝试往下渗透
int maxParentIndex = elementData.size() / 2;
//从最后一个元素的父元素开始,开始逐一往下渗透
for (int index = maxParentIndex; index > 0; index--){
//取到父元素的值
int parentValue = elementData.get(index - 1);
//取到父元素的下标
int parentIndex = index;
//逐渐往下渗透,直到不能再下去了为止
while (parentIndex <= elementData.size() / 2){
//找到下标较小的子元素的下标
int smallerIndexSon = parentIndex * 2;
//找到下标较大的子元素的下标
int biggerIndexSon = smallerIndexSon + 1;
//如果下标较大的子元素不存在
if (biggerIndexSon > elementData.size()){
//就只跟下标较小的子元素相比
//如果移动上去的末尾元素比子元素小,就交换
if (parentValue < elementData.get(smallerIndexSon - 1)){
//二者交换
int temp = elementData.get(smallerIndexSon - 1);
elementData.set(smallerIndexSon - 1, elementData.get(parentIndex - 1));
elementData.set(parentIndex - 1, temp);
//更新下标为下标较小的子元素的下标
index = smallerIndexSon;
//否则,结束往下渗透
} else {
break;
}
//如果下标较大的子元素存在
} else {
//如果较大下标子元素比较小下标子元素大
if (elementData.get(biggerIndexSon - 1) > elementData.get(smallerIndexSon - 1)){
//尝试与较大下标子元素交换
if (elementData.get(biggerIndexSon - 1) > parentValue){
int temp = elementData.get(biggerIndexSon - 1);
elementData.set(biggerIndexSon - 1, elementData.get(parentIndex - 1));
elementData.set(parentIndex - 1, temp);
//更新下标为下标较大的子元素的下标
parentIndex = biggerIndexSon;
//如果子元素不小于较大下标元素,则停止向下渗透
} else {
break;
}
//如果较大下标元素不比较小下表元素大
} else {
//尝试与较小下标子元素交换
if (elementData.get(smallerIndexSon - 1) > parentValue){
int temp = elementData.get(smallerIndexSon - 1);
elementData.set(smallerIndexSon - 1, elementData.get(parentIndex - 1));
elementData.set(parentIndex - 1, temp);
//更新下标为下标较小的子元素的下标
parentIndex = smallerIndexSon;
//如果子元素不小于较大下标元素,则停止向下渗透
} else {
break;
}
}
}
}
}
//最后返回建好的大顶堆elementData
return elementData;
}
/**
* 从优先队列里面弹出优先级最高的元素,用作排序
* 注意,每次poll了之后,剩下的elementData的第一个是之前第二大的
* @return 返回优先级最高的元素,或者null
*/
public Integer poll(){
//如果没有元素了,返回空
if (elementData.size() == 0){
return null;
}
//如果只有一个元素,直接返回该元素
if (elementData.size() == 1){
return elementData.remove(0);
}
//先将第一个,也就是优先级最高的元素取出
int maxPriority = elementData.get(0);
//将最后一个元素取出来,并移除
int lastElement = elementData.remove(elementData.size() - 1);
//最后一个元素,加在队首,也就是第一个,其他的往后移
elementData.set(0, lastElement);
//如果最后一个元素不是最大的,得往下渗透
int index = 1;
//如果该元素还有子元素
while (index <= elementData.size() / 2){
//找到下标较小的子元素的下标
int smallerIndexSon = index * 2;
//找到下标较大的子元素的下标
int biggerIndexSon = smallerIndexSon + 1;
//如果下标较大的子元素不存在
if (biggerIndexSon > elementData.size()){
//就只跟下标较小的子元素相比
//如果移动上去的末尾元素比子元素小,就交换
if (lastElement < elementData.get(smallerIndexSon - 1)){
//二者交换
int temp = elementData.get(smallerIndexSon - 1);
elementData.set(smallerIndexSon - 1, elementData.get(index - 1));
elementData.set(index - 1, temp);
//更新下标为下标较小的子元素的下标
index = smallerIndexSon;
//否则,结束往下渗透
} else {
break;
}
//如果下标较大的子元素存在
} else {
//如果较大下标子元素比较小下标子元素大
if (elementData.get(biggerIndexSon - 1) > elementData.get(smallerIndexSon - 1)){
//尝试与较大下标子元素交换
if (elementData.get(biggerIndexSon - 1) > lastElement){
int temp = elementData.get(biggerIndexSon - 1);
elementData.set(biggerIndexSon - 1, elementData.get(index - 1));
elementData.set(index - 1, temp);
//更新下标为下标较大的子元素的下标
index = biggerIndexSon;
//如果子元素不小于较大下标元素,则停止向下渗透
} else {
break;
}
//如果较大下标元素不比较小下表元素大
} else {
//尝试与较小下标子元素交换
if (elementData.get(smallerIndexSon - 1) > lastElement){
int temp = elementData.get(smallerIndexSon - 1);
elementData.set(smallerIndexSon - 1, elementData.get(index - 1));
elementData.set(index - 1, temp);
//更新下标为下标较小的子元素的下标
index = smallerIndexSon;
//如果子元素不小于较大下标元素,则停止向下渗透
} else {
break;
}
}
}
}
//最后返回优先级最大的元素
return maxPriority;
}
/**
* 堆排序(通过二叉大顶堆的优先队列,实现堆排序)
* @param arr 排序前的数组
* @param increase 是否升序排序
* @return 返回排序后的数组
*/
public static int[] heapSort(int[] arr, boolean increase){
//先用排序前的数组,构建一个最大堆
HeapSort heapSort = new HeapSort(arr);
//声明一个空数组,用于装排好序的数组
int[] sortedArray = new int[arr.length];
/*
* 通过不断地poll,生成从大到小有序的序列,并放到上面的数组中
*/
//如果是升序排序
if (increase){
for (int i = sortedArray.length-1; i >= 0; i--) {
sortedArray[i] = heapSort.poll();
}
//如果是降序排序
} else {
for (int i = 0; i < sortedArray.length; i++) {
sortedArray[i] = heapSort.poll();
}
}
//返回排好序的数组
//如果是小顶堆实现,则poll出来的就是从小到大
return sortedArray;
}
/**
* 堆排序(HeapSort)的驱动程序
* @param arr 待排序数组
* @return 堆排序完成后的有序数组
*/
public static int[] heapSort(int[] arr) {
//调用升序的堆排序,并将排好序的数组返回
return heapSort(arr, true);
}
/**
* 验证堆排序算法
* @param args
*/
public static void main(String[] args) {
//待排序数组
int[] arr = new int[30];
//随机数类
Random random = new Random();
//随机生成排序数组(100以内的整数)
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(100);
}
//打印待排序数组
System.out.println("堆排序前:" + Arrays.toString(arr));
//进行堆排序(调用驱动程序,就是只有一个参数的heapSort()方法)
//注意,堆排序跟之前的排序不太一样,要返回排好序的数组
//而不是最后有序的数组就是原来的数组
int[] heapSortedArr = heapSort(arr);
//这里可以将排好序的数组,重新赋给原来的数组,保持之前的操作
arr = heapSortedArr;
//打印堆排序后的数组
System.out.println("堆排序后:" + Arrays.toString(arr));
}
}
运行 HeapSort 类的main方法,堆排序算法测试通过:
堆排序前:[49, 59, 18, 34, 7, 23, 98, 33, 37, 36, 78, 54, 12, 78, 14, 83, 57, 90, 96, 76, 1, 39, 32, 97, 11, 35, 28, 35, 54, 99]
堆排序后:[1, 7, 11, 12, 14, 18, 23, 28, 32, 33, 34, 35, 35, 36, 37, 39, 49, 54, 54, 57, 59, 76, 78, 78, 83, 90, 96, 97, 98, 99]