堆排序(Heapsort)概念:是指利用堆這種數據結構所設計的一種排序算法;
堆(英語:heap) 概念:計算機科學中一類特殊的數據結構的統稱。堆通常是一個可以被看做一棵樹的數組(集合)對象。
原理圖(圖示爲三叉樹):
擴展說明:
堆排序的每次新建堆排序其實是"冒泡排序"和"父節點和子節點間排序"的綜合結果!
若父節點和子節點間採取的"排序方式2",則:
1. 當N叉樹的N趨近於1,導致層數越多,每個父節點的子節點數越少的時候. "冒泡排序"作用的次數就越多,而當N=1的時候,只剩層結構,則完全是"冒泡排序"起作用,即此時"堆排序"等於"冒泡排序".
2. 當N趨近於數組總長度,每個父節點的子節點越多,層越少排序方式2"作用的次數就越多,而當N等於數組長度-1的時候,實際是一個父節點和子節點的排序問題,此時堆排序等於"排序方式2".
注: "排序方式2"極值篩選效率越高,則整體堆排序越快,因爲每次父子節點間排序僅需要知道極值即可;
以下爲採用選擇排序作爲"排序方式2"的數組堆排序和集合堆排序代碼:(java)
數組堆排序:
import java.util.Arrays;
import java.util.Objects;
/**
* 利用N叉樹對int數組進行堆排序(更改數組類型可應用到char byte short long float double數組,
* 當數組長度比 N 小時,實際就是直接選擇排序;
*/
public class Test04_HeapSort {
private static final int FORK_OF_NUMBER=5;//N叉樹的N設爲常量值
//程序入口
public static void main(String[] args) {
int[] ints={42,31,34,3,56,78,90,12,34,56,78,2,12,1};//等待排序數組
heapSort(ints);//調用排序方法,參數即等待排序數組
System.out.println(Arrays.toString(ints));//輸出結果
}
//排序方法
private static void heapSort(int[] ints){
int length = ints.length;//數組長度
//循環次數等於數組個數
for (int heapSortTime = 0; heapSortTime < length; heapSortTime++) {
Objects.requireNonNull(ints,"數組爲空,哥們兒!");//判空
int sortLength=length-heapSortTime;//新N叉樹元素個數,即剩餘需要排序元素個數
int last=(sortLength-1)%FORK_OF_NUMBER;//末尾不成整樹叉的子節點數量
int times=(last==0)?(sortLength-1)/FORK_OF_NUMBER:((sortLength-1)/FORK_OF_NUMBER)+1;//父節點數量,不完整父節點也算
//循環父節點數個次數
for (int time = 1; time <= times; time++) {
//循環子節點數次
for (int i = 0; i < (last==0?FORK_OF_NUMBER:last); i++) {
//第一個子節點下標
int firstSonForkIndex=(last==0)?sortLength-FORK_OF_NUMBER*time+heapSortTime:sortLength-FORK_OF_NUMBER*(time-1)-last+heapSortTime;
//父節點下標
int fatherForkIndex=(firstSonForkIndex-heapSortTime+FORK_OF_NUMBER-1)/FORK_OF_NUMBER-1+heapSortTime;
//選擇排序,將極值放到父節點
if(ints[firstSonForkIndex+i]<ints[fatherForkIndex]){
int term=ints[firstSonForkIndex+i];
ints[firstSonForkIndex+i]=ints[fatherForkIndex];
ints[fatherForkIndex]=term;
}
}
}
}
}
}
集合堆排序:(由於集合可以使用泛型,所以可以排序任意類型元素的List集合,(但調用方法時需要重寫Comparator接口的compare(T,T)方法),
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Objects;
/**
* 利用N叉樹對List集合進行堆排序,只要定義排序方法的Comparator比較規則(new Comparator或者用
* Lambda表達式皆可),當集合長度比 N 小時,實際就是直接選擇排序;
*/
public class Test04_HeapSort_AnyArray {
private static final int FORK_OF_NUMBER=77;//N叉樹的N設爲常量值
//程序入口
public static void main(String[] args) {
ArrayList<Integer> arrayList=new ArrayList<>();//定義等待排序集合
arrayList.add(6);//添加元素
arrayList.add(2);//添加元素
arrayList.add(5);//添加元素
arrayList.add(90);//添加元素
arrayList.add(1);//添加元素
arrayList.add(2);//添加元素
heapSort(arrayList,(o1,o2) -> o2-o1);//調用排序方法,Lambda表達式傳入Comparator比較器
System.out.println(arrayList);//輸出排序結果
}
private static <T> void heapSort(ArrayList<T> arrayList, Comparator<T> cp){
int length = arrayList.size();//集合元素個數
//循環次數等於集合元素個數
for (int heapSortTime = 0; heapSortTime < length; heapSortTime++) {
Objects.requireNonNull(arrayList,"數組爲空,哥們兒!");//判空
int sortLength=length-heapSortTime;//新N叉樹元素個數,即剩餘需要排序元素個數
int last=(sortLength-1)%FORK_OF_NUMBER;//末尾不成整樹叉的子節點數量
int times=(last==0)?(sortLength-1)/FORK_OF_NUMBER:((sortLength-1)/FORK_OF_NUMBER)+1;//父節點數量,不完整父節點也算
//循環父節點數個次數
for (int time = 1; time <= times; time++) {
//循環子節點數次
for (int i = 0; i < (last==0?FORK_OF_NUMBER:last); i++) {
//第一個子節點下標
int firstSonForkIndex=(last==0)?sortLength-FORK_OF_NUMBER*time+heapSortTime:sortLength-FORK_OF_NUMBER*(time-1)-last+heapSortTime;
//父節點下標
int fatherForkIndex=(firstSonForkIndex-heapSortTime+FORK_OF_NUMBER-1)/FORK_OF_NUMBER-1+heapSortTime;
//選擇排序,將極值放到父節點
if(cp.compare(arrayList.get(firstSonForkIndex+i),arrayList.get(fatherForkIndex))>0){
T term=arrayList.get(firstSonForkIndex+i);
arrayList.set(firstSonForkIndex+i,arrayList.get(fatherForkIndex));
arrayList.set(fatherForkIndex,term);
}
}
}
}
}
}
編程思想:
1. 演示說明了堆排序的基本實現原理,指出了其實際爲兩種排序綜合作用結果的特點;
2. 在集合排序代碼中,排序方法加入Comparator比較器接口,可供調用者自定義比較元素方式;
3. 採用三目運算符根據子節點數確定底層循環次數,減少使用if else判斷語句;
另外:
帶對象類型參數的方法第一步判空等,都是一個程序員的基本修養......