算法系列:(二).java:N叉樹堆排序基本原理與實例

堆排序(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判斷語句;

另外:

    帶對象類型參數的方法第一步判空等,都是一個程序員的基本修養......

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章