C++算法:排序之三(堆排序)

news2024/11/25 0:48:02

C++算法:排序

排序之一(插入、冒泡、快速排序)
排序之二(归并、希尔、选择排序)
排序之三(堆排序)
排序之四(计数、基数、桶排序)


文章目录

  • C++算法:排序
  • 二、比较排序算法
    • 7、堆排序


本文续:C++算法:排序之二(归并、希尔、选择排序)


二、比较排序算法

7、堆排序

堆排序和前面C++数据结构:二叉树之一(数组存储)提到的特抽象的二叉树很有关系,文中提到的完全二叉树的数组存储法,就是堆排序的关键。一般我们都采用大顶堆(也叫大根堆,根节点最大的意思)的方式进行排序,实现的核心思想就一句话:就是一直保持任一根节点总是大于左右子节点的。

很明显这是一个牵一发而动全身的工作,调整了一个结点使其符合大顶堆规则了,可能别的节点又不符合了,我们先找一个静态图片来说明这个问题再看动态图就好理解了:

  • 1、假设存在以下一个符合大顶堆的特征的二叉树,至于一个数组它为什么是二叉树,不明白的去看前文。
    在这里插入图片描述

  • 2、然后我们将图中标记的23替换成5,用以说明调整过程:
    在这里插入图片描述

  • 3、替换成5后,作为根节点,见图a:它比左节点18、右节点15都要小,所以和它的子节点中的最大的交换,就是和左节点18交换,之后就成了图b所示的样子。显然5还是一个根节点,它又比左右节点都要小,所以要继续和子节点中最大的12交换。
    在这里插入图片描述

  • 4、图c就是最后完成的样子,节点5最终被移到了右叶子节点,整个二叉树又符合大顶堆的特性了。理解了这个逻辑再看下面的动图就很容易明白了。

在这里插入图片描述
动图中后期标红的就是排序过程,在初次完成大顶堆的调整后,将根节点移动到层序遍历的最后一个节点,根据数组存储的规律其实就是数组的最后一个元素。如此就造成了大顶堆特性不满足了,那就把最后一个元素从循环中剔除再交换其余元素,使其满足大顶堆特性,如此循环直到完成排序。

代码如下(示例):

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void keep_heap(vector<int> &vec, int len, int node){ 
/*调整结点符合大顶堆特性,len是数组长度,排序时会递减这个值用以排除数组后面已排序的元素,
所以要传递这个参数, node是非叶子节点下标*/

    int left, right, biggest;
    biggest = node;         //某分支的三个节点中最大的,先默认为根;
    left = node * 2 + 1;    //左节点的下标,因为从0开始,所以要加1
    right = node * 2 + 2;   //右节点的下标,因为从0开始,所以要加2
    if (left<len && vec[left] > vec[biggest]){  //要保证不调整已排序的节点
        biggest = left;
    }
    if (right<len && vec[right] > vec[biggest]){
        biggest = right;
    }
    if (biggest != node){       //调整为根节点最大
        swap(vec[node], vec[biggest]);
        keep_heap(vec, len, biggest);  //递归调整交换后的节点,biggest是下标,不会被交换
    }

}

void big_heap(vector<int> &vec){   //第一次建立大顶堆要遍历所有非叶子节点
    int len = vec.size();
    int node = len/2 - 1;  //根据完全二叉树的规则,非叶子节点数是:节点数/2 -1
    while (node >= 0){
        keep_heap(vec, len, node);
        node--;
    }
}

void heap_sort(vector<int> &vec){  //排序函数
    int len = vec.size();
    big_heap(vec);               //第一次建立大顶堆
    for (int i=len-1; i>0; i--){    //开始将最大元素(根节点)交换到数组后面
        swap(vec[0], vec[len-1]);   //将大顶堆的根交换到数组最后面
        len--;                      //排除已交换的下标
        keep_heap(vec, len, 0);     //重建大顶堆
    }
}

int main(){
    vector<int> vec = {91,60,96,13,35,65,46,65,10,30,20,31,77,81,22};
    heap_sort(vec);
    for (auto it=vec.begin(); it!=vec.end(); it++){
        cout << *it << " ";
    }
    return 0;
}

堆排序是一种很优秀的排序算法,具备了插入排序和归并排序的一些特征。时间复杂度是O(NlogN),又是就地排序,所以应用范围很广。这种排序法是在完全二叉树这种数据结构上实现的,那么显然它也可以用于链表结构,只是实现起来要麻烦得多,因为不能用下标操作。但是改进一下代码也是可以实现的,比如笔者曾经花了点时间写了个可以用下标操作的List。当然这是个笨办法,还可以用迭代器来实现。

C++的标准模板库中的 sort 排序就用到了堆排序,再比如游戏服务器排行榜那这种方式排序就太合适了。

在开发游戏排行榜功能时,由于游戏中的玩家不停地进入服务器,离开服务器,所以我们的元素个数是动态的,使用其他的一些算法只能应对一些一次性把所有的元素算完的情况。而如果使用堆排序,就可以不断地往堆里增加元素而不需要重新排序,这就是堆排序的优势。

比如你要在10万个人里排出前100名,这时不管10万个人怎样进进出出,只要进入一个就push一个,只要保证堆里有100个人就可以了,而且这个排行榜的开销也是很低的,只是在这100个元素里进行最小顶堆排序。这样就可以快速地更新游戏服务器在线排名

所以本系列排序算法文章单独给堆排序写了一文,一方面是这个排序法用到了完全二叉树这种数据结构,解释清楚比较费字还费图。另一方面,这种排序和快速排序一样重要,只要你想当个正经码农,就必须熟练掌握的。至于小顶堆的实现,也就不用单独再费神来实现一遍了,把比较大小部分的大于号改成小于号就行了,当然你最好改个变量名是吧?

好了,十大排序法中所有比较排序的算法都写完了,下一节就是非比较排序了。


未完待续…

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

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

相关文章

全排列--回溯

1题目 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2&#xff1a; 输入&#xff1a…

关于Spring中自带的@Schedule实现自动任务

SpringBoot中自带了一个能够实现定时任务的注解Schedule 如果定时任务比较简单&#xff0c;则可以使用SpringBoot中自带的Schedule&#xff0c;但是如果任务调度很复杂的话&#xff0c;就建议使用Quartz组件了。 说一下这个Schedule注解怎么用&#xff1f; 基本使用 第一步&a…

Centos8之系统升级

一、centos8系统简介 CentOS 8是一个基于Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码构建的开源操作系统。它是一款稳定、可靠、安全的服务器操作系统&#xff0c;适合用于企业级应用和服务的部署。CentOS 8采用了最新的Linux内核和软件包管理系统&#xff0c…

chatgpt赋能python:Python如何分行——提高代码可读性和效率的必备技能

Python如何分行——提高代码可读性和效率的必备技能 什么是分行&#xff1f; 分行&#xff0c;即将一行长代码分为多行&#xff0c;使得代码更加易读、易维护、易修改。 Python作为一门高级编程语言&#xff0c;具有简洁、易读、高效的特点。但在实际编程过程中&#xff0c;…

Amazon Device EDI 数据库方案开源介绍

近期为了帮助广大用户更好地使用 EDI 系统&#xff0c;我们根据以往的项目实施经验&#xff0c;将成熟的 EDI 项目进行开源。用户安装好知行之桥EDI系统之后&#xff0c;只需要下载我们整理好的示例代码&#xff0c;并放置在知行之桥指定的工作区中&#xff0c;即可开始使用。 …

C++调python程序示例

背景 平台&#xff1a;Xavier nvidia AGX板子 编写c程序测试单目3D目标检测DEVIANT&#xff08;https://blog.csdn.net/qq_39523365/article/details/130982966?spm1001.2014.3001.5501&#xff09;python算法的过程。代码如下&#xff1a; 文件结构 具体代码&#xff1a; …

基于 prefetch 的 H5 离线包方案 | 京东云技术团队

前言 对于电商APP来讲&#xff0c;使用H5技术开发的页面占比很高。由于H5加载速度非常依赖网络环境&#xff0c;所以为了提高用户体验&#xff0c;针对H5加载速度的优化非常重要。离线包是最常用的优化技术&#xff0c;通过提前下载H5渲染需要的HTML/JS/CSS资源&#xff0c;加…

如何用Arcgis做一个地区的温度或降水量分布图

1.首先需要拥有一张shp格式的研究地区的矢量区域图&#xff08;很多网站都可以下载到全国各地区县域或者省域界线的矢量图&#xff0c;比如小木虫&#xff09;&#xff0c;以山西省为例: 2.导入本研究区域样地或样点的经纬度、温度&#xff08;或降水&#xff09;的csv格式数据…

我与 InsCODE AI 创作助手的一次对话

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 我与INDCODE AI 创作助手的一次对话 &#x1f9ca;摘要&#x1f9ca;前言&#x1f9ca;对话内容&#x1f9ca;结束语 &#x1f9ca;摘要 本文介绍了 CSDN 嵌入式INSCODE AI 创作助手…

华为OD机试真题 Java 实现【跳格子2】【2023 B卷 100分】,附详细解题思路

一、题目描述 小明和朋友玩跳格子游戏&#xff0c;有n个连续格子组成的圆圈&#xff0c;每个格子有不同的分数&#xff0c;小朋友可以选择从任意格子起跳&#xff0c;但是不能跳连续的格子&#xff0c;不能回头跳&#xff0c;也不能超过一圈。 给定一代表每个格子得分的非负整…

【项目】GCC(gcc,g++)、静态库的制作与使用、动态库

GCC、静态库 1.2 GCC(1&#xff09;gcc&#xff08;1&#xff09;常用命令&#xff08;2&#xff09; C程序编译过程&#xff08;3&#xff09;GCC工作流程 1.3 GCC(2&#xff09;g1.3静态库的制作1.5静态库的使用1.6动态库的制作1.7动态库加载失败的原因1.8解决动态库加载失败…

六面钻调试流程(第一步.设置轴参数)

第一步.设置轴参数 &#xff08;1&#xff09;编码器类型 &#xff08;2&#xff09;编码器位数 &#xff08;3&#xff09;设置站地址开关 &#xff08;4&#xff09;设置从站地址 &#xff08;5&#xff09;设置从站地址偏移地址

【云原生】docker-Cgroup资源限制

Docker容器的资源控制 Docker通过Cgroup 来控制容器使用的资源配额&#xff0c;包括CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配额和使用量控制。Caroup 是ControlGroups的缩写&#xff0c;是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源…

报表生成器FastReport .Net用户指南: 显示表达式

FastReport .Net是一款全功能的Windows Forms、ASP.NET和MVC报表分析解决方案&#xff0c;使用FastReport .NET可以创建独立于应用程序的.NET报表&#xff0c;同时FastReport .Net支持中文、英语等14种语言&#xff0c;可以让你的产品保证真正的国际性。 FastReport.NET官方版…

Tomcat的安装与使用,Maven与Servlet的使用

文章目录 一.Tomcat的下载与使用1. Tomcat简介2. 安装和使用 二.Maven的使用三.Servlet1. 第一个Servlet程序2. 在idea中集成Tomcat3. 常见错误 一.Tomcat的下载与使用 1. Tomcat简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目, 由…

学习Kafka生产者的缓冲池设计

大家一定都了解Java的线程池&#xff0c;线程池有什么好处呢&#xff1f;如果没有线程池&#xff0c;我们每次创建线程都要新建一个线程&#xff0c;这样对CPU的消耗比较大。那么利用线程池我们可以对已经创建好的线程复用&#xff0c;线程就不用频繁创建和销毁了。 同样&…

回顾 | Semantic Kernel:面向 AI 编程(三) - 云原生

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun 微软 Reactor 为帮助广开发者&#xff0c;技术爱好者&#xff0c;更好的学习 .NET Core, C#, Python&#xff0c;数据科学&#xff0c;机器学习&#xff0c;AI&#xff0c;区块链, IoT 等技术&#xff0…

chatgpt赋能python:Python如何创建角色

Python如何创建角色 在游戏开发中&#xff0c;创建角色是非常重要的一个环节&#xff0c;也是游戏设计的重要一环。Python作为一种广泛使用的编程语言&#xff0c;可以用于快速且高效地创建角色。 1. 创建角色的基本思路 创建角色的主要思路是定义角色的属性&#xff0c;包括…

ReactJS入门(一)—— 初步认识React

React刚开始红的时候&#xff0c;由于对其不甚了解&#xff0c;觉得JSX的写法略非主流&#xff0c;故一直没打算将其应用在项目上&#xff0c;随着身边大神们的科普&#xff0c;才后知后觉是个好东西。 好在哪里呢&#xff1f;个人拙见&#xff0c;有俩点&#xff1a; 1. 虚拟…

14.数据结构之多路查找树与堆

前言 之前介绍的都是二叉查找树&#xff0c;二叉树一个节点最多有两个子节点&#xff0c;那么多于两个节点是什么情况呢&#xff0c;这就是我们本节要介绍的多路查找树。 多路查找树&#xff0c;也是我们数据库mysql底层索引维护方式。下面&#xff0c;我们来详细介绍。 1. …