堆排序(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判断语句;
另外:
带对象类型参数的方法第一步判空等,都是一个程序员的基本修养......