算法系列:(二).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判断语句;

另外:

    带对象类型参数的方法第一步判空等,都是一个程序员的基本修养......

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