排序算法学习11_堆排序(Java)

堆排序


一、排序介绍

堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点

  • 子结点小于父结点就是大顶堆
  • 反之就是小顶堆

堆排序(英语:Heapsort)是指利用这种数据结构所设计的一种排序算法。且是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序

二、堆排序实现

堆数据结构实现方式,不了解的可以看看往前博客,这边主要来讲排序的实现方式(其实并没有多出什么内容)

给出一个如下数组:Integer[] a = {0,-3,-7,-1,9,7,5,8,11,-5,-8};

  • 思路
    • 对给出的数组序列,重新排序,使其该数组实现大顶堆性质
    • 根据堆性质取出最大值存放到数组最后,当然该数组的最后,下一次就不用再去处理了

代码实现:

  • 转换成堆思路
    • 从序列中间位置开始往前操作,每次取一个值与他的左右子树进行比较,大的值上来,最终该数组序列将为一个大顶堆
  • 堆排序思路
    • 首先取顶部结点与最后一个结点交换位置,此时最后一个结点位置肯定是最大的
      • 排好序的位置就不必要在处理了
      • 然后让此时的顶部节点进行下沉到正确位置,大的就浮上来
    • 继续执行以上逻辑,直到剩两个元素时,就可以结束排序了
package datastructure.heap;

import jdk.nashorn.internal.ir.CallNode;

public class HeapSort {

    public static void sort (Comparable[] array) {
        Comparable[] heap = conversionHeap(array);
        heapSort(heap);
    }
    // 转换成堆
    public static Comparable[] conversionHeap (Comparable[] heap) {
        int index = heap.length / 2 - 1;
        for (int i = index; i >= 0; i--) {
            int k = i; // 作为父结点
            for (int j = k; j <= heap.length / 2 - 1;) {
                if (j * 2 + 2 < heap.length) {
                    // 比较左右子结点
                    if (less(heap,2*j+1,2*j+2)) {
                        j = 2 * j + 2;
                    } else j = 2 * j + 1;
                }else j = 2 * j + 1;

                if (less(heap,k,j))
                    swap(heap,k,j);
                k = j;
            }
        }
        return heap;
    }
    // 对堆进行排序
    public static void heapSort (Comparable[] heap) {
        int endItem = heap.length - 1; // 排序所有元素
        for (int topItem = 0; endItem >= topItem;) {
            // 到只剩两个元素时就不可以不讲道理直接换,
            // 否则可能出现: [31, 10, 34, 36, 45, 45, 60, 60, 69, 76]
            if (endItem == 1) {
                if (!less(heap,topItem,endItem)) {
                    swap(heap,topItem,endItem);
                }
                break;
            }
            swap(heap, topItem, endItem);
            endItem--; // 排好序的位置,就不必再排序
            int k = topItem; // 对原本最后的结点进行下沉
            // 下沉
            for (int j = k; j < endItem / 2;) {
                if (heap[2 * j + 2] != null) {
                    if (less(heap, 2 * j + 1, 2 * j + 2)) {
                        j = 2 * j + 2;
                    } else j = 2 * j + 1;
                } else j = 2 * j + 1;

                if (less(heap,k,j))
                    swap(heap,k,j);
                k = j;
            }
        }
    }

    // 交换两个位置上的数据
    private static void swap (Comparable[] heap, int i, int j) {
        Comparable t = heap[i];
        heap[i] = heap[j];
        heap[j] = t;
    }
    // 比较堆中两个索引位置上的数据
    private static boolean less (Comparable[] heap, int i, int j) {
        return heap[i].compareTo(heap[j]) < 0;
    }
}
  • 上面两个方法对于下沉算法都进行了具体实现,大伙可以尝试封装到一个方法中!
  • 具体感觉还有点瑕疵,但是说不上来,有问题的话欢迎讨论

三、对以上堆排序进行测试

public class TestHeapSort {
    public static void main(String[] args) {
        Integer[] a = {0,-3,-7,-1,9,7,5,8,11,-5,-8};
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
        HeapSort.sort(a);
        System.out.println(Arrays.toString(a));
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
    }
}

输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YSP1lgnj-1615727744868)(C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1615724919262.png)]

普通家用笔记本测试80万条随机数据:

public class TestHeapSort {
    public static void main(String[] args) {
        Integer[] a = new Integer[800000];
        for(int i = 0;i < a.length;i++){
            a[i] = (int)(Math.random()*800000);
        }
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
        HeapSort.sort(a);
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
    }
}

输出:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值