广告
返回顶部
首页 > 资讯 > 后端开发 > Python >JAVA十大排序算法之堆排序详解
  • 270
分享到

JAVA十大排序算法之堆排序详解

2024-04-02 19:04:59 270人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录堆排序知识补充二叉树满二叉树完全二叉树二叉堆代码实现时间复杂度算法稳定性思考总结堆排序 这里的堆并不是JVM中堆栈的堆,而是一种特殊的二叉树,通常也叫作二叉堆。它具有以下特点:

堆排序

这里的堆并不是JVM中堆栈的堆,而是一种特殊的二叉树,通常也叫作二叉堆。它具有以下特点:

  • 它是完全二叉树
  • 堆中某个结点的值总是不大于或不小于其父结点的值

知识补充

二叉树

树中节点的子节点不超过2的有序树

image-20210804135913978

满二叉树

二叉树中除了叶子节点,每个节点的子节点都为2,则此二叉树为满二叉树。

image-20210804140132004

完全二叉树

如果对满二叉树的结点进行编号,约定编号从根结点起,自上而下,自左而右。则深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称之为完全二叉树。

特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。需要注意的是,满二叉树肯定是完全二叉树,而完全二叉树不一定是满二叉树。

image-20210804144904950

二叉堆

二叉堆是一种特殊的堆,可以被看做一棵完全二叉树的数组对象,而根据其性质又可以分为下面两种:

  • 大根堆:每一个根节点都大于等于它的左右孩子节点,也叫最大堆
  • 小根堆:每一个根节点都小于等于它的左右孩子节点,也叫最小堆

如果把一个数组通过大根堆的方式来表示(数组元素的值是可变的),如下:

image-20210804180209118

由此可以推出:

  • 对于位置为 k 的节点,其子节点的位置分别为,左子节点 = 2k + 1,右子节点 = 2(k + 1)

如:对于 k = 1,其节点的对应数组为 5

左子节点的位置为 3,对应数组的值为 3

右子节点的位置为 4,对应数组的值为 2

  • 最后一个非叶子节点的位置为 (n/2) - 1,n为数组长度

如:数组长度为6,则 (6/2) - 1 = 2,即位置 2 为最后一个非叶子节点

给定一个随机数组[35,63,48,9,86,24,53,11],将该数组视为一个完全二叉树:

image-20210804190655494

从上图很明显的可以看出,这个二叉树不符合大根堆的定义,但是可以通过调整,使它变为最大堆。如果从最后一个非叶子节点开始,从下到上,从右往左调整,则:

image-20210804224254053

通过上面的调整,该二叉树为最大堆,这个时候开始排序,排序规则:

  • 将堆顶元素和尾元素交换交换
  • 后重新调整元素的位置,使之重新变成二叉堆

image-20210804234843626

代码实现


public class HeapSort {
    public static final int[] ARRAY = {35, 63, 48, 9, 86, 24, 53, 11};
    public static int[] sort(int[] array) {
        //数组的长度
        int length = array.length;
        if (length < 2) return array;
        //首先构建一个最大堆
        buildMaxHeap(array);
        //调整为最大堆之后,顶元素为最大元素并与微元素交换
        while (length > 0) {//当lenth <= 0时,说明已经到堆顶
            //交换
            swap(array, 0, length - 1);
            length--;//交换之后相当于把树中的最大值弹出去了,所以要--
            //交换之后从上往下调整使之成为最大堆
            adjustHeap(array, 0, length);
        }
        return array;
    }
    //对元素组构建为一个对应数组的最大堆
    private static void buildMaxHeap(int[] array) {
        //在之前的分析可知,最大堆的构建是从最后一个非叶子节点开始,从下往上,从右往左调整
        //最后一个非叶子节点的位置为:array.length/2 - 1
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            //调整使之成为最大堆
            adjustHeap(array, i, array.length);
        }
    }
    
    private static void adjustHeap(int[] array, int parent, int length) {
        //定义最大值的索引
        int maxIndex = parent;
        //parent为对应元素的位置(数组的索引)
        int left = 2 * parent + 1;//左子节点对应元素的位置
        int right = 2 * (parent + 1);//右子节点对应元素的位置
        //判断是否有子节点,再比较父节点和左右子节点的大小
        //因为parent最后一个非叶子节点,所以如果有左右子节点则节点的位置都小于数组的长度
        if (left < length && array[left] > array[maxIndex]) {//左子节点如果比父节点大
            maxIndex = left;
        }
        if (right < length && array[right] > array[maxIndex]) {//右子节点如果比父节点大
            maxIndex = right;
        }
        //maxIndex为父节点,若发生改变则说明不是最大节点,需要交换
        if (maxIndex != parent) {
            swap(array, maxIndex, parent);
            //交换之后递归再次调整比较
            adjustHeap(array, maxIndex, length);
        }
    }
    //交换
    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    public static void print(int[] array) {
        for (int i : array) {
            System.out.print(i + "  ");
        }
        System.out.println("");
    }
    public static void main(String[] args) {
        print(ARRAY);
        System.out.println("============================================");
        print(sort(ARRAY));
    }
}

时间复杂度

堆的时间复杂度是 O(nlogn)

参考:堆排序的时间复杂度分析

算法稳定性

堆的结构为,对于位置为 k 的节点,其子节点的位置分别为,左子节点 = 2k + 1,右子节点 = 2(k + 1),最大堆要求父节点大于等于其2个子节点,最小堆要求父节点小于等于其2个子节点。

在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(最大堆)或者最小(最大堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1,n/2-2,… 1 这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。

思考

对于快速排序来说,其平均复杂度为O(nlogn),堆排序也是O(nlogn),怎么选择?如下题:

LeetCode:数组中的第K个最大元素

此题的意思是对于一个无序数组,经过排序后的第 k 个最大的元素。

我们知道快速排序是需要对整个数组进行排序,这样才能取出第 k 个最大的元素。

如果使用堆排序,且是最大堆的方式,则第k次循环即可找出第 k 个最大的元素,并不需要吧整个数组排序。

所以对于怎么选择的问题,要看具体的场景,或者是两者都可。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注编程网的更多内容!

--结束END--

本文标题: JAVA十大排序算法之堆排序详解

本文链接: https://www.lsjlt.com/news/133307.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JAVA十大排序算法之堆排序详解
    目录堆排序知识补充二叉树满二叉树完全二叉树二叉堆代码实现时间复杂度算法稳定性思考总结堆排序 这里的堆并不是JVM中堆栈的堆,而是一种特殊的二叉树,通常也叫作二叉堆。它具有以下特点: ...
    99+
    2022-11-12
  • Java十大排序算法之堆排序刨析
    二叉堆是完全二叉树或者是近似完全二叉树。 二叉堆满足二个特性︰ 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。 2.每个结点的左子树和右子树都是一个二叉堆(都是最...
    99+
    2022-11-12
  • JAVA十大排序算法之桶排序详解
    目录桶排序代码实现时间复杂度算法稳定性总结桶排序 桶排序是计数排序的升级,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过函数的某种映射关系,将待排序数组...
    99+
    2022-11-12
  • JAVA十大排序算法之基数排序详解
    目录基数排序代码实现时间复杂度算法稳定性基数排序 vs 桶排序 vs 计数排序总结基数排序 常见的数据元素一般是由若干位组成的,比如字符串由若干字符组成,整数由若干位0~9数字组成。...
    99+
    2022-11-12
  • JAVA十大排序算法之冒泡排序详解
    目录冒泡排序代码实现代码实现时间复杂度算法稳定性总结冒泡排序 1.从数组头开始,比较相邻的元素。如果第一个比第二个大(小),就交换它们两个 2.对每一对相邻元素作同样的工作,从开始第...
    99+
    2022-11-12
  • JAVA十大排序算法之选择排序详解
    目录选择排序代码实现动图演示代码实现时间复杂度算法稳定性总结选择排序 1.找到数组中最大(或最小)的元素 2.将它和数组的第一个元素交换位置(如果第一个元素就是最大(小)元素那么它就...
    99+
    2022-11-12
  • JAVA十大排序算法之插入排序详解
    目录插入排序代码实现动图演示代码实现时间复杂度算法稳定性总结插入排序 当我们在玩扑克牌的时候,总是在牌堆里面抽取最顶部的一张然后按顺序在手中排列。 插入排序是指在待排序的元素中,假设...
    99+
    2022-11-12
  • JAVA十大排序算法之希尔排序详解
    目录希尔排序代码实现时间复杂度算法稳定性总结希尔排序 一种基于插入排序的快速的排序算法。简单插入排序对于大规模乱序数组很慢,因为元素只能一点一点地从数组的一端移动到另一端。例如,如果...
    99+
    2022-11-12
  • JAVA十大排序算法之归并排序详解
    目录归并排序怎么分怎么治代码实现时间复杂度算法稳定性总结归并排序 归并,指合并,合在一起。归并排序(Merge Sort)是建立在归并操作上的一种排序算法。其主要思想是分而治之。什么...
    99+
    2022-11-12
  • JAVA十大排序算法之快速排序详解
    目录快速排序问题思路荷兰国旗问题代码实现时间复杂度算法稳定性总结快速排序 快速排序是对冒泡排序的一种改进,也是采用分治法的一个典型的应用。JDK中Arrays的sort()方法,具体...
    99+
    2022-11-12
  • JAVA十大排序算法之计数排序详解
    目录计数排序问题代码实现时间复杂度算法稳定性总结计数排序 一种非比较排序。计数排序对一定范围内的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进...
    99+
    2022-11-12
  • 图解Java排序算法之堆排序
    目录预备知识堆排序堆堆排序基本思想及步骤再简单总结下堆排序的基本思路:总结预备知识 堆排序 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均...
    99+
    2022-11-12
  • Java 十大排序算法之希尔排序刨析
    目录希尔排序原理希尔排序的API设计希尔排序的代码实现希尔排序是插入排序的一种,又称"缩小增量排序”,是插入排序算法的一种更高效的改进版本。 希尔排序原理 1.选定一个增长量h,按照...
    99+
    2022-11-12
  • Java 十大排序算法之归并排序刨析
    目录归并排序原理归并排序API设计归并排序代码实现归并排序的时间复杂度分析归并排序原理 1.尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元...
    99+
    2022-11-12
  • Java 十大排序算法之插入排序刨析
    目录插入排序原理插入排序API设计插入排序代码实现插入排序的时间复杂度分析插入排序原理 ①把所有元素分成已排序和未排序两组 ②找到未排序组的第一个元素,向已经排序的组中进行插入 ③倒...
    99+
    2022-11-12
  • Java 十大排序算法之选择排序刨析
    目录选择排序原理选择排序API设计选择排序代码实现选择排序的时间复杂度选择排序原理 ①假设第一个索引处的元素为最小值,和其他值进行比较,如果当前的索引处的元素大于其他某个索引处的值,...
    99+
    2022-11-12
  • Java 十大排序算法之冒泡排序刨析
    目录冒泡排序原理冒泡排序API设计冒泡排序的代码实现冒泡排序的时间复杂度分析冒泡排序原理 ①比较相邻的元素,如果前一个元素比后一个元素大,则交换这两个元素的位置 ②对每一对相邻的元素...
    99+
    2022-11-12
  • Java十大排序算法之计数排序刨析
    计数排序是非比较的排序算法,用辅助数组对数组中出现的数字计数,元素转下标,下标转元素 计数排序优缺点 优点:快 缺点:数据范围很大,比较稀疏,会导致辅助空间很大,造成空间的浪费 使用...
    99+
    2022-11-12
  • Python排序算法之堆排序算法
    目录1. 树满二叉树的特性:什么是完全二叉树?完全二叉树的专业概念:2. 二叉堆2.1 二叉堆的抽象数据结构2.2 API 实现3. 堆排序4. 后记本文从树数据结构说到二叉堆数据结...
    99+
    2023-01-07
    python堆排序算法实现 堆排序算法以及python实现 python 堆排序算法
  • Java 归并排序算法、堆排序算法实例详解
    基本思想:  归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。归并排序示例:合并方法:设r[i…n]由两个有序子表r[i…m...
    99+
    2023-05-31
    java 归并排序 堆排序
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作