堆排序 实战算法分享,比较器的使用,各种排序算法总结

1 篇文章 0 订阅

堆排序 实战算法分享

前言

关于排序算法,很多文章都已经深入分析过,堆排序其空间复杂度为O(1),时间复杂度为O(nlogn),在众多排序算法种可以说性能比较优越。
浏览众多的网上文章,未找到关于排序算法实战算法的文章。最近由于生产环境性能提示需求,去除了使用MySql数据order by的数据库排序功能,必须要做jdk内存中排序,现在将排序算法分享如下。

堆排序的过程

堆排序实际是对一个数组进行内部元素置换,不需额外占用空间,仅消耗点数组内元素比较遍历的时间,期算法可分为如下几步:
1.构建堆。
堆其实是二叉树型结构,一个数组可以按照如下规则构建堆:
i元素为二叉树的父节点,2i+1为左孩子节点,2i+2为右孩子节点。
2.调整堆为顶堆。
顶堆的意思是堆顶的元素要么最大称大顶堆,升序排序使用;要么最小称小顶堆,降序排序使用。
本文以下以升序,大顶堆为例。
调整的过程为对父,左,右三个节点比较,将最大值调整到堆顶。
循环过程从数组长度的一半开始,递减。
3. 调整为大顶后,将堆顶的元素和堆尾的元素交换。
4. 缩小范围,将堆尾的元素剔除,继续重复2,3过程。直到整个数组变得有序。

上面的文字可能不太形象,下面用图示说明一下:
我们以待排序数组[2,3,1,4]为例,进行升序排列。

1.构建堆 按照 i为父节点,2i+1为左孩子节点,2i+2为有孩子节点
2.按照大小调整堆元素
按照大小交换父子节点位置
3.整个堆调整为大顶堆
继续调整堆,直到调整成大顶堆
4.交换堆顶与堆尾元素位置,并缩小堆排序范围
交换堆顶与堆尾元素,提出堆尾元素
5、继续排序剩下的堆
在这里插入图片描述
在这里插入图片描述
6、直到全部调整完毕,则排序完成
堆调整完成排序完成

代码实战

1、主方法

/**
     *  对象集合,根据对象的某一个属性排序
     * @param array 对象集合
     * @param fieldName 对象属性名称
     * @param isAsc 是否升序
     * @param <T> 对象
     * @throws Exception
     */
    public static <T> void heapSort(List<T> array, String fieldName, boolean isAsc){
        if(CollectionUtils.isEmpty(array)){
            return;
        }
        try {
            buildTopHeap(array, fieldName, isAsc); // 构建顶堆
            for (int i = array.size() - 1; i >= 1; i--) { //调整
                Collections.swap(array, 0, i);
                Object obj = array.get(i);
                changeHeap(array, obj.getClass(), fieldName, i, 0, isAsc);
            }
        }catch (Exception e){
            return;
        }
    }

2、构建顶堆

public static <T> void buildTopHeap(List<T> array, String fieldName, boolean isAsc) throws Exception{
        if (array == null || array.size() <= 1) {
            return;
        }
        int half = array.size() / 2; // 从len/2 开始遍历
        for (int i = half; i >= 0; i--) {
            Object obj = array.get(i);
            changeHeap(array,obj.getClass(),fieldName, array.size(), i,isAsc); //调整堆位置
        }
    }

3、交换堆元素(递归)

public static <T> void changeHeap(List<T> array,Class<?> clazz, String fieldName, int heapSize, int index,boolean isAsc) throws Exception{
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        int findedIndex = index;

        if(isAsc){//升序
            if (left < heapSize && compare(array.get(left) ,array.get(findedIndex), clazz, fieldName)>0) {
                findedIndex = left;
            }
            if (right < heapSize && compare(array.get(right) ,array.get(findedIndex), clazz, fieldName)>0) {
                findedIndex = right;
            }
        } else {//降序
            if (left < heapSize && compare(array.get(left) ,array.get(findedIndex), clazz, fieldName)<0) {
                findedIndex = left;
            }
            if (right < heapSize && compare(array.get(right) ,array.get(findedIndex), clazz, fieldName)<0) {
                findedIndex = right;
            }
        }
        if (index != findedIndex) {
            Collections.swap(array,index, findedIndex);
            changeHeap(array,clazz,fieldName, heapSize, findedIndex, isAsc);
        }
    }

4、辅助方法


    public static int compare(Object original,Object another, Class<?> clazz, String fieldName) throws Exception{
        Object originalFieldValue = getFieldValue(original, clazz, fieldName);
        Object anotherFieldValue =  getFieldValue(another, clazz, fieldName);
        int result =  compareObject(originalFieldValue,anotherFieldValue);
        return result;
    }

    public static Object getFieldValue(Object original, Class<?> clazz, String fieldName) throws Exception{
        if(original == null){
            throw new ClassNotFoundException("original not exist.");
        }
        Object fieldNameValue = null; //字段名值
        List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : fields) {
            if(fieldName.equals(field.getName())){
                field.setAccessible(true);
                fieldNameValue = field.get(original);
                break;
            }
        }
        return fieldNameValue;
    }

    public static int compareObject(Object original,Object another){
        if(original instanceof Integer && another instanceof Integer){
            Integer originalInt = (Integer)original;
            Integer anotherInt = (Integer)another;
            return originalInt.compareTo(anotherInt);
        }else if(original instanceof String && another instanceof String){
            String originalStr = (String)original;
            String anotherStr = (String)another;
            return originalStr.compareToIgnoreCase(anotherStr);
        }else if(original instanceof Double && another instanceof Double){
            Double originalStr = (Double)original;
            Double anotherStr = (Double)another;
            return originalStr.compareTo(anotherStr);
        }else if(original instanceof Long && another instanceof Long){
            Long originalStr = (Long)original;
            Long anotherStr = (Long)another;
            return originalStr.compareTo(anotherStr);
        }else if(original instanceof Byte && another instanceof Byte){
            Byte originalStr = (Byte)original;
            Byte anotherStr = (Byte)another;
            return originalStr.compareTo(anotherStr);
        }else if(original instanceof BigDecimal && another instanceof BigDecimal){
            BigDecimal originalStr = (BigDecimal)original;
            BigDecimal anotherStr = (BigDecimal)another;
            return originalStr.compareTo(anotherStr);
        }
        return 0;
    }

5、测试一把


    @Test
    public void sortTest() throws Exception {
        int[] array = {9, 8, 7, 56, 5, 4, 3, 2, 1, 0, -1, 203, -3};

        System.out.println("Before heap:");
        ArrayUtil.printArray(array);

        SortUtil._heapSort(array);

        System.out.println("After heap sort:");
        ArrayUtil.printArray(array);

        List<AA> aaList = Lists.newArrayList();
        aaList.add(new AA(1, "a"));
        aaList.add(new AA(98, "a"));
        aaList.add(new AA(54, "c"));
        aaList.add(new AA(2, "b4ruy"));
        aaList.add(new AA(5, "r"));
        aaList.add(new AA(21, "brj"));
        aaList.add(new AA(35, "sdhr"));
        aaList.add(new AA(12, "fdb"));
        aaList.add(new AA(5, "r"));
        aaList.add(new AA(90, "g"));
        aaList.add(new AA(-1, "sadgc"));
        SortUtil.heapSort(aaList, "name");
        System.out.println("list After heap sort:");
        ArrayUtil.printArray(aaList);
    }

    public static class AA {
        public Integer id;
        public String name;

        public AA() {
        }

        public AA(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }
    }

6、结果

list After heap sort:
[
{"id":98,"name":"a"}
{"id":1,"name":"a"}
{"id":2,"name":"b4ruy"}
{"id":21,"name":"brj"}
{"id":54,"name":"c"}
{"id":12,"name":"fdb"}
{"id":90,"name":"g"}
{"id":5,"name":"r"}
{"id":5,"name":"r"}
{"id":-1,"name":"sadgc"}
{"id":35,"name":"sdhr"}
]

使用比较器Comparator<? super T> c 改造

完成上述代码变成之后,心里稍稍有点成就感,但是看看排序算法的调用就知道还有瑕疵,如下:
SortUtil.heapSort(aaList, “name”);
第二个参数还是用字符串来标记对象的属性名称,太不友好了。
追求完美的程序员怎么会就此罢手呢,阅读jdk自带的排序算法:
jdk自带的算法写法如下,第二个参数是一个比较器
Collections.sort(aaList, Comparator.comparing(AA::getHeight));

读完源码后,决定使用比较器Comparator对算法进行改造。
废话少说,直接贴出代码:


/***
 * 排序工具类
 * 1、对象集合 堆排序算法
 */
public class SortUtil {

    /***
     *  对象集合,根据对象的某一个属性做升序排序
     * @param array  对象集合
     * @param c Comparator 确定列表顺序的比较器
     * @param <T> 对象
     * @throws Exception
     */
    public static <T> void heapSort(List<T> array, Comparator<? super T> c){
        heapSort(array, c, true);
    }

    /**
     *  对象集合,根据对象的某一个属性排序
     * @param array 对象集合
     * @param c Comparator 确定列表顺序的比较器
     * @param isAsc 是否升序
     * @param <T> 对象
     * @throws Exception
     */
    public static <T> void heapSort(List<T> array, Comparator<? super T> c, boolean isAsc){
        if(CollectionUtils.isEmpty(array)){
            return;
        }
        try {
            buildTopHeap(array, c, isAsc);
            for (int i = array.size() - 1; i >= 1; i--) {
                Collections.swap(array, 0, i);
                Object obj = array.get(i);
                changeHeap(array, obj.getClass(), c, i, 0, isAsc);
            }
        }catch (Exception e){
            return;
        }
    }

    /***
     * 构建顶堆
     * isAsc=true 构建大顶堆
     * isAsc=false 构建小顶堆
     * @param array
     * @param isAsc
     */
    public static <T> void buildTopHeap(List<T> array, Comparator<? super T> c, boolean isAsc) throws Exception{
        if (array == null || array.size() <= 1) {
            return;
        }
        int half = array.size() / 2;
        for (int i = half; i >= 0; i--) {
            Object obj = array.get(i);
            changeHeap(array,obj.getClass(),c, array.size(), i,isAsc);
        }
    }

    /**
     * 调整堆
     * @param array
     * @param heapSize
     * @param index
     */
    public static <T> void changeHeap(List<T> array,Class<?> clazz, Comparator<? super T> c, int heapSize, int index,boolean isAsc) throws Exception{
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        int findedIndex = index;

        if(isAsc){//升序
            if (left < heapSize && c.compare(array.get(left) ,array.get(findedIndex))>0) {
                findedIndex = left;
            }
            if (right < heapSize && c.compare(array.get(right) ,array.get(findedIndex))>0) {
                findedIndex = right;
            }
        } else {//降序
            if (left < heapSize && c.compare(array.get(left) ,array.get(findedIndex))<0) {
                findedIndex = left;
            }
            if (right < heapSize && c.compare(array.get(right) ,array.get(findedIndex))<0) {
                findedIndex = right;
            }
        }
        if (index != findedIndex) {
            Collections.swap(array,index, findedIndex);
            changeHeap(array,clazz,c, heapSize, findedIndex, isAsc);
        }
    }
}

测试一下,调用测试代码:


    @Test
    public void sortTest() throws Exception {
        int[] array = {9, 8, 7, 56, 5, 4, 3, 2, 1, 0, -1, 203, -3};

        System.out.println("Before heap:");
        ArrayUtil.printArray(array);

        SortUtil._heapSort(array);

        System.out.println("After heap sort:");
        ArrayUtil.printArray(array);

        List<AA> aaList = Lists.newArrayList();
        aaList.add(new AA(1, "a",(float)107.2));
        aaList.add(new AA(98, "a",(float)197.2));
        aaList.add(new AA(54, "c",(float)7.2));
        aaList.add(new AA(2, "b4ruy",(float)151.1));
        aaList.add(new AA(5, "r",(float)17.2));
        aaList.add(new AA(21, "brj",(float)7.2));
        aaList.add(new AA(35, "sdhr",(float)475.0998));
        aaList.add(new AA(12, "fdb",(float)1017.2));
        aaList.add(new AA(5, "r",(float)65.9));
        aaList.add(new AA(90, "g",(float)38.111));
        aaList.add(new AA(-1, "sadgc",(float)172.2));
        SortUtil.heapSort(aaList, Comparator.comparing(AA::getHeight));
        System.out.println("list After heap sort:");
        ArrayUtil.printArray(aaList);
    }

    public static class AA {
        public Integer id;
        public String name;
        public float height;

        public AA() {
        }

        public AA(Integer id, String name,float height) {
            this.id = id;
            this.name = name;
            this.height = height;
        }

        public double getHeight() {
            return height;
        }

        public void setHeight(float height) {
            this.height = height;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }
    }

测试结果:

[
{"id":21,"name":"brj","height":7.2}
{"id":54,"name":"c","height":7.2}
{"id":5,"name":"r","height":17.2}
{"id":90,"name":"g","height":38.111}
{"id":5,"name":"r","height":65.9}
{"id":1,"name":"a","height":107.2}
{"id":2,"name":"b4ruy","height":151.1}
{"id":-1,"name":"sadgc","height":172.2}
{"id":98,"name":"a","height":197.2}
{"id":35,"name":"sdhr","height":475.0998}
{"id":12,"name":"fdb","height":1017.2}
]

这里特别说明一点,height为7.2,分别是name:c 和 name:brj,在初始化的时候name:brj 排在name:c的数据前面,可是堆排序的结果确是name:c 在 name:brj前。
这刚好说明了堆排序的特点:不稳定
稳定的意思就是排序前后相同数据的顺序的顺序是一致的。

附:各种排序算法总结

算法总结
在这里插入图片描述
图片名词解释:
n: 数据规模
k: “桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存

算法分类

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值