本文我準備用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]