【追求卓越12】算法--堆排序

引导

        前面几节,我们介绍了有关树的数据结构,我们继续来介绍一种树结构——堆。堆的应用场景有很多,比如从大量数据中找出top n的数据;根据优先级处理网络请求;这些情景都可以使用堆数据结构来实现。

什么是堆?

正如上文所说,堆是一种树结构。它的定义满足以下两点:

  1. 堆是一个完全二叉树
  2. 堆中每个节点的值必须大于等于(或小于等于)其子树中每个节点的值

完全二叉树在前面我们已经讲过;第二点,其实等价于堆中每个节点大于等于(或小于等于)其左右子节点。

对于每个节点大于等于左右子节点,我们称为大顶堆;每个节点小于等于左右子节点,我们称为小顶堆。

堆的操作

堆这种数据结构有点不一样。不适合查找或删除指定值的操作。

但是它可以帮助我们解决一些特定的问题。但是不同的问题,都离不开以下核心操作:插入一个元素,删除堆顶元素。

创建一个堆

我们知道完全二叉树适合用数组进行保存。因为它不需要保存左右指针,通过数组下标就可以实现。

数组下表为i的节点,其左子节点的数组下标为2*i,右子节点的数组下标为 2 *i+1;任意节点的父节点下标为i/2;根节点的下标为1.

插入一个元素

        向堆中插入一个数据,我们可以将新加入的数据加入到堆的后面,使其继续维持一个完全二叉树。

        但是插入一个新的数据就无法保证其父节点大于等于(或小于等于)左右子节点了。于是我们还要进行调整,使其满足堆的特性。我们称为这个过程为堆化。堆化的过程,其实也简单,只要循着插入节点,向上对比,进行交换即可。

/
*
大顶堆*
/
#define MAXLEN 1024
int heap[MAXLEN+1] = 0;
int count = 0;
int insert(int data)
{
    if(count >= MAXLEN)
        return -1; /
*堆满了*
/

    count++;

    heap[count] = data;
    int i = count;
    int temp = 0;
    while(i/2 > 0 && heap[i] > heap[i/2])
    {
        temp = heap[i/2];
        heap[i/2] = heap[i];
        heap[i] = temp;

        i = i/2;
    }
}

删除堆顶元素

由堆的特性所致,删除堆顶元素,实际上就是找出最大或者最小值的过程。删除堆顶元素其实也简单,可按照以下思路进行:

  1. 将最后一个元素放到堆顶,保证树是一个完全二叉树
  2. 从堆顶开始进行堆化,使其满足特质二

/
*
大顶堆*
/
#define MAXLEN 1024
int heap[MAXLEN+1] = 0;
int count = 0;
int  removeTop()
{
    if(count == 0) return -1;
    heap[1] = a[count];
    count--;

    heapify(count,1);
    return 0;
}
/
*从某个节点开始,向下堆化*
/
int heapify(int count, int start)
{
    while(true)
    {
        int max = start;
        if(i
*2 < count && heap[i*
2]>heap[max])
            max = i
2;
*        if(i*
2+1 < count && heap[i
2+1]>heap[max])
*            max = i*
2+1;   

        if(max == start)
            break;
        swap(heap,max,start);/
*交换max下标和start下标的值*
/
        start = max;
    }
}

        通过插入一个数据和删除堆顶元素的操作流程,我们知道时间的消耗主要集中在堆化中。由于完全二叉树的树高不会超过logn。所以堆的插入数据和删除堆顶元素的时间复杂度都是O(logn)。

如何实现堆排序?

        我们已经知道堆的性质以及基本的操作。但是对于一组数据我们该如何进行堆排序呢?原始的数据肯定是无序也不是堆结构的。因此我们第一步应该是建立堆。

建堆

建堆的方式有两种

第一种最简单:首先假设堆中的数据为0,依次将原始数据放到堆中。我们知道插入数据的时间复杂度是O(logn)。故插入第一个数据的消耗的时间为log1,第二个数据消耗的时间为log2...以此推测,第n个数据消耗时间为logn。

方法一消耗的时间为:log1+log2+log3...+logn=O(logn!),并且需要额外的内存。

第二种思路就比较特殊:对于完全二叉树而言,下标从n/2 + 1到n的节点都是叶子节点,并且叶子节点是不需要堆化的。

因此我们只需要将原始数据中下标从1到n/2进行堆化即可,并且不需要额外的内存。

int buildheap(int heap[],int count)
{
    int i = n/2;
    for( ; i >= 1 ; i--)
        heapify(heap,count,i);
}

那么第二种思路的时间复杂度是多少呢?

我们知道堆化的操作和所在节点的高度有关。由于叶子节点不需要堆化,并且每层节点和节点高度成正比,如图:

故消耗的时间为:

T = 2^0
*h + 2^1*
(h-1) + 2^2*(h-2) +...+ 2^(h-1)*1
T = -h +2 + 2^2 + 2^3 + ... + 2^h
T=2^(h+1) -h - 2

由于h=logn(以2为底)通过化简,得出时间复杂度为O(2n-h-2)=O(n)。

至此,我们已将完成建堆

排序

在删除堆顶元素时,我就说过了,这就是排序的过程。因此堆排序,就是将堆顶元素不断删除,直至堆为空。

代码如下:

int heapsort()
{
    buildheap(heap,count);
    int k = count;
    while(k > 1)
    {
        printf("%d\r",heap[1]);
        swap(heap,1,count);
        k--;
        heapify(a,k,1);
    }
    printf("\n");
}

现在我们看堆排序的时间复杂度是多少?

        建堆的时间复杂度是O(n),排序的时间复杂度是O(nlogn)(实际上应该是logn+log(n-1)+log(n-2)+...+log(1),这里方便,我们记录为O(logn))。故堆排序的时间复杂度为O(nlogn)。

快速排序的性能为什么比堆排序的要好?

在排序章节,我们提到了快速排序,它是时间复杂度也是O(nlogn)。但是在实际开发过程中,我们快速排序的性能优于堆排序。我觉得原因有以下几点:

  1. 堆排序的访问没有快速排序好。即使堆排序也是以数组为存储。但是由于它访问不是连续的,不能很好的利用CPU缓存。
  2. 对于同样的数据,堆排序的数据交换要多于快速排序。

堆应用

优先级队列

        在工作中,我们经常会遇到定时任务的问题。一般思路:将每个任务保存到数组中,每过一个时间间隔(1秒),就检测一下数组,看哪个任务达到了设定时间,如果到达了就取出任务执行,并删除

其实这样的定时器效率是很低的,为什么呢?

  1. 往往到达下一个任务之间的间隔是需要很长时间,那么在达到之前所有的检测都是无用的,这是在浪费系统资源
  2. 如果任务列表的长度很大,那么每次遍历,都会消耗很多的资源。这也是不可取的。

针对该类问题,我们可以采用优先级队列来解决。我们按照任务时间进行堆化。堆顶就是最先要被执行的任务。

修改方案:

  1. 根据任务时间进行堆化(小堆顶),堆顶任务就是下一个将要被执行的任务
  2. 计算现在距离下一个任务的时间T,sleep T秒,再执行堆顶任务。这样在执行下一个任务之前,定时器不需要做任何事情,性能大大提高。

Top k

堆在求top k的问题中,表现的也同样出色。

求top K的问题,我们可以先将数据排序,再取前K个元素。我们可以通过快排,那么求top K的时间复杂度是O(nlogn),这同样也很快,难道堆会更好吗?

堆应用的思路:

  1. 取数据中的前K个元素,建立小顶堆。
  2. 继续遍历数组中的元素,如果元素大于堆顶元素,则加入堆中,并删除堆顶元素。若比堆顶元素小,则不进行处理。
  3. 当遍历完整个数组后,堆中的数据就是前top K数据。

假设每个元素都需要进行插入,那么就是堆化n个元素,堆化的时间复杂度是O(logK),故使用过堆求top K的问题,其时间复杂度是O(nlogk)。

求中位数

在求中位数的问题中,我们一般的思路是将原始数据进行排序。

再求中位数,如果n为奇数的话,中位数就是n/2+1;如果n为偶数的话,中位数就是n/2或n/2+1。这种方式的消耗主要集中在对n个数据排序上面,使用快速排序,它的时间复杂度就是O(nlogn)。

但这样的方式比较适合静态数据,当数据能够动态添加时,再采取这样的方式,就不再适合了。

堆对于这种动态数据,求中位数有其优势。我们的思路是这样的:

  1. 先将现有数据进行升序排序,取前n/2个数据进行大堆顶堆化。后n/2个数据进行小堆顶堆化。(假设n为偶数。若n为奇数,取前n/2+1个数据为大堆顶)
  2. 当n为偶数时,中位数就是大堆顶和小堆顶的堆顶元素;当n为奇数时,中位数就是大堆顶的堆顶元素;
  3. 插入一个元素时,如果插入元素小于等于大堆顶元素,我们就将元素插入到大堆顶;反之插入到小堆顶。这样就容易不满足我们的预定:取前n/2个数据进行大堆顶堆化。后n/2个数据进行小堆顶堆化。(假设n为偶数。若n为奇数,取前n/2+1个数据为大堆顶)。我们就需要将两个堆的堆顶元素互相调整,使其满足上诉约定

        通过上述的逻辑,我们就可以得到一个适合动态数据,求中位数的方案。其时间复杂度主要集中在堆化过程O(logn)。

总结

        本节,我们介绍了堆这种数据结构以及堆结构的定义。也从代码的角度去实现了堆和堆排序。

        最后也比较了快速排序相对于堆排序的优点。我后面会继续总结一个关于堆排序的应用方向的一系列题。争取吃透。

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

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

相关文章

3.11-容器的资源限制

这一小节我们来看一下如何限制容器的资源&#xff0c;比如CPU和内存。 我们先来看一下对内存的限制。 --memory和--memory-swap这两个参数&#xff0c;如果我们只限定了--memory&#xff0c;没有限定--memory-swap&#xff0c;那么--memory-swap的大小就会和--memory大小一样。…

Cesium实现热力图功能

效果图如下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdev…

数字化时代的政务服务:构建便捷高效的线上政务大厅

引言&#xff1a; 随着数字化时代的来临&#xff0c;如何通过线上政务大厅搭建一个便捷高效的服务平台&#xff0c;以更好地满足公众需求值得探究。线上政务大厅是政务服务的新方式&#xff0c;但搭建线上政务大厅并不是一件容易的事情&#xff0c;需要精心的规划和设计。 一…

常量字符串(const)

数组名就是地址&#xff0c;str1与str2是两个不同的数组&#xff0c;虽然内容相同&#xff0c;但是地址不同&#xff0c;故为no const char * str是常量字符串&#xff0c;如果已有相同内容str3&#xff0c;则写入相同内容的str4是不会再开辟新的空间了&#xff0c;因为常量已…

react中模块化样式中:global的作用

在react中如果是通过import styles from ./index.less这种方式模块化引入样式的话&#xff0c;那么编译后的less文件里的样式名都会自动添加后缀。而:global的作用就是不让类名添加后缀

故障识别:CNN-BiLSTM-SelfAttention时空特征融合多头自注意力机制的故障识别程序,数据由Excel导入,直接运行!

适用平台&#xff1a;Matlab2023版及以上 本程序参考中文EI期刊《基于CNN-BiLSTM 的滚动轴承变工况故障诊断方法法》&#xff0c;程序注释清晰&#xff0c;干货满满&#xff0c;下面对文章和程序做简要介绍。 在CNN-BiLSTM-SelfAttention故障识别模型中&#xff0c;结合了卷积…

指标管理系统

参考 指标系统业务操作流程 阿里大数据开发治理平台

开源一个局域网文件共享工具

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 hello&#xff0c;夜深了&#xff0c;又是shigen深夜写博客的时间啦&#xff0c;今天分享的内容是《开源一个局…

OpenGeometry 开源社区特聘子虔科技云CAD专家 共建云几何内核

11月5日&#xff0c;由广东省工业和信息化厅、广东省科学技术厅、广东省教育厅、深圳市人民政府主办的2023工业软件生态大会在广东省深圳市召开。 开幕式上&#xff0c;备受关注的云几何内核开源平台——OpenGeometry开源社区正式发布。这意味着在几何引擎领域将通过开源这个模…

2023年亚太地区数学建模大赛 C 题

我国新能源电动汽车的发展趋势 新能源汽车是指以先进技术原理、新技术、新结构的非常规汽车燃料为动力来源&#xff08;非常规汽车燃料指汽油、柴油以外的燃料&#xff09;&#xff0c;将先进技术进行汽车动力控制和驱动相结合的汽车。新能源汽车主要包括四种类型&#xff1a;…

【追求卓越01】数据结构--数组

引导 这一章节开始&#xff0c;正式进入数据结构与算法的学习过程中。由简到难&#xff0c;先开始学习最基础的数据结构--数组。 我相信对于数组&#xff0c;大家肯定是不陌生&#xff0c;因为数组在大多数的语言中都有&#xff0c;也是大家在编程中常常会接触到的。我不会说数…

免费分享自建GPT网站,畅享无限创意!

PS&#xff1a;该文章基本为GPT3.5 撰写 这里与大家分享一个令人兴奋的消息&#xff1a;我建立了一个全新的GPT网站&#xff0c;并且我将免费提供给大家使用&#xff01;这个网站基于最新的GPT-3.5 Turbo模型&#xff0c;能够为您带来无限的创意和惊喜。 在这个免费的GPT网站…

一条命令彻底卸载Linux自带多个版本jdk

一条命令彻底卸载Linux自带多个版本jdk 检查系统已经安装的jdk rpm -qa | grep java卸载所有已经安装的 jdk xargs 将参数逐个传递 将已安装的 java 程序逐个当做参数传递给 rpm -e --nodeps rpm -qa | grep java | xargs rpm -e --nodeps再次检查系统已经安装的jdk rpm -qa | …

python-opencv划痕检测

python-opencv划痕检测 这次实验&#xff0c;我们将对如下图片进行划痕检测&#xff0c;其实这个比较有难度&#xff0c;因为清晰度太差了。 我们做法如下&#xff1a; &#xff08;1&#xff09;读取图像为灰度图像&#xff0c;进行自适应直方图均衡化处理&#xff0c;增强图…

基于爬行动物算法优化概率神经网络PNN的分类预测 - 附代码

基于爬行动物算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于爬行动物算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于爬行动物优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

梦开始的地方——Adobe Premiere Pro

今天&#xff0c;我们来说说一款老生常谈的相信也是很多人都经常迫切需要的软件。Adobe Premiere Pro&#xff0c;简称Pr&#xff0c;是由Adobe公司开发的一款视频编辑软件。 Premiere Pro是视频编辑爱好者和专业人士必不可少的视频编辑工具。它可以提升您的创作能力和创作自由…

Leetcode—6.N字形变换【中等】

2023每日刷题&#xff08;三十七&#xff09; Leetcode—6.N字形变换 算法思想 参考k神的题解 实现代码 class Solution { public:string convert(string s, int numRows) {if(numRows < 2) {return s;}vector<string> rows(numRows);int flag -1;int i 0;for(…

使用docker命令_进入容器_登录mysql服务_并执行sql语句---Docker工作笔记005

今天就用到了,不得不说用docker用到的还是少,记录一下,常用的也就这些吧. 首先执行: docker ps [root@localhost dataease-1.18.9]# docker ps CONTAINER ID IMAGE COMMAND CREATED …

【NLP】培训LLM的不同方式

在大型语言模型&#xff08;LLM&#xff09;领域&#xff0c;存在多种具有不同手段、要求和目标的培训机制。由于它们有不同的用途&#xff0c;因此重要的是不要将它们相互混淆并了解它们适用的不同场景。 在本文中&#xff0c;我想概述一些最重要的训练机制&#xff0c;包括预…

气候变化和人类活动对中国植被固碳的贡献量化数据月度合成产品

简介&#xff1a; 气候变化和人类活动对中国植被固碳的贡献量化数据月度合成产品包括中国2001~2018年地表短波波段反照率、植被光合有效辐射吸收比、叶面积指数、森林覆盖度和非森林植被覆盖度、地表温度、地表净辐射、地表蒸散发、地上部分自养呼吸、地下部分自养呼吸、总初级…