【数据结构】堆排序

news2024/11/26 23:26:01

堆是一种叫做完全二叉树的数据结构,可以分为大根堆,小根堆,而堆排序就是基于这种结构而产生的一种程序算法。

大堆:每个节点的值都大于或者等于他的左右孩子节点的值

小堆:每个结点的值都小于或等于其左孩子和右孩子结点的值

不管是大堆还是小堆父节点的数组下标和其孩子节点的下标关系都为

parent=(child-1)/2 leftchild=2*parent+1 rightchild=2*parent+2

  1. 建堆

建堆有两种方法1.向上调整建堆2.向下调整建堆

  1. 向上调整建堆

void Swap(int* p1, int* p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
void AdjustUp(int* a, int child)     //向上调整
{
    int parent = (child - 1) / 2;

    while (child > 0)
    {
        if (a[child] < a[parent])    //建大堆还是小堆在这里调整
        {                            //大堆a[parent]<a[child] 小堆反之
            Swap(&a[child], &a[parent]);
            child = parent;
            parent = (child - 1) / 2;

        }

        else
        {
            break;
        }
    }
}

void HeapSort(int* a, int n)
{
    for (int i = 0; i < n; ++i)
    {
        AdjustUp(a, i);
    }
}

int main()
{
    int a[] = { 1,10,2,20,3,30,4,40,5,50 };
    HeapSort(a, sizeof(a) / sizeof(int));

    for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        printf("%d ", a[i]);
    }

    return 0;
}

向上调整建堆是从数组的第一个元素开始一次向后遍历数组元素,每一个数组元素都向上调整建堆,这个过程就模拟建立起了堆,这个过程的时间复杂度推导过程:O(N*logN)

2.向下调整建堆(推荐使用)

void Swap(int* p1, int* p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
void AdjustDown(int* a, int n, int parent)     //向下调整
{
    int minChild = parent * 2 + 1;
    while (minChild < n)
    {
        if (minChild + 1 < n && a[minChild + 1] > a[minChild])   //这里控制大小堆
        {
            minChild++;

        }

        if (a[minChild] > a[parent]) //这里控制大小堆
        {
            Swap(&a[minChild], &a[parent]);
            parent = minChild;
            minChild = parent * 2 + 1;

        }
        else
        {
            break;
        }

    }


}
void HeapSort(int* a, int n)
{
    for (int i = (n - 1-1) / 2; i >= 0; --i)
    {
        AdjustDown(a, n, i);
    }
}

int main()
{
    int a[] = { 1,10,2,20,3,30,4,40,5,50 };
    HeapSort(a, sizeof(a) / sizeof(int));

    for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        printf("%d ", a[i]);
    }

    return 0;
}

向下调整的开始的位置是最后一个元素的父节点位置,因为最后一行的元素本来就符合大堆或者小堆的性质所以不用调整,根据数组的最后一个元素的下标计算出该节点的父节点的数组下标,从这个父节点开始向下调整,调整完后再将父节点的数组下标--,再从这个节点开始向下调整,直到调整完根节点后调整结束,堆就建立好了,大小堆是根据向下调整函数里的比较决定的。时间复杂度推导如下O(N):

因为向下调整的时间复杂度低于向上调整的时间复杂度,所以推荐使用的是向下调整建堆。

2.选数

了解完以后,我们来实现堆排序:

void AdjustDown(HPDataType* a, int n, int parent)
{
    //最小的默认为左孩子
    int minchild = 2 * parent + 1;
    while (minchild <n)
    {
        //找出小的那个孩子
        if (minchild+1<n&&a[minchild+1] < a[minchild])
        {
            minchild++;
        }
        //小堆
        if (a[minchild] < a[parent])
        {
            Swap(&a[minchild], &a[parent]);
            parent = minchild;
            minchild = 2 * parent + 1;
        }
        else
        {
            break;
        }
    }
}

void HeapSort(int* a, int n)
{
    
    for (int i = (n-1-1)/2; i>=0; i--)
    {
        AdjustDown(a, n, i);
    }
    //选数
    int i = 1;
    while (i < n)
    {
        Swap(&a[0], &a[n - i]);
        AdjustDown(a, n - i, 0);
        i++;
    }

}


int main()
{
    int a[] = { 15,1,19,25,8,34,65,4,27,7 };
    HeapSort(a, sizeof(a) / sizeof(int));

    for (int i = 0; i < sizeof(a) / sizeof(int); i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
    return 0;
}

这里的思路就是先对数组进行向下调整建堆,如果要升序就建立大堆,将根节点位置的元素和最后一个元素交换位置使得最大的元素放到了数组的最后边,放到后边的元素不参与向下调整(通过传参控制),然后让被换到根节点的元素向下调整,回到符合堆的性质的位置,此时调整完成后次大的元素被调整到了根节点的位置,再让根节点的元素和倒数第二个元素交换,交换到后边的元素不参与向下调整,再次进行向下调整,直到i=n时结束调整,此时输出数组就是升序的。如果要降序那就建立小堆。

总结一句话:

升序--建大堆,降序--建小堆

3.TOP-K问题

如何从数组中找到前K个大或者前K个小的数?

错误的思维:

选前K个大的数建立的是大堆,那最大的元素就在最上方,我们拿走了最大的元素,那剩下的元素堆的关系全乱了,又得重新排序,再选出最大的元素,这样一来假设要从特别大的一堆数据中进行选数那代价就非常大了,效率变得很低。同理选小数不能建小堆。

正确方法

  1. 用数据集合中前K个元素来建堆

前k个最大的元素,则建小堆

前k个最小的元素,则建大堆

  1. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

代码实现:

#include<stdlib.h>
#include<stdio.h>


void Swap(int* p1, int* p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}


void AdjustDown(int* a, int n, int parent)     //向下调整
{
    int minChild = parent * 2 + 1;
    while (minChild < n)
    {
        if (minChild + 1 < n && a[minChild + 1] < a[minChild])
        {
            minChild++;

        }

        if (a[minChild] < a[parent])
        {
            Swap(&a[minChild], &a[parent]);
            parent = minChild;
            minChild = parent * 2 + 1;

        }
        else
        {
            break;
        }

    }


}
void Topk(int* a, int k,int n)
{
    int* minheap = (int*)malloc(k * sizeof(int));
    if (minheap == NULL)
    {
        perror("malloc fail!");
        exit(-1);
    }

     for (int i = 0; i < k; i++)
    {
        minheap[i] = a[i];
    }

    for (int j = (k - 2) / 2; j >= 0; --j)
    {
        AdjustDown(minheap, k, j);   //向下调整建小堆因为选的是前K大的数 ---如果选小数就建大堆
    }

    int k1 = k;

    //遍历k后的元素交换然后调整
    while (k <n)
    {
        if (a[k] > minheap[0])
        {
            minheap[0] = a[k];
            AdjustDown(minheap, k, 0);

        }
        ++k;
    }


    for (int i = 0; i < k1; ++i)
    {
        printf("%d ", minheap[i]);
    }

}
int main()
{
    int a[] = { 1,10,2,20,3,30,4,40,5,50 };
    int k = 5;  //选出前5个大的数
    int n = sizeof(a) / sizeof(int);  //数组元素个数
    Topk(a, k,n);
    

    return 0;
}

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

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

相关文章

扬帆优配|业务量大突破,这个行业发展明显向好

近期上市的新股&#xff0c;大都在招股阐明书里公布了本年第一季度成绩预告。 我国快递事务量本年已达200亿件 国家邮政局监测数据显现&#xff0c;到3月8日&#xff0c;本年我国快递事务量已到达200.9亿件&#xff0c;比2019年到达200亿件提前了72天&#xff0c;比2022年提前…

goland开发环境搭建及运行第一个go程序HelloWorld

1、下载和安装golang 点击进入下载页面 下载好安装包&#xff0c;点击安装。 我之前安装过低版本的安装包&#xff0c;所以这里提示要先卸载已经安装过的低版本的。 同意协议&#xff0c;继续安装。 默认安装的文件夹为C盘&#xff0c;建议更改&#xff0c;我这里更改为D盘…

YOLOv5训练大规模的遥感实例分割数据集 iSAID从切图到数据集制作及训练

最近想训练遥感实例分割&#xff0c;纵观博客发现较少相关 iSAID数据集的切分及数据集转换内容&#xff0c;思来想去应该在繁忙之中抽出时间写个详细的教程。 iSAID数据集下载 iSAID数据集链接 下载上述数据集。 百度网盘中的train和val中包含了实例和语义分割标签。 上述…

哪些职业适合创业?学习哪些技能可以自己创业?

创意行业&#xff1a;创意行业包括广告、设计、影视等领域&#xff0c;需要创新思维和创意能力&#xff0c;适合创业。学习创意思维、平面设计、影视制作等技能可以自己创业。 科技行业&#xff1a;科技行业包括互联网、人工智能、物联网等领域&#xff0c;需要技术能力和创新思…

基于JavaEE开发博客系统项目开发与设计(附源码)

文章目录1.项目介绍2.项目模块3.项目效果1.项目介绍 这是一个基于JavaEE开发的一个博客系统。实现了博客的基本功能&#xff0c;前台页面可以进行文章浏览&#xff0c;关键词搜索&#xff0c;登录注册&#xff1b;登陆后支持对文章进行感谢、评论&#xff1b;然后还可以对评论…

[网络工程师]-网络规划与设计-逻辑网络设计(二)

3、广域网技术选择 3.1广域网互连技术 3.1.1 数字数据网络 数字数据网络(Digital Data Network,DDN)是一种利用数字信道提供数据信号传输的数据传输网,是一个半永久性连接电路的公共数字数据传输网络,为用户提供了一个高质量、高带宽的数字传输通道。 利用DDN网络实现局…

【C++】7.string

1.标准库的string类 string是表示字符串的字符串类在使用string类时&#xff0c;必须包含#include头文件以及using namespace std;string类是使用char(即作为它的字符类型&#xff0c;使用它的默认char_traits和分配器类型(关于模板的更多信息&#xff0c;请参阅basic_string)…

智能网联汽车安全芯片介绍(一)

汽车的新四化(电动化、网联化、智能化、共享化)让汽车安全越来越受到重视,比如一个不太容易被破解的汽车遥控钥匙或者非接触开门等,越智能越开始需要安全。而过去的一些安全事件也凸显了安全的必要性。 黑客早已经盯上了汽车。2015年,Charlie Miller 、 Chris Valsek曾通过…

熟悉mmdetection3d数据在模型中的处理流程

目录1、搭建模型2、读取数据3、运行流程3.1 图像特征获取3.2 点云特征获取3.3 head3.4 编码bbox4、可视化5、总结本图文数据集采取KITTI数据集配置文件的介绍可以参考博主上一篇图文本图文旨在利用一条数据&#xff0c;走完整个多模态数据处理分支&#xff0c;获得bbox&#xf…

Linux内核里的传输层数据流

传输层发送数据包socket sendto系统调用应用程序通过socket调用sendto系统调用发送数据包&#xff0c;根据网络层协议调用inet_sendmsg或者inet6_sendmsg()函数&#xff0c;将数据包送入协议栈发送。SYSCALL_DEFINE6(sendto...) - net/socket.csock_sendmsg() - net/socket.cso…

compose系列教程-2. 显示图片

要在Android中使用Compose显示图片&#xff0c;需要使用Image组件。以下是一个简单的例子&#xff0c;它显示了一张图片&#xff1a; Composable fun MyApp() { val image painterResource(id R.drawable.my_image) Image(painter image, contentDescription "…

dynamics 365的增删改查

今天需要完成对dynamics 365的增删改查&#xff0c;网上一直找不到合适的教程&#xff0c;官方文档看不懂&#xff0c;实在没办法了&#xff0c;于是下载了chatgpt,对他进行了提问。 我&#xff1a;怎么用visual studio基于dynamics 365进行增删改查&#xff1f; ChatGPT 中文…

Python笔记 -- 类

文章目录1、引入2、操作属性3、继承4、将实例用作属性5、导入类1、引入 类和实例 使用类可以模拟任何东西&#xff0c;下面是一个小狗的简单类Dog&#xff0c;它表示任意小狗&#xff0c;实例my_dog表示一个具体的小狗方法 类中的函数称为方法&#xff0c;有关函数的一切均适用…

兔c同学的一篇:使用python 的 unittest模块对类和函数进行测试

文章目录1. 测试函数简单的函数测试单元测试和测试用例可通过的测试不可通过的测试测试未通过时怎么办2. 测试类各种断言方法测试一个类测试 AnonymousSurvey方法setUp()导言 在编写函数或类时&#xff0c;还可为其编写测试。通过测试&#xff0c;可以确定代码面对各种输入都能…

面试官必问--谈谈Spring Bean对象的生命周期吧

现在是时候讨论Spring Bean从产生到销毁整个过程的细节了&#xff0c;也就是Spring Bean的生命周期。在这里文哥先温馨提示&#xff1a;Spring Bean的生命周期是面试高频点之一&#xff0c;希望大家好好掌握哦~一. Spring Bean生命周期的概述如果没有Spring的环境&#xff0c;J…

张力控制之开环模式

张力控制的相关知识也可以参看专栏的其它文章,链接如下: 张力闭环控制之传感器篇(精密调节气阀应用)_RXXW_Dor的博客-CSDN博客跳舞轮对应张力调节范围,我们可以通过改变气缸的气压方式间接改变,张力跳舞轮在收放卷闭环控制上的详细应用,可以参看下面的文章链接,这里我…

人工智能实验一:利用遗传算法求解 TSP(旅行商)问题

1.任务描述 本关任务&#xff1a;利用遗传算法求解 TSP 问题。 2.相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1. 遗传算法&#xff1b;2. TSP问题。 遗传算法 一个后继状态由两个父状态决定&#xff0c;以k个随机产生的状态开始&#xff08;population&…

Kaggle赛题解析:Diffusion Prompt生成

文章目录一、比赛信息二、比赛背景三、比赛任务四、评价指标五、数据描述六、解题思路一、比赛信息 比赛名称&#xff1a;Stable Diffusion - Image to Prompts 推断生成高度详细、清晰的焦点、插图、宏伟、史诗般的 3d 渲染图像的prompt 比赛链接&#xff1a;https://www.k…

python----获取一部小说

1、需求说明 获取一部小说的标题内容&#xff0c;以txt文档形式保存 2、项目说明 3、代码 # 怎么发送请求 # pip install requests import requests# pip install lxml->从标签里提起文字 #from lxml import etree from lxml import html etreehtml.etree # 发送给谁 url…

Android---系统启动流程

目录 Android 系统启动流程 init 进程分析 init.rc 解析 Zygote 概叙 Zygote 触发过程 Zygote 启动过程 什么时Runtime&#xff1f; System Server 启动流程 Fork 函数 总结 面试题 Android 是 google 公司开发的一款基于 Linux 的开源操作系统。 Android 系统启动…