Java 堆排序原理 图文详解 代码逻辑

文章目录

  • 1. 时间复杂度 & 空间复杂度
  • 2. 大顶堆、小顶堆
  • 3. 具体步骤 & 原理
    • 1. 判断是否满足堆的性质
    • 2. 维护堆的性质
      • 3. 交换位置
  • 4. 代码实现

1. 时间复杂度 & 空间复杂度

  • 时间复杂度: O(nlogn)
    建堆时间复杂度: O(n)
    排序时间复杂度: O(nlogn)
  • 空间复杂度: O(1)

2. 大顶堆、小顶堆

现有堆排序 分为大顶堆 和 小顶堆, 本文以大顶堆为例.

  • 大顶堆: 所有节点的父节点均大于左右孩子节点
    9 大于 7 、8
    9的左孩子7, 大于其左右孩子 5 6
    9的右孩子8, 大于其左右孩子 3 4
    7的左孩子 5, 大于其左右孩子 1 2
    大顶堆
    前序遍历结果为: 9 7 8 5 6 3 4 1 2
  • 小顶堆: 所有节点的父节点均小于左右孩子节点
    1 的左右孩子 2 3, 均大于1
    1 的左孩子 2 , 其左右孩子 4 5 均大于2
    1 的右孩子 3 , 其左右孩子 6 7 均大于3
    2 的左孩子 4 , 其左右孩子 8 9 均大于4
    小顶堆
    前序遍历结果: 1 2 3 4 5 6 7 8 9

3. 具体步骤 & 原理

当我们需要对一个无序数组进行排序时, 使用堆排序往往是一个不错的选择. 时间复杂度和空间复杂度相比冒泡、快排等均有不同程度的提升.
加入我们想要对以下数据排序, 其具体步骤及原理如下
原始二叉树
前序遍历结果: 3 5 4 7 1 2 9 6 8

1. 判断是否满足堆的性质

当我们拥有一个无序数组后, 每次去判断是否满足 大顶堆 或 小顶堆 的性质, 这里选择用大顶堆.
因为 大顶堆 需要满足条件: 父节点比左右孩子节点大, 我们从根节点开始遍历, 确认是否满足.
从根节点遍历
从图中可以看到 3 5 4不满足 根节点大于左右孩子节点. 这怎么办呢?

2. 维护堆的性质

此时就需要我们去做交换, 从左右孩子中找到最大的那个节点, 并与根节点做交换.

  1. 我们发现, 5 在这三个数中最大, 就让他与3交换, 得到第一步结果:
    第一步交换结果
  2. 根节点交换后, 我们继续遍历左右孩子节点, 判断是否满足大顶堆性质
    左节点遍历
    发现 3 的左右孩子节点 7 1 并不满足大顶堆性质, 继续交换得到结果 7 3 1
    在这里插入图片描述
  3. 交换完后我们发现根节点与左右孩子 变为了 5 7 4, 不再满足大顶堆性质了
    在这里插入图片描述
    继续交换 5 和 7, 得到7 5 4
    在这里插入图片描述
  4. 此时继续遍历 7的左子结点 5 的 左右孩子 5 3 1, 发现满足条件, 继续遍历5的左子结点 3
    在这里插入图片描述
  5. 3 的左右孩子结点 6 8 不满足大顶堆性质, 继续交换 3 和 8
    在这里插入图片描述
  6. 接着继续遍历, 直到所有的节点均满足 大顶堆 性质为止
    5 8 1不再满足大顶堆性质, 继续交换8 和 5
    在这里插入图片描述
    遍历7 8 4, 不满足条件, 交换 7 和 8
    在这里插入图片描述
    此时7 5 1 满足条件, 继续遍历7的左子结点5, 看他的左右孩子节点是否满足条件.
    遍历5 6 3, 不满足条件, 继续交换 5 和 6, 得到 6 5 3
    在这里插入图片描述
    遍历4 2 9, 不满足条件, 交换4 和 9, 得到9 2 4
    在这里插入图片描述
    遍历 8 7 9, 不满足条件, 交换8 和 9
    在这里插入图片描述
    最终, 得到了结果 9 7 8 6 1 2 4 5 3, 这个就是第一次遍历后得到的最终结果.

3. 交换位置

从结果中我们发现: 根节点值最大,并且满足每一个父节点均大于左右孩子节点. 此时的结果就是一个 大顶堆
在这里插入图片描述

  1. 接下来, 我们只需要将 根节点值最后一个值 交换.可以得到 3 7 8 6 1 2 4 5 9
    在这里插入图片描述

  2. 这时, 最大的数字放到了最后, 我们就完成了初步交换, 接着我们将 9 剔除, 9不再参与 堆性质的维护.
    在这里插入图片描述

  3. 从3开始, 继续维护堆的性质, 并且交换位置, 最终得到如下结果: 8 7 4 6 1 2 3 5
    在这里插入图片描述

  4. 继续交换根节点和最后一个节点的位置, 得到5 7 4 6 1 2 3 8
    在这里插入图片描述

  5. 将8 剔除, 并且重复以上步骤, 得到了最终结果 1 2 3 4 5 6 7 8 9
    在这里插入图片描述

  6. 这样子我们就得到了最终排序后的结果

4. 代码实现

public static void main(String[] args) {
    int[] nums = new int[]{3,5,4,7,1,2,9,6,8};
//        int[] nums = new int[]{-4,0,7,4,9,-5,-1,0,-7,-1};
    heapSort(nums, nums.length);
    for (int num : nums) {
    	// 1 2 3 4 5 6 7 8 9
        System.out.println(num);
    }
}

/**
 * 维护堆的性质
 * @param nums 数组
 * @param len 数组长度
 * @param i 数组下标
 */
public static void heapify(int[] nums, int len, int i) {
    // 根节点的下标
    int largest = i;
    // 左子结点下标
    int l = 2 * i + 1;
    // 右子节点下标
    int r = 2 * i + 2;
    // 判断 左子节点 的值 是否大于 根节点的值, 如果大于, 则交换位置
    if (l < len && nums[l] > nums[largest]) {
        largest = l;
    }
    // 判断 右子节点 的值 是否大于 根节点的值, 如果大于, 则交换位置
    if (r < len && nums[r] > nums[largest]) {
        largest = r;
    }
    // 如果根节点的下标发生了变化, 则进行交换位置
    if (largest != i) {
        // 交换位置
        swap(nums, largest, i);
        // 递归维护堆的性质
        heapify(nums, len , largest);
    }
}

/**
 * 构建堆 & 排序
 * @param nums
 * @param len
 */
public static void heapSort(int[] nums, int len) {
    // 下标为i的左子结点
    for (int i = (len - 1) / 2; i >= 0; i--) {
        heapify(nums, len, i);
    }

    // 循环遍历, 交换根节点 与 最后一个节点 位置
    for (int i = len - 1; i > 0; i--) {
        swap(nums, 0, i);
        // 维护堆的性质
        heapify(nums, i, 0);
    }
}

/**
 * 交换位置
 * @param nums
 * @param l
 * @param r
 */
public static void swap(int[] nums, int l, int r) {
    int tmp = nums[l];
    nums[l] = nums[r];
    nums[r] = tmp;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络|数据流向剖析与分层模型详解

文章目录 一、网络中的数据流向二、计算机网络通信模型1.OSI 模型2.TCP/IP 模型3.TCP/IP五层模型3.1 分层架构描述3.2各层地址结构3.3UDP数据包报头结构 三、总结 一、网络中的数据流向 在计算机网络中&#xff0c;数据的流向是指数据从发送端到接收端的传输路径。数据流向涉及…

ensp、HCL环境部署vm版

ensp、HCL环境部署vm版 前言部署环境vmware安装下载镜像创建虚拟机安装ensp、HCL创建快照 问题此平台不支持虚拟化的 AMD-V/rvi。 前言 因为我换了电脑&#xff0c;锐龙版的win11&#xff0c;我按照以前的思路去装软件&#xff0c;发现有很多问题&#xff0c;特别是跳hyper-v弹…

鸿蒙项目云捐助第二十九讲云捐助项目云数据库商品的批量增加功能实现

鸿蒙项目云捐助第二十九讲云捐助项目云数据库商品的批量增加功能实现 关于鸿蒙云捐助项目&#xff0c;前面的内容已使用云函数&#xff0c;云数据库分别实现云捐助项目首页中的项分类导航&#xff0c;底部导航&#xff0c;轮播图功能&#xff0c;这里继续实现云数据库加载捐赠…

【LeetCode: 83. 删除排序链表中的重复元素 + 链表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Spring源码_05_IOC容器启动细节

前面几章&#xff0c;大致讲了Spring的IOC容器的大致过程和原理&#xff0c;以及重要的容器和beanFactory的继承关系&#xff0c;为后续这些细节挖掘提供一点理解基础。掌握总体脉络是必要的&#xff0c;接下来的每一章都是从总体脉络中&#xff0c; 去研究之前没看的一些重要…

2024-12-29-sklearn学习(25)无监督学习-神经网络模型(无监督) 烟笼寒水月笼沙,夜泊秦淮近酒家。

文章目录 sklearn学习(25) 无监督学习-神经网络模型&#xff08;无监督&#xff09;25.1 限制波尔兹曼机25.1.1 图形模型和参数化25.1.2 伯努利限制玻尔兹曼机25.1.3 随机最大似然学习 sklearn学习(25) 无监督学习-神经网络模型&#xff08;无监督&#xff09; 文章参考网站&a…

BUG分析 - 重启有时失败

1. 倒查版本 1.0_11 - ok1.0_12 - fail 2.对比1.0_11和1.0_12 失败时的日志 ================================== 1.0_11 ============================== 2024-12-26 09:46:51.886 INFO [26332] [ThreadPLCPool::in

git注意事项

提交代码的备注 feat : 开发 新增功能 fix: 修复 git相关 1. git安装及全局用户设置 Git安装 npm install git -ggit修改用户名邮箱密码 git config --global --replace-all user.name "要修改的用户名" git config --global --replace-all user.email"要修改…

LeetCode每日三题(六)数组

一、最大子数组和 自己答案&#xff1a; class Solution {public int maxSubArray(int[] nums) {int begin0;int end0;if(numsnull){//如果数组非空return 0;}else if(nums.length1){//如果数组只有一个元素return nums[0];}//初值选为数组的第一个值int resultnums[0];int i…

一个通用的居于 OAuth2的API集成方案

在现代 web 应用程序中&#xff0c;OAuth 协议是授权和认证的主流选择。为了与多个授权提供商进行无缝对接&#xff0c;我们需要一个易于扩展和维护的 OAuth 解决方案。本文将介绍如何构建一个灵活的、支持多提供商的 OAuth 系统&#xff0c;包括动态 API 调用、路径参数替换、…

小程序配置文件 —— 15 页面配置

页面配置 小程序的页面配置&#xff0c;也称为局部配置&#xff0c;每一个小程序页面也可以使用自己的 .json 文件来对页面的窗口表现进行配置&#xff1b; 需要注意的是&#xff1a;页面配置文件的属性和全局配置文件中的 window 属性几乎一致&#xff0c;只不过这里不需要额…

【从零开始入门unity游戏开发之——C#篇37】进程、线程和C# 中实现多线程有多种方案

文章目录 进程、线程和C#多线程一、进程的基本概念二、线程的基本概念三、C#中的多线程1、为什么需要多线程&#xff1f;2、*C# 中如何实现多线程**2.1 **使用 Thread 类**&#xff08;1&#xff09;示例&#xff08;2&#xff09;线程休眠&#xff08;3&#xff09;设置为后台…

评分模型在路网通勤习惯分析中的应用——提出问题(1)

1、问题的由来、目标和意义 最近一段时间和公司其它业务部门讨论时&#xff0c;发现一个有趣的交通路网问题&#xff0c;车辆从S点行驶到V点共用时40分钟&#xff0c;这段时间内路网中的卡口摄像头识别到了车辆通过的信息。如下图所示&#xff1a; 设计师需要通过这些有限的路…

机器学习DAY7: 特征工程和特征选择(数据预处理)(完)

本文通过特征提取、特征转换、特征选择三个过程介绍数据预处理方法&#xff0c;特征提取将原始数据转换为适合建模的特征&#xff0c;特征转换将数据进行变换以提高算法的准确性&#xff0c;特征选择用来删除无用的特征。 知识点 特征提取特征转换特征选择 本次实验的一些示…

【Unity3D】Jobs、Burst并行计算裁剪Texture3D物体

版本&#xff1a;Unity2019.4.0f1 PackageManager下载Burst插件(1.2.3版本) 利用如下代码&#xff0c;生成一个Texture3D资源&#xff0c;它只能脚本生成&#xff0c;是一个32*32*32的立方体&#xff0c;导出路径记得改下&#xff0c;不然报错。 using UnityEditor; using Uni…

紫光同创-盘古200pro+开发板

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 一、开发系统介绍 开发系统概述 MES2L676-200HP 开发板采用紫光同创 logos2 系列 FPGA&#xff0c;型号&#xff1a;…

【后端】LNMP环境搭建

长期更新各种好文&#xff0c;建议关注收藏&#xff01; 本文近期更新完毕。 LNMPlinuxnginxmysqlphp 需要的资源 linux服务器 web服务软件nginx 对应的语言编译器代码文件 数据库mysql安装 tar.gz包或者命令行安装 进入root&#xff1a; sodu 或su mkdir path/{server,soft}…

VSCode设置Playwright教程

1.安装扩展 打开VS Code&#xff0c;在扩展—>搜索"Playwright Test for VSCode"&#xff0c;点击安装 按快捷键CommandShiftP&#xff0c;输入install playwright&#xff0c;点击安装Playwright 安装成功会有如下提示 2.调试脚本 打开tests/example.spec.ts文…

RK3566和Robo_C的EMC防护设计细节

USB部分的防护细节&#xff1a; ROBO C的USB接口&#xff1a; PF级别的电容滤波&#xff1a; TVS电容&#xff08;TVS Capacitor&#xff09;&#xff1a;用于与TVS二极管配合&#xff0c;保护电路免受瞬态电压冲击。电容一般较小&#xff0c;通常为几十皮法&#xff08;pF&am…

MicroDiffusion——采用新的掩码方法和改进的 Transformer 架构,实现了低预算的扩散模型

介绍 论文地址&#xff1a;https://arxiv.org/abs/2407.15811 现代图像生成模型擅长创建自然、高质量的内容&#xff0c;每年生成的图像超过十亿幅。然而&#xff0c;从头开始训练这些模型极其昂贵和耗时。文本到图像&#xff08;T2I&#xff09;扩散模型降低了部分计算成本&a…