堆排序(java)

目录

基础堆排序

一、概念及其介绍

二、适用说明

三、过程图示

四、Java 实例代码

优化堆排序

Java 实例代码

基础堆排序

一、概念及其介绍

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。

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

二、适用说明

我们之前构造堆的过程是一个个数据调用 insert 方法使用 shift up 逐个插入到堆中,这个算法的时候时间复杂度是 O(nlogn),本小节介绍的一种构造堆排序的过程,称为 Heapify,算法时间复杂度为 O(n)

三、过程图示

完全二叉树有个重要性质,对于第一个非叶子节点的索引是 n/2 取整数得到的索引值,其中 n 是元素个数(前提是数组索引从 1 开始计算)。

堆排序(java)

索引 5 位置是第一个非叶子节点,我们从它开始逐一向前分别把每个元素作为根节点进行 shift down 操作满足最大堆的性质。

索引 5 位置进行 shift down 操作后,22 和 62 交换位置。

堆排序(java)

对索引 4 元素进行 shift down 操作

堆排序(java)

对索引 3 元素进行 shift down 操作

堆排序(java)

对索引 2 元素进行 shift down 操作

堆排序(java)

最后对根节点进行 shift down 操作,整个堆排序过程就完成了。

堆排序(java)

四、Java 实例代码

Heapify.java

/**
 * 用heapify进行堆排序
 */
public class Heapify {

    protected T[] data;
    protected int count;
    protected int capacity;

    // 构造函数, 通过一个给定数组创建一个最大堆
    // 该构造堆的过程, 时间复杂度为O(n)
    public Heapify(T arr[]){

        int n = arr.length;

        data = (T[])new Comparable[n+1];
        capacity = n;

        for( int i = 0 ; i < n ; i ++ )
            data[i+1] = arr[i];
        count = n;
        //从第一个不是叶子节点的元素开始
        for( int i = count/2 ; i >= 1 ; i -- )
            shiftDown(i);
    }
    // 返回堆中的元素个数
    public int size(){
        return count;
    }
    // 返回一个布尔值, 表示堆中是否为空
    public boolean isEmpty(){
        return count == 0;
    }
    // 像最大堆中插入一个新的元素 item
    public void insert(T item){
        assert count + 1  0;
        T ret = data[1];
        swap( 1 , count );
        count --;
        shiftDown(1);
        return ret;
    }
    // 获取最大堆中的堆顶元素
    public T getMax(){
        assert( count > 0 );
        return data[1];
    }

    // 交换堆中索引为i和j的两个元素
    private void swap(int i, int j){
        T t = data[i];
        data[i] = data[j];
        data[j] = t;
    }

    //********************
    //* 最大堆核心辅助函数
    //********************
    private void shiftUp(int k){

        while( k > 1 && data[k/2].compareTo(data[k]) < 0 ){
            swap(k, k/2);
            k /= 2;
        }
    }

    private void shiftDown(int k){

        while( 2*k  0 )
                j ++;
            // data[j] 是 data[2*k]和data[2*k+1]中的最大值

            if( data[k].compareTo(data[j]) >= 0 ) break;
            swap(k, j);
            k = j;
        }
    }

    // 测试 heapify
    public static void main(String[] args) {
        int N = 100;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
        Heapify heapify = new Heapify(arr);
        // 将heapify中的数据逐渐使用extractMax取出来
        // 取出来的顺序应该是按照从大到小的顺序取出来的
        for( int i = 0 ; i < N ; i ++ ){
            arr[i] = heapify.extractMax();
            System.out.print(arr[i] + " ");
        }

        // 确保arr数组是从大到小排列的
        for( int i = 1 ; i < N ; i ++ )
            assert arr[i-1] >= arr[i];
    }
}

SortTestHelper.java

public class SortTestHelper {
    // SortTestHelper不允许产生任何实例
    private SortTestHelper(){}

    // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]
    public static Integer[] generateRandomArray(int n, int rangeL, int rangeR) {

        assert rangeL

优化堆排序

上一节的堆排序,我们开辟了额外的空间进行构造堆和对堆进行排序。这一小节,我们进行优化,使用原地堆排序。

对于一个最大堆,首先将开始位置数据和数组末尾数值进行交换,那么数组末尾就是最大元素,然后再对W元素进行 shift down 操作,重新生成最大堆,然后将新生成的最大数和整个数组倒数第二位置进行交换,此时到处第二位置就是倒数第二大数据,这个过程以此类推。

整个过程可以用如下图表示:

堆排序(java)

Java 实例代码

HeapSort.java

/**
 * 原地堆排序
 */
public class HeapSort {

    public static void sort(Comparable[] arr) {

        int n = arr.length;

        // 注意,此时我们的堆是从0开始索引的
        // 从(最后一个元素的索引-1)/2开始
        // 最后一个元素的索引 = n-1
        for (int i = (n - 1 - 1) / 2; i >= 0; i--)
            shiftDown(arr, n, i);

        for (int i = n - 1; i > 0; i--) {
            swap(arr, 0, i);
            shiftDown(arr, i, 0);
        }
    }

    // 交换堆中索引为i和j的两个元素
    private static void swap(Object[] arr, int i, int j) {
        Object t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    // 原始的shiftDown过程
    private static void shiftDown(Comparable[] arr, int n, int k) {

        while (2 * k + 1 < n) {
            //左孩子节点
            int j = 2 * k + 1;
            //右孩子节点比左孩子节点大
            if (j + 1 < n && arr[j + 1].compareTo(arr[j]) > 0)
                j += 1;
            //比两孩子节点都大
            if (arr[k].compareTo(arr[j]) >= 0) break;
            //交换原节点和孩子节点的值
            swap(arr, k, j);
            k = j;
        }
    }

    // 测试 HeapSort
    public static void main(String[] args) {

        int N = 100;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
        sort(arr);
        // 将heapify中的数据逐渐使用extractMax取出来
        // 取出来的顺序应该是按照从大到小的顺序取出来的
        for (int i = 0; i < N; i++) {
            System.out.print(arr[i] + " ");
        }
        // 确保arr数组是从大到小排列的
        for (int i = 1; i < N; i++)
            assert arr[i - 1] >= arr[i];
    }
}

SortTestHelper.java

public class SortTestHelper {
    // SortTestHelper不允许产生任何实例
    private SortTestHelper(){}

    // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]
    public static Integer[] generateRandomArray(int n, int rangeL, int rangeR) {

        assert rangeL

Original: https://www.cnblogs.com/siwuxiebuff/p/16208541.html
Author: 思无邪buff
Title: 堆排序(java)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570263/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • SpringBoot自定义注解失效原因(2022-10-3)

    长话短说,我负责的是一个多模块项目,接手的时候没有注意 @ComponentScan 注解的扫描范围,所以打包的时候,没有扫到我新加包。所以,重点检查下 @ComponentSca…

    Java 2023年6月9日
    063
  • JAVA抓取百度热搜榜实时数据

    背景:[JAVA]前几天面试超碧,聊到其接触的项目,有抓取各类排行的实时数据,进行多国语言翻译,抓取目前比较火的语言是php、go,由于目前工作使用JAVA,因此也模拟实现了一下抓…

    Java 2023年6月9日
    0108
  • Java学习 (七)基础篇 变量

    变量 变量顾名思义,就是可以变化的量 Java是一种强类型语言,每个变量都必须声明其类型 *Java变量是程序中最基本的存储单位,其要素包括变量名、变量类型和作用域 type va…

    Java 2023年6月8日
    089
  • jvm垃圾回收的过程

    垃圾回收的过程分为两步: 1.判断对象是否死亡 (1)引用计数器法: ①每当有一个对象引用是,计数器加一,当计数器为0是对象死亡 ②缺点:无法解决循环引用的问题,假设A引用B,B引…

    Java 2023年6月6日
    092
  • java 位运算、移位运算、简单使用

    1,运算符和使用以及二进制的转化 public static void main(String[] args) { int a = 100; int b = 97; System….

    Java 2023年5月29日
    073
  • 小数点的几种精确方法

    小数点的几种精确方法 1.直接用格式化输出String.format() double b=123.4; System.out.println(String.format(&quo…

    Java 2023年6月5日
    072
  • Java开发笔记汇总

    Java语法与.Net对比Java规范与约定KotlinMaven笔记SpringBoot笔记2SpringCloud笔记MyBatis笔记发布Jar包到中央仓库 作者:NewSe…

    Java 2023年5月29日
    0126
  • 和朱晔一起复习Java并发(一):线程池

    和我之前的Spring系列文章一样,我们会以做一些Demo做实验的方式来复习一些知识点。本文我们先从Java并发中最最常用的线程池开始。 从一个线程池实验开始 首先我们写一个方法来…

    Java 2023年5月29日
    076
  • ApplicationContext refresh 过程及一些重要的 processor 解析

    注册 Bean 到容器,通过限定名获取 Bean 可以拦截 Bean 初始化前后的处理 可以在 Bean 属性注入后和即将销毁时做一些逻辑处理 解决了循环依赖 其实总结起来它实现的…

    Java 2023年6月5日
    078
  • 通过nginx 访问 centos 7 服务器上的.Net Core

    先安装依赖 yum -y install pcre-devel openssl openssl-devel yum -y install gcc gcc-c++ autoconf …

    Java 2023年5月30日
    096
  • Nginx配置,413 Request Entity Too Large错误解决

    今天有同事找我,说图片上传之后,不知道去哪里了。分析了一下问题,找到原因之后做了处理,这里简要记录一下。 问题原因: 1.首先后台log并无错误信息; 2.捡查了一下浏览器,发现n…

    Java 2023年5月30日
    081
  • Redis6通信协议升级至RESP3,一口气看完13种新数据类型

    原创:微信公众号 &#x7801;&#x519C;&#x53C2;&#x4E0A;,欢迎分享,转载请保留出处。 在前面的文章 Redis:我是如何与…

    Java 2023年6月5日
    098
  • npm常用命令及参数总结

    NPM几个常用命令和参数的意思: npm&#xA0;<span class="hljs-keyword"><span class=&q…

    Java 2023年6月5日
    071
  • 用 Vim 编辑 Markdown 时直接粘贴图片

    我习惯使用 Vim 编辑 Markdown 文件,一直存在一个痛点就是粘贴图片很不方便。 前后对比 我以前常用的操作流程: 复制图片/截图; 在保存图片对话框里一层层点选保存路径,…

    Java 2023年6月5日
    080
  • [学习标准库]ctype.h

    概述: ctype.h为我们提供了很多了与字符相关的判断或处理函数,方便地对字符做判断和转换大小写等处理。 下面以函数为单位进行学习。 isalnum 功能: 测试传入参数其对应的…

    Java 2023年6月5日
    093
  • 谈一谈分布式会话

    一、什么是会话 会话Session代表的是客户端与服务器的一次交互过程,这个过程可以是连续也可以是时断时续的。曾经的Servlet时代(jsp)),一旦用户与服务端交互,服务器to…

    Java 2023年6月5日
    0104
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球