• 堆排序算法Java实现


    摘要 介绍堆排序的基本概念及其实现。

    前言

      排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。这里讲的排序是内排序中的堆排序算法,它属于选择排序的一种。

      堆排序和插入排序一样,是一种就地排序算法(不需要额外的存储空间)。堆是一种数据结构,它可以被视为一种完全二叉树。最小堆又叫小顶堆,满足小顶堆的条件是每个孩子节点的值都大于父节点。大顶堆则相反。

    源码

    import java.util.Arrays;
    
    public class HeapSort {
        //使用数组存储堆中的数据
        private int[] data;
    
        public static void main(String[] args) {
            int[] a = {1, 50, 38, 78, 33, 12, 65, 97, 76, 13, 27, 32, 50, 63, 101};
    
            int lastIndex = a.length - 1;
            //循环建堆
            for (int i = 0; i < lastIndex; i++) {
                //建堆
                buildMaxHeap(a, lastIndex - i);
                //交换堆顶和最后一个元素
                swap(a, 0, lastIndex - i);
                System.out.println("第" + i + "次遍历,执行结果:" + Arrays.toString(a));
            }
        }
    
        /**
         * 对data数组从0到 lastIndex 建大顶堆
         */
        public static void buildMaxHeap(int[] data, int lastIndex) {
            //从lastIndex节点(最后一个节点)的父节点开始
            for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
                //k保存正在判断的节点
                int k = i;
                //如果当前k节点的子节点存在
                while (k * 2 + 1 <= lastIndex) {
                    //k节点的左子节点的下标
                    int biggerIndex = left(k);
                    //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
                    if (biggerIndex < lastIndex) {
                        //若右子节点的值较大
                        if (data[biggerIndex] < data[biggerIndex + 1]) {
                            //biggerIndex总是记录较大子节点的下标
                            biggerIndex++;
                        }
                    }
                    //如果k节点的值小于其较大的子节点的值
                    if (data[k] < data[biggerIndex]) {
                        //交换它们
                        swap(data, k, biggerIndex);
                        //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
                        k = biggerIndex;
                    } else {
                        break;
                    }
                }
            }
        }
    
        /**
         * 根据父节点下标获取左孩子节点下标
         *
         * @param index 下标
         * @return 2 * index + 1
         */
        private static int left(int index) {
            // 左移1位相当于乘2
            return (index + 1) << 1 - 1;
        }
    
        //交换
        private static void swap(int[] data, int i, int j) {
            int tmp = data[i];
            data[i] = data[j];
            data[j] = tmp;
        }
    }
    

    基于Top k的最小堆算法

    /**
     * 最小堆
     *
     */
    public class MinHeap {
        //使用数组存储堆中的数据
        private int[] data;
    
        public MinHeap(int[] data) {
            this.data = data;
            bulidHeap();
        }
    
        /**
         * 建立最小堆
         */
        private void bulidHeap() {
            for (int i = (data.length) / 2 - 1; i >= 0; i--) {//下标小于等于i的节点拥有子节点
                change(i);
            }
        }
    
        /**
         * 根据父节点判断是否
         * 与左右孩子交换
         *
         * @param i
         */
        private void change(int i) {
            int temp = 0;
            int left = left(i);
            int right = right(i);
    //存在右节点则存在左节点
            if (right < data.length) {
    //拿到左右孩子中小的下标
                temp = min(left, right);
                if (data[i] > data[temp]) {
                    swap(i, temp);
    //如果和子节点发生交换,则要对子节点的左右孩子进行调整
                    change(temp);
                }
            } else if (right < data.length) {
    //不存在右节点但存在左节点,则左节点无孩子节点
                if (data[i] > data[left])
                    swap(i, left);
    //孩子节点大于父节点,直接交换位置
            }
        }
    
        /**
         * 获取两个节点中较小的节点的下标
         *
         * @param i
         * @param j
         * @return
         */
        private int min(int i, int j) {
            if (data[i] >= data[j])
                return j;
            return i;
        }
    
        /**
         * 根据父节点下标获取
         * 左孩子节点下表
         *
         * @param i
         * @return
         */
        private int left(int i) {
            return ((i + 1) << 1) - 1;
        }
    
        /**
         * 根据父节点下表获取
         * 右孩子节点下标
         *
         * @param i
         * @return
         */
        private int right(int i) {
            return (i + 1) << 1;//左移1位相当于乘2
        }
    
        /**
         * 根据下标交换数组中的位置
         *
         * @param i
         * @param j
         */
        private void swap(int i, int j) {
            int temp = data[i];
            data[i] = data[j];
            data[j] = temp;
        }
    
        /**
         * 重置堆顶
         *
         * @param root
         */
        public void newRoot(int root) {
            data[0] = root;
            change(0);
        }
    
        /**
         * 获取堆顶
         *
         * @return
         */
        public int getRoot() {
            return data[0];
        }
    
        /**
         * 获取堆数据
         */
        public int[] getData() {
            return data;
        }
    
    }
    
    

    实战

      小顶堆和大顶堆对于解决TOP-K问题拥有较高的效率,在N个数中找到K(K<N)个最大或最小的数可以分别使用小顶堆算法和大顶堆算法。下面我用上面的小顶堆算法找出一个数组中最大的7个数。

    public class TopK {
    
        public static void main(String[] args) {
            int[] num = {1, 3, 6, 7, 332, 355, 11, 325, 63, 25, 75, 32, 393, 759};
            int[] data = {1, 3, 6, 7, 332, 355, 11};//取前7个数据建立最小堆
            MinHeap heap = new MinHeap(data);
            for (int i = 7; i < num.length; i++) {
                //循环与堆顶比较,大于于堆顶则重置堆顶
                if (num[i] > heap.getRoot()) {
                    heap.newRoot(num[i]);
                }
            }
            //循环输出前7个最大的数
            for (int n : heap.getData()) {
                System.out.println(n);
            }
        }
    }
    

    测试结果如下所示:

    63
    325
    75
    393
    332
    355
    759

    小结

      对于Wiener以上的话题,大家又有什么自己的独特见解呢?欢迎在下方评论区留言!

    Reference


      读后有收获,小礼物走一走,请作者喝咖啡。

    赞赏支持

  • 相关阅读:
    编程风格与注意事项(一)
    BB-Black 初体验之远程控制篇
    我的BB-Black之旅……(一)
    等精度频率计设计中的猫腻
    Linux查看物理CPU个数、核数、逻辑CPU个数、Cpu型号
    基于TransportClient的elasticsearch(es)消费kafka数据---Java程序设计
    elasticsearch概述
    HBase 性能优化笔记
    hive1.2.1搭建遇到的问题
    Hbase的表设计
  • 原文地址:https://www.cnblogs.com/east7/p/15202738.html
Copyright © 2020-2023  润新知