(八)Java算法:堆排序(详细图解)

news2024/11/18 13:54:50

目录

    • 一、前言
      • 1.1、概念
      • 1.2、大根堆特点
    • 二、maven依赖
    • 三、流程解析
      • 3.1、初始建堆
      • 3.2、堆化第一步
      • 3.2、堆化第二步
      • 3.3、堆化第三步
      • 3.4、堆化第四步
      • 3.5、堆化第五步
      • 3.6、堆化第六步
    • 四、编码实现
      • 4.1、代码实现
      • 4.2、运行结果:
    • 扩展

一、前言

1.1、概念

  根据堆的结构可以分为大根堆和小根堆,它是一个完全二叉树,先来了解下什么是大根堆和小根堆吧。

  • 大根堆:每个结点的值都大于其左孩子和右孩子结点的值,即: a r r [ i ] arr[i] arr[i] > a r r [ 2 ∗ i + 1 ] arr[2*i+1] arr[2i+1] && a r r [ i ] arr[i] arr[i] > a r r [ 2 ∗ i + 2 ] arr[2*i+2] arr[2i+2]
  • 小根堆:每个结点的值都小于其左孩子和右孩子结点的值,即: a r r [ i ] arr[i] arr[i] < a r r [ 2 ∗ i + 1 ] arr[2*i+1] arr[2i+1] && a r r [ i ] arr[i] arr[i] < a r r [ 2 ∗ i + 2 ] arr[2*i+2] arr[2i+2]

1.2、大根堆特点

  本文主要以大根堆为例,它的特点如下:

  • 数组索引为 0 0 0 的位置存放堆顶的元素(大根堆根节点就是最大值了)
  • 数组索引为 i i i 的元素的左右叶子节点的索引分别是 2 ∗ i + 1 2 * i + 1 2i+1 2 ∗ i + 2 2 * i + 2 2i+2
  • 数组索引为 i i i 的元素的父节点的下标索引为 i − 1 2 \frac{i-1}{2} 2i1

二、maven依赖

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.6.0</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.14</version>
    </dependency>
</dependencies>

三、流程解析

3.1、初始建堆

  先把我们数组的元素转化为大根堆。

在这里插入图片描述

  • 数组索引为 i i i 的元素的左右叶子节点的索引分别是 2 ∗ i + 1 2 * i + 1 2i+1 2 ∗ i + 2 2 * i + 2 2i+2,我们可以把数组转化为图1的树,这个是初始化的数据
  • 图1中最后一个父节点是 10 10 10,它不满足大根堆的条件,因为它的子节点的值 19 19 19比它大,所以我们交换他们的值得到图2,当前的节点就满足大根堆了,然后继续下一个节点
  • 往前继续找到父节点 8 8 8(也就是从最右的父节点一直往前遍历),它不满足大根堆的条件,因为它的子节点的值 23 23 23比它大,所以交换 8 8 8 23 23 23 得到图3,然后继续下一个节点
  • 下一父节点是 28 28 28,它的值比两个子节点的值都大,已经满足大根堆的条件了,建堆完成

3.2、堆化第一步

  我们这一步是接着上面建堆的结果的,也就是以图3为基础变化的。

在这里插入图片描述

  • 通过上一步建堆时我们已经得到了大根堆了,也就是图3,我们把大根堆的值和最右未排序的子节点进行交换,也就是 28 28 28 9 9 9 得到图4,则 28 28 28 就是排序完成的数据
  • 图4中知道,排除排序好的节点( 28 28 28)来看, 19 19 19 23 23 23 满足大根堆,不用变化,但是根节点9不满足,因为它比它最大子节点 23 23 23小,所以交换 9 9 9 23 23 23 得到图5
  • 交换后的节点 9 9 9还是不满足大根堆条件,因为它比最大子节点 21 21 21小,所以交换 9 9 9 21 21 21 得到图6 9 9 9 已经没有子节点了,建堆完成

3.2、堆化第二步

  我们这一步是接着上面建堆的结果的,也就是以图6为基础变化的。

在这里插入图片描述

  • 上一步建堆时我们已经得到了大根堆了,我们把大根堆的值和最右未排序的子节点进行交换,也就是 23 23 23 10 10 10 得到图7,则 23 23 23 就是排序完成的数据
  • 图7中知道,排除排序好的节点( 23 23 23 28 28 28)来看, 19 19 19 21 21 21 满足大根堆,不用变化,但是根节点 10 10 10不满足,因为它比它最大子节点 21 21 21小,所以交换 10 10 10 21 21 21 得到图8
  • 图8中,交换后的节点 10 10 10已经满足大根堆的条件了,建堆完成

3.3、堆化第三步

  我们这一步是接着上面建堆的结果的,也就是以图8为基础变化的。

在这里插入图片描述

  • 上一步建堆时我们已经得到了大根堆了,我们把大根堆的值和最右未排序的子节点进行交换,也就是 21 21 21 9 9 9得到图9,则 21 21 21 就是排序完成的数据
  • 图9中知道,排除排序好的的节点( 21 21 21 23 23 23 28 28 28)来看, 19 19 19 10 10 10 满足大根堆,不用变化,但是根节点 9 9 9不满足,因为它比它最大子节点 19 19 19小,所以交换 9 9 9 19 19 19 得到图10
  • 图10中,交换后的节点 9 9 9(除去排序完成的没有子节点了)已经满足大根堆的条件了,建堆完成

3.4、堆化第四步

  我们这一步是接着上面建堆的结果的,也就是以图10为基础变化的。

在这里插入图片描述

  • 上一步建堆时我们已经得到了大根堆了,我们把大根堆的值和最右未排序的子节点进行交换,也就是 19 19 19 8 8 8得到图11,则 19 19 19 就是排序完成的数据
  • 图11中知道,排除排序好的的节点( 19 19 19 21 21 21 23 23 23 28 28 28)来看, 9 9 9 10 10 10 满足大根堆,不用变化,但是根节点 8 8 8不满足,因为它比它最大子节点 10 10 10小,所以交换 8 8 8 10 10 10 得到图12
  • 图12中,交换后的节点 8 8 8(除去排序完成的没有子节点了)已经满足大根堆的条件了,建堆完成

3.5、堆化第五步

  我们这一步是接着上面建堆的结果的,也就是以图12为基础变化的。

在这里插入图片描述

  • 上一步建堆时我们已经得到了大根堆了,我们把大根堆的值和最右未排序的子节点进行交换,也就是 10 10 10 9 9 9 得到图13,则 10 10 10 就是排序完成的数据
  • 图13中知道,排除排序好的的节点( 10 10 10 19 19 19 21 21 21 23 23 23 28 28 28)来看,交换后的节点 8 8 8(除去排序完成的没有子节点了)已经满足大根堆的条件了
  • 最后结果就是图14,节点都已经满足大根堆的条件了,建堆完成

3.6、堆化第六步

  我们这一步是接着上面建堆的结果的,也就是以图14为基础变化的。

在这里插入图片描述

  • 上一步建堆时我们已经得到了大根堆了,我们把大根堆的值和最右未排序的子节点进行交换,也就是 9 9 9 8 8 8 得到图15,则 9 9 9 就是排序完成的数据
  • 图15中知道,排除排序好的的节点( 9 9 9 10 10 10 19 19 19 21 21 21 23 23 23 28 28 28)来看,只有一个根节点节点 8 8 8了,也就是图16,不用比较了,最后的结果就是全部排序完的图17,到此我们的排序就完成了

四、编码实现

4.1、代码实现

public static void heapSort(int[] arr) {
    // 获取数组长度
    int length = arr.length;
    // 数组索引为 i 的元素的父节点的下标索引为:i-1/2 得到
    // 因为下标从0开始,最后一个子节点(length-1)减去1再除以2得到父节点(length-1-1)/2=length / 2 - 1
    // 我们从最后一个父节点开始遍历直到根节点(父节点会和子节点进行比较的)
    for (int i = length / 2 - 1; i >= 0; i--) {
        // 把数组转化为堆,我们称之为建堆
        buildHeap(arr, i, length);
    }
    log.info("数组建堆后的结果:{}", arr);
    // 排序,因为之前已经完成了建堆,意味着,根节点就是我们需要的值
    for (int i = length - 1; i >= 0; i--) {
        // 将当前根节点与未排序的最大子节点进行交换
        swap(arr, 0, i);
        // 剩下的元素继续建堆,要理解i--,刚刚交换的根节点的值就是已排序的不会参与遍历了
        buildHeap(arr, 0, i);
    }
}

private static void buildHeap(int[] arr, int i, int length) {
    // 大顶堆的节点调整
    while (true) {
        // 定义最大节点的位置
        int maxPos = i;
        // 检查在未排序列表中,当前节点的值是不是小于它的左子节点(2i+1)
        if (i * 2 + 1 < length && arr[i] < arr[i * 2 + 1]) {
            maxPos = i * 2 + 1;
        }
        // 检查在未排序列表中,同时当前的最大节点和i节点的右子节点(2i+2)也比较找出最大值的节点
        // 也就是找出父节点,左节点,右节点三者中的最大节点值
        if (i * 2 + 2 < length && arr[maxPos] < arr[i * 2 + 2]) {
            maxPos = i * 2 + 2;
        }
        // maxPos没变说明已经找不到比当前节点大的了
        if (maxPos == i) {
            break;
        }
        // 交换两个节点(当前节点和最大值的节点进行交换)
        // 这里就是父节点和子节点中那个较大的节点进行交换
        swap(arr, i, maxPos);
        // 继续往下处理这个过程()也就是继续处理最大节点,看它是否满足大根堆的条件(比它的子节点都大)
        i = maxPos;
    }
    log.info("大顶堆的节点调整后结果:{}", arr);
}

private static void swap(int[] arr, int i, int j) {
    log.info("交换数据:{}和{}交换", arr[i], arr[j]);
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

public static void main(String[] args) {
    int[] arr = new int[]{28, 8, 10, 23, 21, 19, 9};
    log.info("要排序的初始化数据:{}", arr);
    //从小到大排序
    heapSort(arr);
}

  一定要结合本地第一节的概念和特点去看代码,才会事半功倍。

4.2、运行结果:

要排序的初始化数据:[28, 8, 10, 23, 21, 19, 9]
交换数据:10和19交换
大顶堆的节点调整后结果:[28, 8, 19, 23, 21, 10, 9]
交换数据:8和23交换
大顶堆的节点调整后结果:[28, 23, 19, 8, 21, 10, 9]
大顶堆的节点调整后结果:[28, 23, 19, 8, 21, 10, 9]
数组建堆后的结果:[28, 23, 19, 8, 21, 10, 9]
交换数据:28和9交换
交换数据:9和23交换
交换数据:9和21交换
大顶堆的节点调整后结果:[23, 21, 19, 8, 9, 10, 28]
交换数据:23和10交换
交换数据:10和21交换
大顶堆的节点调整后结果:[21, 10, 19, 8, 9, 23, 28]
交换数据:21和9交换
交换数据:9和19交换
大顶堆的节点调整后结果:[19, 10, 9, 8, 21, 23, 28]
交换数据:19和8交换
交换数据:8和10交换
大顶堆的节点调整后结果:[10, 8, 9, 19, 21, 23, 28]
交换数据:10和9交换
大顶堆的节点调整后结果:[9, 8, 10, 19, 21, 23, 28]
交换数据:9和8交换
大顶堆的节点调整后结果:[8, 9, 10, 19, 21, 23, 28]
交换数据:8和8交换
大顶堆的节点调整后结果:[8, 9, 10, 19, 21, 23, 28]

扩展

  实际在我们的数据结构中,有一种队列采用的也是堆排序,那就是PriorityQueue(优先队列),大家可以作一个了解。

package com.alian.algorithm.sort;

import lombok.extern.slf4j.Slf4j;

import java.util.Iterator;
import java.util.PriorityQueue;

@Slf4j
public class HeapSort {

    public static void heapSort(int[] arr) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        for (int value : arr) {
            queue.offer(value);
        }
        Iterator<Integer> iterator = queue.iterator();
        while (iterator.hasNext()) {
            log.info("排序后的数据:{}", queue.poll());
        }
    }

    public static void main(String[] args) {
        int[] arr = new int[]{28, 8, 10, 23, 21, 19, 9};
        log.info("要排序的初始化数据:{}", arr);
        //从小到大排序
        heapSort(arr);
    }
}

运行结果:

要排序的初始化数据:[28, 8, 10, 23, 21, 19, 9]
排序后的数据:8
排序后的数据:9
排序后的数据:10
排序后的数据:19
排序后的数据:21
排序后的数据:23
排序后的数据:28

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

彩印图文版《Elasticsearch实战》文档,阿里内部共享,堪称精品

学习是一种基础性的能力。然而&#xff0c;“吾生也有涯&#xff0c;而知也无涯。”&#xff0c;如果学习不注意方法&#xff0c;则会“以有涯随无涯&#xff0c;殆矣”。 学习就像吃饭睡觉一样&#xff0c;是人的一种本能&#xff0c;人人都有学习的能力。我们在刚出生的时候…

Lesson1强化学习(RL)初印象 学习笔记

一、强化学习引入 ​ 人的智能可以遗传获得也可以通过后天学习&#xff1b;学习有两种&#xff0c;模仿前人的经验是一种学习&#xff1b;如果没有前人的经验可以学习&#xff0c;就需要和环境进行交互&#xff0c;得到反馈来学习。 #mermaid-svg-XUxguPj6VHcJMK3W {font-famil…

the account is locked

感谢阅读问题描述解决方案1.WinR打开命令行输入&#xff1a;sqlplus &#xff0c;或者使用sqlplus / as sysdba;无需输入密码。2.假设我们要解锁的账户是scott3.修改密码&#xff0c;从而避免再次被锁4.重启服务或者客户端&#xff08;看你是桌面版还是服务器版&#xff09;&am…

警惕,3D建模为什么选3dsMAX不选MAYA

如今现在的游戏建模都是次世代建模&#xff0c;3DMAX确实是主流软件之一&#xff0c;但是为什么说MAYA更好呢❓ 首先&#xff0c;两款软件都很适合用现代的建模&#xff0c;但是难度上有一定的差异❌ • 软件区别 &#x1f340;3DSMAX&#xff1a; 它是目前使用最广泛的3d软…

十、Mysql的DQL语句

Mysql的DQL语句十、Mysql的DQL语句select的查询一、查看系统参数二、select常用函数三、select的单表查询1、from子句2、where子句2.1 where配合等值查询2.2where配合比较操作符(> < > < <>)2.3where配合逻辑运算符(and or )2.4where配合模糊查询2.5where配合…

流量控制可靠传输机制停止-等待协议

注&#xff1a;最后有面试挑战&#xff0c;看看自己掌握了吗 文章目录链路层流量控制和传输层的流量控制区别停止-等待协议为什么要有停止等待协议无差错情况滑动窗口协议后退N帧协议GBN选择重传协议SR可靠传输流量控制&#x1f343;博主昵称&#xff1a;一拳必胜客 &#x1f3…

供应链全流程计划与排产解决方案核心功能概要

通过数字智能化运营实现将本增效至为重要。 许多企业的业务现状是销售、生产计划与市场不匹配&#xff0c;企业的运营效率低且成本高&#xff1a; 销售计划计划需要大量的人员进行沟通&#xff0c;销售预测的分析维度少、粒度粗&#xff0c;不仅效率低&#xff0c;且预测只是一…

【mycat】常用分片规则

一、 常用分片规则 1、取模 ​ 此规则为对分片字段求摸运算。也是水平分表最常用规则 2、分片枚举 ​ 通过在配置文件中配置可能的枚举 id&#xff0c;自己配置分片&#xff0c;本规则适用于特定的场景&#xff0c;比如有些业务需要按照省份或区县来做保存&#xff0c;而全…

Delphi Enterprise具有强大视觉设计功能

Delphi Enterprise具有强大视觉设计功能 Delphi可帮助您使用Object Pascal为Windows、Mac、Mobile、IoT和Linux构建和更新数据丰富、超连接、可视化的应用程序。Delphi Enterprise适用于构建客户端/服务器或多层应用程序、REST服务等的开发团队。 Delphi功能 单一代码库-用更少…

小程序在线客服完整实现

1、先注册公众号》小程序(需实名认证) 2、开发》测试》上传》审核发布》发布》使用 3、公众号(订阅号或服务号)与小程序(此为在线客服)通过同一主体绑定可以打通调用 建议 内测完成后再上传。如果多次上传demo会处罚的。 添加客服人员列表(授权)可以电脑、手机端均…

(免费分享)springboot论坛bbs系统

源码获取&#xff1a;关注文末gongzhonghao&#xff0c;输入010领取下载链接 开发工具&#xff1a;IDEA 数据库mysql5.7 技术&#xff1a;springbootjpashiroredislayui 前台截图&#xff1a; 后台截图&#xff1a; package com.qxczh.admin.service.impl;impor…

云南美食介绍 简单静态HTML网页作品 美食餐饮网站设计与实现 学生美食网站模板

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

A puma at large

New words and expressions puma n. 美洲狮 spot v. 看出,发现 evidence n. 证据 accumulate v. 积累,积聚 oblige v. 使……感到必须 hunt n. 追猎;寻找 blackberry n. 黑莓 Pumas are large, cat-like animals which are found in America. When reports came i…

点击化学PEG试剂巯基吡啶-PEG-叠氮,OPSS-PEG-azide,OPSS-PEG-N3

点击化学PEG试剂OPSS-PEG-azide&#xff08;OPSS-PEG-N3 &#xff09;化学试剂其中文名为巯基吡啶-聚乙二醇-叠氮&#xff0c;它所属分类为Azide PEG Orthopyridyl disulfide (OPSS) PEG。 其分子量均可定制&#xff0c;有&#xff1a;2K 巯基吡啶-PEG-叠氮、5k OPSS-PEG-N3、…

TCP/IP协议、UDP协议及网络基础概念

网络程序设计编写的是与其他计算机进行通信的程序。Java已经将网络程序所需要的东西封装成不同的类&#xff0c;用户只要创建这些类的对象&#xff0c;使用相应的方法&#xff0c;即使不具备有关的网络知识&#xff0c;也可以编写出高质量的网络通信程序。 1.局域网与因特网 …

如何快速实现根因分析/业务大盘

Ideas Worth Spreading 写在前面的话 特别感谢 感谢在最早开发鱼骨图带领我前进的技术负责人-佳哥&#xff08;总监&#xff09;。他一丝不苟的技术态度、严密的逻辑和高要求&#xff08;这里大家都懂的&#xff09;&#xff0c;让我成长。这里是他在语雀的博客&#xff0c;其…

(new online judge)1322蓝桥杯2017初赛 包子凑数

P1322 - [蓝桥杯2017初赛]包子凑数 - New Online Judge 题目分两步&#xff1a;&#xff08;1&#xff09;判断结果是否为INF&#xff1b;&#xff08;2&#xff09;如果不是INF&#xff0c;统计数量。考点是“数论gcd简单DP”。 1. 什么时候答案不是INF 什么时候答案不是INF…

ADSP21489之CCES开发笔记(六)

一、仿真调试CCES代码 1、导入21489的Demo代码 2、修改设计SS4SH存储代码&#xff0c;如红框部分 3、搭配USBi调试&#xff0c;修改如下代码部分 oSSnConfig.bSkipInitialDownload 0; //usbi调试时&#xff0c;设置为0&#xff0c;默认为1 oCommConfig.nSelectSPI SELECT_SP…

英文Essay写作中存在哪些门道?

刚进入英国学校学习的小伙伴&#xff0c;每次放假都要赶Essay。那么什么是Essay呢&#xff1f;Essay怎么写呢&#xff1f;今天我们就来讲讲Essay中的门道。 A little partner who has just entered a British school must catch up with Essay every holiday.So what is Essay?…

入门:树莓派装系统、亮机,无需外接显示器键盘鼠标(保姆级教程)

借鉴了&#xff1a;https://blog.csdn.net/qq_24211837/article/details/120255311 1.官网下载树莓派系统 https://www.raspberrypi.org/software/operating-systems/ 安装后页面还是比较柔和的 2.格式化SD卡 首次入门&#xff0c;还是先学习前辈的方法进行操作 下载格式…