【排序】详解堆排序

一、思想

堆排序是一种基于比较的排序算法,且使用了堆的数据结构来辅助进行排序。其思想是利用堆的特性,即在每个节点都保证是最大(大顶堆)或者最小(小顶堆)的关键码。具体原理和步骤如下:

  1. 构建初始堆:将待排序的数组构造成一个最大堆(升序排序时使用)或最小堆(降序排序时使用)。这通过从最后一个非叶子节点开始,依次向上对每个父节点进行调整来完成。调整的方式是,如果父节点的值小于其子节点的值(对于最大堆),则将它们交换,直到该父节点的值大于其所有子节点的值,或已达到数组的开头。
  2. 堆顶元素交换:将堆顶元素(即当前最大的元素)与数组的最后一个元素交换,此时最后一个元素放置了当前的最大值。
  3. 调整堆结构:由于交换了堆顶元素,堆的结构被破坏,需要对剩下的元素再次进行调整以维持堆的性质。这一过程是从新的堆顶元素开始向下进行的,确保父节点的值大于或等于其子节点的值。
  4. 重复操作:重复执行步骤2和步骤3,每次减少一个元素进行堆调整,直至整个数组有序。每轮排序后,数组的末尾都会增加一个排序好的元素,直到所有元素都排好序。

总的来说,堆排序是一个较为高效的排序算法,其时间复杂度为O(nlogn)。尽管它在最坏、平均和最好情况下的时间复杂度都一样,但是常数因子较小,因此在实践中通常比其他O(nlogn)算法更快。此外,堆排序也不需要额外的存储空间,是一种原地排序算法

二、图解
1.图解堆化

首先我们要了解一个数组如何调整为一个堆,首先有这样一个数组,我们如何让他调整成为大根堆(所有的父亲节点比孩子节点的元素值大)

我们只需要从后往前依次对子堆进行向下调整即可。具体实现如下:首先我们需要从最后一个孩子所在的子堆进行调整,最好一个孩子就是数组长度-1的位置,那么我们如何根据孩子节点的下标去求出父亲节点的下标呢,我们只需要让孩子节点的下标减去1的差然后除2就能得到父亲节点的下标,同样我们知道父亲节点的下标同样除2在加1就能求出孩子节点的下标。接着进行向下调整:首先对于这个堆如何进行向下调整呢?

首先我们需要找到孩子节点中的最大值,然后拿他与父亲节点比较,如果他比父亲节点大,那么就进行交换,然后依次向下进行调整(调整孩子节点的子堆),如果比父亲节点小说明这个堆已经调整完成我们就可以结束向下

此时交换完成后,继续向下进行调整

这个时候发现已经没有孩子节点了于是结束这个堆的调整开始调整下一个堆

这个时候孩子节点比父亲节点值大,于是进行交换,交换后继续向下进行调整parent = child; child = 2 * parent + 1,重复上述操作找到孩子的最大值继续与父亲节点进行比较

孩子节点最大值比父亲节点值大继续进行交换与向下调整

这个时候孩子节点下标越界也就意味着没有孩子,于是结束调整,下面是堆化代码

2.图解堆排

在将数组堆化以后,我们就可以根据堆的特性进行排序,由于堆顶的元素一定是堆中最大的值。我们可以将他与最后一个元素交换,然后调整堆结束的下标防止最大元素在进行堆维护时重新回到堆顶。再将堆维护为大根堆重复这个操作,知道堆结束下标为0时

重新调整堆,在0位置继续向下调整维护大根堆

重复此操作将最大值交换到末尾

继续调整

交换到末尾

重复上述步骤知道结束下标指向0

 

三、代码实现
void heap_sort(vector<int>& arr) {
    // 1. 堆化
    for (int parent = (arr.size() - 1 - 1) / 2; parent >= 0; parent--) {
        int child = 2 * parent + 1;
        while (child < arr.size()) {
            if (child + 1 < arr.size() && arr[child] < arr[child + 1]) child++;
            if (arr[child] > arr[parent]) {
                swap(arr[child], arr[parent]);

                parent = child;
                child = 2 * parent + 1;
            } else {
                break;
            }
        }
    }

    // 2. 开始堆排
    int len = arr.size() - 1;
    while (len) {
        swap(arr[0], arr[len]);
        int parent = 0;
        int child = 2 * parent + 1;
        while (child < len) {
            if (child + 1 < len && arr[child] < arr[child + 1]) child++;
            if (arr[child] > arr[parent]) {
                swap(arr[child], arr[parent]);

                parent = child;
                child = 2 * parent + 1;
            } else {
                break;
            }
        }
        len--;
    }
}
    private static void siftDown(int[] arr, int parent, int len) {
        int child = 2 * parent + 1;
        while (child < len) {
            // 寻找两孩子节点中最大得
            if (child + 1 < len && arr[child + 1] > arr[child]) {
                child++;
            }

            // 孩子节点与父亲节点进行比较
            if (arr[child] > arr[parent]) {
                // 进行交换
                swap(arr, child, parent);
                // 继续向下调整
                parent = child;
                child = 2 * parent + 1;
            } else {
                // 父亲节点比孩子节点大, 满足大根堆结束调整
                break;
            }
        }
    }

    public static void heapSort(int[] arr) {
        // 堆化
        for (int parent = (arr.length - 1 - 1) / 2; parent >= 0; parent--) {
            siftDown(arr, parent, arr.length);
        }
        // 排序
        int len = arr.length - 1;
        while (len >= 0) {
            // 交换
            swap(arr, 0, len);
            // 调整
            siftDown(arr, 0, len);
            // 修改结束下标
            len--;
        }
    }

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

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

相关文章

基于SpringBoot的论坛系统(附项目源码+论文)

摘要 如今的时代&#xff0c;是有史以来最好的时代&#xff0c;随着计算机的发展到现在的移动终端的发展&#xff0c;国内目前信息技术已经在世界上遥遥领先&#xff0c;让人们感觉到处于信息大爆炸的社会。信息时代的信息处理肯定不能用之前的手工处理这样的解决方法&#xf…

最值得入手的五款骨传导耳机,六大专业的选购技巧

亲爱的小伙伴们&#xff0c;你们是否曾因长时间戴着耳机而感到耳朵不适&#xff0c;比如耳朵闷痛、发痒&#xff0c;甚至出现异味&#xff1f;现在有一种耳机可以帮你解决这些问题&#xff0c;它就是骨传导耳机。这种耳机的设计避免了传统入耳式耳机可能带来的堵塞感和细菌滋生…

【prompt五】CoCoOP:Conditional Prompt Learning for Vision-Language Models

motivation 随着像CLIP这样强大的预训练视觉语言模型的兴起,研究如何使这些模型适应下游数据集变得至关重要。最近提出的一种名为上下文优化(CoOp)的方法将提示学习(nlp的最新趋势)的概念引入视觉领域,以适应预训练的视觉语言模型。具体来说,CoOp将提示中的上下文单词转换为…

Golang 程序启动原理详解

一.编译 go源代码首先要通过 go build 编译为可执行文件,然后去机器上直接执行的&#xff0c;在 linux 平台上为 ELF 格式的可执行文件&#xff0c;linux 能直接执行这个文件,而编译阶段会经过编译器、汇编器、链接器三个过程最终生成可执行文件 编译器&#xff1a;*.go 源码通…

数字逻辑与计算机组成

冯诺依曼计算机 计算机结构 计算机特点 1.采用二进制 2.程序存储 2.由五大部件组成计算机系统&#xff1a;运算器、存储器、控制器、输入设备和输出设备 计算机硬件系统的层次 中央处理器&#xff08;CPU&#xff09;&#xff1a;运算器 控制器 计算机主机&#xff1a;…

【韩国留学】四大生活技能 学起来!柯桥留学中介韩语学习

如何高效拿学分 在韩国大学&#xff0c;学分是评价学生学习成果的重要标准。要想高效拿学分&#xff0c;首先要制定合理的学习计划。明确每学期需要修的课程&#xff0c;并提前预习&#xff0c;了解课程重点和难点。 其次&#xff0c;要积极参与课堂讨论&#xff0c;这不仅能提…

社科院与杜兰大学金融管理硕士——让我们的读研梦想,与春天一同醒来

随着春天的到来&#xff0c;万物复苏&#xff0c;生机盎然。在这个充满希望的季节里&#xff0c;你的读研梦想觉醒了吗&#xff1f;社科院与杜兰大学金融管理硕士项目为你提供梦想的种子&#xff0c;它将在你心中生根发芽&#xff0c;助你在学术殿堂里收获丰硕的果实。 中国社会…

第七个程序:两个字符串连接后计算长度

实验步骤; 第一步&#xff1a;新建项目 第二步&#xff1a;程序编写 第三步&#xff1a;运行结果 Labview一共7个字节&#xff0c;长度为7&#xff0c;一个字母一个字节 汉字为2个字节&#xff0c;图一为4&#xff0c;图二为8 所以结果分别为11和15 视频教学&#xff1a; 字…

javaWebssh题库管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh题库管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Mye…

U410866 统计分数

本题为本人原创&#xff0c;请勿抄袭。 难度&#xff1a;普及- 题目背景 为了统计学生们的分数和排名&#xff0c;老师们翻来覆去睡不着觉。请你为老师编写一个这样的程序。 题目描述 这是一题将结构体和排序结合在一起的题。 输入格式 输入&#xff1a; 第一行&…

javascript操作BOM的方法

目录 1.window.alert() 2.window.confirm() 3.window.prompt() 4.window.location() 5.window.navigator() 6.window.screen() 7.window.history() 8.window.setTimeout() 和 window.clearTimeout() 9.window.setInterval() 和 window.clearInterval() BOM&#xff08…

Unity 轮转图, 惯性, 自动回正, 点击选择

简单的实现 2D 以及 3D 的轮转图, 类似于 Web 中无限循环的轮播图那样. 文中所有代码均已同步至 github.com/SlimeNull/UnityTests 3D 轮转图: Assets/Scripts/Scenes/CarouselTestScene/Carousel.cs2D 轮转图: Assets/Scripts/Scenes/CarouselTestScene/UICarousel.cs 主要逻…

【学习记录】C++面向对象高级编程【更新中】

C面向对象高级编程 1 inline-内联函数1.1 什么是内联函数&#xff1f;1.2 为什么需要内联函数&#xff1f; 2 构造函数2.1 构造函数是什么&#xff1f;2.2 为什么需要构造函数&#xff1f;2.3 ctor(构造函数)可以有很多个-overloading重载2.4 ctors放在private区-Singleton 3 参…

Anthropic发布最强大模型Claude 3,实力碾压GPT-4和Gemini!

前言 2024年3月4日&#xff0c;Anthropic 发布了Claude 3新版系列模型&#xff0c;含Haiku、Sonnet 和 Opus三个版本。其中最强大的模型在各种基准测试中均优于OpenAI的GPT-4和Google的 Gemini 1.0 Ultra&#xff0c;已成为大模型领域的新巨头。 大家如果对AI感兴趣&#xff0c…

TensorRT入门:trtexec开发辅助工具的使用

文章目录 一、trtexec简介二、trtexec使用1.trtexec常用参数1. 构建阶段2. 运行阶段 2.基本使用方法1. trtexec最基本的使用方法&#xff0c;读取onnx模型并通过trtexec测试推理性能。2. trtexec解析ONNX文件&#xff0c;使用优化选择构建TensorRT引擎并保存至.plan文件补充&am…

力扣--动态规划64.最小路径和

思路分析&#xff1a; 基本思路&#xff1a; 本算法采用动态规划的思想&#xff0c;通过构建一个额外的二维矢量 dp 来存储每个位置的最小路径和。最终目标是求得右下角位置的最小路径和&#xff0c;即整个网格的最小路径和。 初始化&#xff1a; 初始化矢量的行数和列数&…

使用awk和正则表达式过滤文本或字符串 - 详细指南和示例

当我们在 Linux 中运行某些命令来读取或编辑字符串或文件中的文本时&#xff0c;我们经常尝试将输出过滤到感兴趣的特定部分。这就是使用正则表达式派上用场的地方。 什么是正则表达式&#xff1f; 正则表达式可以定义为表示多个字符序列的字符串。关于正则表达式最重要的事情之…

考研数学|数一125学长备考经验+资料

考研数学复习规划的关键&#xff0c;是不要执着于进度&#xff0c;不要执着于每天每个时间段准确的划分去做什么做什么&#xff0c;就好像完成任务的权重大于复习质量的权重一样&#xff0c;本末倒置了。 正确的做法&#xff0c;是聚焦于学习质量&#xff0c;持之以恒。所需要掌…

FreeRTOS操作系统学习——FreeRTOS工程创建

FreeROTS工程创建 详细步骤 如无特殊情况&#xff0c;大部人都要配置为外部高速时钟 另外&#xff0c;本实验使用了FreeRTOS&#xff0c;FreeRTOS的时基使用的是Systick&#xff0c;而 STM32CubeMX中默认的HAL库时基也是Systick&#xff0c;为了避免可能的冲突&#xff0c;最…

如何理解XML解析库?

untangle untangle 是一个简洁的用于解析 XML 文档的库。输入一个 XML 文档后&#xff0c;untangle 将文档的结构映射成结点和属性&#xff0c;并返回一个 Python 对象。 形如以下的 XML 文件&#xff1a; <?xml version"1.0"?> <root><child nam…