【数据结构】堆排序详细图解

news2025/4/16 2:39:07

堆排序目录

  • 1、什么是堆?
    • 1.1、什么是大顶堆
    • 1.2、什么是小顶堆
  • 2、堆排序的过程
  • 3、堆排序的图解
    • 3.1、将数组映射成一个完全二叉树
    • 3.2、将数组转变为一个大顶堆
    • 3.3、开始进行堆排序
  • 4、堆排序代码

1、什么是堆?

堆的定义在一棵完全二叉树中,每一棵子树的根节点值均大于或小于其左右子树的所有根节点值,被称为堆。其中每一棵子树的根节点值均大于左右子树的节点时,这棵树被称为大顶堆,反之,被称为小顶堆。

堆的特殊定义导致了堆具有特殊的结构,也就是说,一个堆的根节点,肯定是最大/最小的,因此,堆排序就是要将一串数组放进一个堆中去,先将这个堆构建好,然后我们就可以肯定堆顶元素是这串数组中最大/最小的了,之后我们就取走堆顶元素,将堆中最后一个元素放到堆顶,然后再维护这个堆,让它重新成为一个合法的堆即可。

1.1、什么是大顶堆

在这里插入图片描述
我们对堆中的节点按层进行编号,映射到数组中就是下面这个样子:

在这里插入图片描述
大顶堆特点 a r r [ i ] > = a r r [ 2 i + 1 ] & & a r r [ i ] > = a r r [ 2 i + 2 ] arr[i]>=arr[2i+1]\&\&arr[i]>=arr[2i+2] arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2]

1.2、什么是小顶堆

在这里插入图片描述
在这里插入图片描述

小顶堆特点 a r r [ i ] < = a r r [ 2 i + 1 ] & & a r r [ i ] < = a r r [ 2 i + 2 ] arr[i]<=arr[2i+1]\&\&arr[i]<=arr[2i+2] arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2] //i对应第几个节点,i从0开始标号

一般升序采用大顶堆,降序采用小顶堆

2、堆排序的过程

堆排序的过程:

  1. 根据拿到的数组构建大顶堆/小顶堆;

  2. 从堆顶取走元素,放到其应该存在的位置中去。从堆底拿到堆中最后一个元素,放到堆顶,此时这个堆很可能不再合法也就是说不再是一个堆;

  3. 维护这个堆,通过自己写的方法调整堆中节点结构,让它重新变成一个堆;

  4. 重复2,3过程,直到堆被取空,此时数组也被完全排列好;

3、堆排序的图解

3.1、将数组映射成一个完全二叉树

我们先自己写了一个无序的数组,如图所示,这个数组是很没有规律的。然后我们既然想把这个数组构建成堆

在这里插入图片描述
接下来,我们根据这个数组的结构映射出一个完全二叉树的结构,如下图所示:

在这里插入图片描述

3.2、将数组转变为一个大顶堆

在这里我们使用大顶堆进行排序,基于小顶堆的堆排序和这里的算法思路相同,只是实现起来有微小的差异,不过需要声明的是:基于数组的堆排序使用大顶堆排序更加方便,写起来代码量更少一些。

大顶堆的构建,我们可以使用递归的方法来构建,但是这里我们暂且不深入研究递归,因此我们使用从堆底元素一个个排查构建的基础手段进行堆的构建,也就是从数组尾部一个一个往前找直到找到第一个不是叶子结点的节点后,我们对以它为根节点的子树进行整改,让其成为一个堆,这样一个个的往前遍历整改,就能够使得这棵树完全成为一个堆。这里只是简述了我的堆构建算法的大体思路,其中有很多细节将在下面进行详细的展示,同时使用我的算法在构建堆的时候存在一个非常重要的细节,它关系到这个堆能否被成功构建,接下来我们开始详细讲解如何构建一个堆。

  1. 后往前找,找到了第一个不是叶子结点的节点,如图所示:

    在这里插入图片描述
    可见该节点的叶子节点都小于根节点,也就是15,因此这棵子树本身就是一个大顶堆,我们继续向前遍历。

  2. 这时我们遍历到了值为7的节点,这个节点也大于其左子树和右子树,因此它也是一个大顶堆,我们继续向前遍历:

    在这里插入图片描述

  3. 我们这时遍历到了值为1的节点,很不幸,它小于它的左子树和右子树,它不再是堆了:

    在这里插入图片描述

    这时我们应该怎么做?我们应该从它的左右孩子中挑选出最大的一个,然后和根节点进行交换,这样就足以保证这棵子树的根节点大于它的所有直接孩子了。这里我们进行交换,会得到一个这样的新结构:

    在这里插入图片描述

  4. 我们这时遍历到了以值为3的节点为根的子树,这个子树显然也不是一个大顶堆,3比它的左右孩子都小,如图所示:

    在这里插入图片描述
    这时我们需要将其变成一个大顶堆,有了前面的经验,我们做起来轻车熟路,选择根节点孩子节点中的最大值和根节点交换就行了,如图所示:

    在这里插入图片描述

    等等!这,这不对吧?这棵子树的根节点确确实实已经大于它的左右直接孩子了,但是,由于3和15的交换,导致了该子树的右子树不再是一个大顶堆。现在,3是该子树的右子树的根节点,而这个根节点的右孩子是12,12大于3,它已经不符合大顶堆的概念了。这时,重要的知识点来了:由于大顶堆构建导致的一次节点值互换,有极大的可能直接导致以参与值交换的孩子节点为根节点的子树不再是一个大顶堆,简而言之,大顶堆的构建过程中的值互换操作,会导致一个更小的子树不再是大顶堆,放到这里就是,节点值为3的子树,为了变成大顶堆,和它的右孩子节点,也就是值为15的节点发生了交换,这时这颗子树的根节点值不再是3了,而是15,如上图所示,而这时,这个子树的右子树的根节点不再是15了,而变成了3,这就直接导致这个右子树不再是堆了,其有序性遭到了破坏。这时,我们要继续深入,攘外必先安内,解决掉这个问题。

    我们将当前的游标指向当前树的右子树根节点,也就是现在值为3的节点:

    在这里插入图片描述
    我们将此刻标红的节点继续处理,变成大顶堆,它没有右孩子,只有左孩子,因此不需要找最大孩子,直接交换就行:

    在这里插入图片描述
    现在我们将解决了刚才的问题,现在以15为根节点(也就是最初以3为根节点)的那个子树,彻彻底底确确实实的变成大顶堆了。可见,关于堆的维护,其实是穿插在堆的构建中的,构建堆的操作可能导致一个子树不再是堆,这时我们就应该在一次交换操作之后检索以参与交换的孩子节点为根节点的子树是否还是一个堆,如果不是了,那么我们必须要将当前的根节点游标指向它,将以它为根节点的子树作为新的问题规模,重复堆构建操作。现在我们解决了这个问题,就要回退到之前的位置,并继续向前遍历。

  5. 现在我们终于遍历到首节点了,也就是堆顶,或者说这棵树的根节点,也就是值为6的节点:

    在这里插入图片描述

    我们通过观察发现,现在这棵树显然不是一个大顶堆,根节点的左右孩子都比根节点大,我们挑选最大的直接孩子也就是15,和6交换:
    在这里插入图片描述

    有了之前的经历,我们已经见怪不怪了,由于6和15的交换,导致了这棵树的左子树不再是一个堆,因为现在以6为根节点的子树小于它的直接左右孩子的值,也就是6和12,好事多磨,我们没有办法,只得向下深入,将游标重新指向当前值为6的节点,将其重新进行堆构建:

    在这里插入图片描述

    我们发现当前子树的根节点的左右孩子最大的是12,因此我们做一个交换,并且在交换后我们将游标指向参与交换的孩子节点位置,检测这次交换是否造成了子树堆的破坏:

    在这里插入图片描述
    好在没有,现在我们将游标回退到之前的位置,一个大顶堆也宣告完成:

    在这里插入图片描述
    以上就是堆构建的过程,然而,你以为这就完了吗?答案是还没有,这只是把一个堆构建了出来,我们实际上还没有开始进行排序,但是实际上整个过程我们已经完成了大半了,堆的构建以及维护就是上面讲的内容了,而这些内容就是最为核心的内容,并且这个构建算法需要在堆排序中反复使用,因此大家要多加学习。接下来,我们开始进行堆排序。

3.3、开始进行堆排序

那么,堆排序的过程是怎样的呢?现在我们已经得到了构建一个大顶堆的算法,因此我们现在可以对这个堆进行一些操作并有自信将其变回一个新的大顶堆了。所以我们先将堆顶元素和堆底元素进行替换:

在这里插入图片描述

也就是将15和3进行互换,如上图所示。在互换后我们会得到如下图所示的一个新树,此时它已经不是大顶堆了:

在这里插入图片描述
之后,我们将堆底元素拿走,放到数组的最末端去,有:

在这里插入图片描述

在这个操作之后,我们发现以该树根节点为根节点的子树不再是大顶堆了,因为根节点发生了变化,因此我们对这棵树进行维护,使用上文“将数组转变为一个大顶堆”中提到的方法,让这棵新树重现变为大顶堆:

先让3和12交换:

在这里插入图片描述
然后我们发现有一棵左子树不再是大顶堆了,我们继续交换:

在这里插入图片描述
在此之后,又发现更小的一棵子树不是大顶堆了,我们继续进行堆维护操作:

在这里插入图片描述
至此,一棵新的大顶堆树又完成了,我们重复上文提到的堆顶元素和堆底元素交换的过程,并同样重复拿走交换后的堆底元素的操作:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
至此,我们又拿到了除15外最大的数字并排列到了15之后,重复这个过程,我们最终将得到一个从小到大的有序数组。在每次交换取数之后,再次进行堆的维护,这样一来我们就可以不断的取走当前剩余数字中最大的,并维持大顶堆,保证我们下一次取数也能立刻找到最大的。以上就是堆排序的过程。

4、堆排序代码

public class HeapSort {
    public static void main(String[] args) {
        int[] arr = new int[] { 5, 2, 1, 6, 4, 8, 11, 34, 56, 17, 26 };// 测试数组
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    
    public static void heapSort(int[] arr) {
        for (int i = arr.length - 1; i >= 0; i--) {
            adjustSort(arr, i, arr.length);
        }
        for (int i = arr.length - 1; i >= 0; i--) {
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            adjustSort(arr, 0, i);
        }
    }

    public static void adjustSort(int[] arr, int parent, int lenght) {
        int temp = arr[parent];
        int Child = 2 * parent + 1;
        while (Child < lenght) {
            // 找到左右子节点中较大的那个节点,如果左边大就用child,如果右边大就用child++
            if (Child + 1 < lenght && arr[Child] < arr[Child + 1]) {
                Child++;
            }
            if (temp >= arr[Child]) {
                break;
            }
            arr[parent] = arr[Child];

            parent = Child;
            // 查找当前节点的子节点,如果有子节点,继续调整
            Child = parent * 2 + 1;
        }
        // 交换数据
        arr[parent] = temp;
    }
}

adjustSort负责维护大顶堆,heapSort负责将堆顶元素与最后一个数对调,然后重复前面的数

码字不易,如果有收获不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

在这里插入图片描述

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

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

相关文章

CST1016.基于Spring Boot+Vue高校竞赛管理系统

计算机/JAVA毕业设计 【CST1016.基于Spring BootVue高校竞赛管理系统】 【项目介绍】 高校竞赛管理系统&#xff0c;基于 DeepSeek Spring AI Spring Boot Vue 实现&#xff0c;功能丰富、界面精美 【业务模块】 系统共有两类用户&#xff0c;分别是学生用户和管理员用户&a…

安卓性能调优之-掉帧测试

掉帧指的是某一帧没有在规定时间内完成渲染&#xff0c;导致 UI 画面不流畅&#xff0c;产生视觉上的卡顿、跳帧现象。 Android目标帧率&#xff1a; 一般情况下&#xff0c;Android设备的屏幕刷新率是60Hz&#xff0c;即每秒需要渲染60帧&#xff08;Frame Per Second, FPS&a…

GPT-SoVITS:5 步实现 AI 语音克隆

在 AI 技术高速迭代的今天&#xff0c;语音合成早已突破”机械朗读“的局限 —— 从短视频创作者的虚拟配音、游戏角色的个性化声线&#xff0c;到智能客服的自然交互&#xff0c;GPT-SoVITS正凭借其强大的多模态融合能力&#xff0c;成为实现”AI 声音克隆“与“情感化语音生成…

记录:安装 Docker Desktop 时直接设置安装路径及容器存储路径

近期学用 deepseek 本地知识库的构建&#xff0c;准备尝试几个不同的 RAG 工具&#xff0c;结果基本都需要 Docker 支持&#xff0c;故又重新拾起 Docker 来安装&#xff0c;刚好看到个不用目录链接就可以直接设置安装路径的方法&#xff0c;就记录一下&#xff0c;以免以后忘…

算法训练之贪心

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

Vagrant 安装指南:从零开始搭建开发环境

Vagrant 是一款强大的虚拟化工具&#xff0c;能够帮助开发者快速创建和管理轻量级的、可复制的开发环境。它通过与 VirtualBox、VMware 或 Hyper-V 等虚拟机提供程序结合使用&#xff0c;让你在本地轻松运行虚拟机。本文将详细介绍如何在 Windows、macOS 和 Linux 系统上安装 V…

APIGen-MT:高效生成多轮人机交互Agent数据的两阶段框架

APIGen-MT&#xff1a;高效生成多轮人机交互数据的两阶段框架 引言 随着人工智能技术的飞速发展&#xff0c;AI代理&#xff08;Agent&#xff09;已从简单的聊天机器人发展为能够执行复杂现实任务的系统&#xff0c;例如管理金融交易、安排预约和处理客户服务等。然而&#x…

【NLP】 21. Transformer整体流程概述 Encoder 与 Decoder架构对比

1. Transformer 整体流程概述 Transformer 模型的整个处理流程可以概括为从自注意力&#xff08;Self-Attention&#xff09;到多头注意力&#xff0c;再加上残差连接、层归一化、堆叠多层的结构。其核心思想是利用注意力机制对输入进行并行计算&#xff0c;从而避免传统 RNN …

《Vue Router实战教程》21.扩展 RouterLink

欢迎观看《Vue Router 实战&#xff08;第4版&#xff09;》视频课程 扩展 RouterLink RouterLink 组件提供了足够的 props 来满足大多数基本应用程序的需求&#xff0c;但它并未尝试涵盖所有可能的用例&#xff0c;在某些高级情况下&#xff0c;你可能会发现自己使用了 v-sl…

开发一个答题pk小程序的大致成本是多少

答题 PK 小程序通常指的是一种允许用户之间进行实时或异步答题竞赛的应用程序&#xff0c;可能结合PK答题、积分系统、排行榜等功能。 一、首先&#xff0c;确定答题 PK 小程序的基本功能模块。这可能包括用户注册登录、题库管理、题目类型&#xff08;单选、多选、判断等&am…

GPT-2 语言模型 - 模型训练

本节代码是一个完整的机器学习工作流程&#xff0c;用于训练一个基于GPT-2的语言模型。下面是对这段代码的详细解释&#xff1a; 文件目录如下 1. 初始化和数据准备 设置随机种子 random.seed(1002) 确保结果的可重复性。 定义参数 test_rate 0.2 context_length 128 tes…

科技项目验收测试包括哪些内容?有什么作用?

在现代科技快速发展的背景下&#xff0c;科技项目的验收测试已成为项目管理中的重要环节。科技项目验收测试是一种系统性的方法&#xff0c;旨在评估一个科技项目是否达到预定的技术指标和要求&#xff0c;确认项目的完成质量。该测试通常在项目实施完成后进行&#xff0c;通过…

websoket 学习笔记

目录 基本概念 工作原理 优势 应用场景 HTTP协议与 webSoket协议之间的对比 消息推送场景 1. 轮询&#xff08;Polling&#xff09; 2. 长轮询&#xff08;Long Polling&#xff09; 3. 服务器发送事件&#xff08;Server-Sent Events, SSE&#xff09; 4. WebSocket…

博途 TIA Portal之1200做从站与汇川EASY的TCP通讯

上篇我们写到了博途做主站与汇川EASY的通讯。通讯操作起来很简单,当然所谓的简单,也是相对的,如果操作成功一次,那么后面就很容易了, 如果操作不成功,就会很遭心。本篇我们将1200做从站,与汇川EASY做主站进行TCP的通讯。 1、硬件准备 1200PLC一台,带调试助手的PC机一…

【数据结构_6下篇】有关链表的oj题

思路&#xff1a; 1.分别求出这两个链表的长度 2.创建两个引用&#xff0c;指向两个链表的头节点&#xff1b;找到长度长的链表&#xff0c;让她的引用先走差值步数 3.让这两个引用&#xff0c;同时往后走&#xff0c;每个循环各自走一步 然后再判定两个引用是否指向同一个…

vscode+wsl 运行编译 c++

linux 的 windows 子系统&#xff08;wsl&#xff09;是 windows 的一项功能&#xff0c;可以安装 Linux 的发行版&#xff0c;例如&#xff08;Ubuntu&#xff0c;Kali&#xff0c;Arch Linux&#xff09;等&#xff0c;从而可以直接在 windows 下使用 Linux 应用程序&#xf…

关于 Spring Boot 微服务解决方案的对比,并以 Spring Cloud Alibaba 为例,详细说明其核心组件的使用方式、配置及代码示例

以下是关于 Spring Boot 微服务解决方案的对比&#xff0c;并以 Spring Cloud Alibaba 为例&#xff0c;详细说明其核心组件的使用方式、配置及代码示例&#xff1a; 关于 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案! https://sca.aliyun.com/?spm7145af80…

VS 基于git工程编译版本自动添加版本号

目录 概要 实现方案 概要 最近在用visual Studio 开发MFC项目时&#xff0c;需要在release版本编译后的exe文件自动追加版本信息。 由于我们用的git工程管理&#xff0c;即需要基于最新的git 提交来打版本。 比如&#xff1a; MFCApplication_V1.0.2_9.exe 由于git 提交信…

pytorch软件封装

封装代码&#xff0c;通过传入文件名&#xff0c;即可输出类别信息 上一章节&#xff0c;我们做了关于动物图像的分类&#xff0c;接下来我们把程序封装&#xff0c;然后进行预测。 单张图片的predict文件 predict.py 按着路径&#xff0c;导入单张图片做预测from torchvis…

【多线程-第四天-自己模拟SDWebImage的下载图片功能-看SDWebImage的Demo Objective-C语言】

一、我们打开之前我们写的异步下载网络图片的项目,把刚刚我们写好的分类拖进来 1.我们这个分类包含哪些文件: 1)HMDownloaderOperation类, 2)HMDownloaderOperationManager类, 3)NSString+Sandbox分类, 4)UIImageView+WebCache分类, 这四个文件吧,把它们拖过来…