数据结构与算法:堆排序和TOP-K问题

news2024/9/29 11:37:05

朋友们大家好,本节内容来到堆的应用:堆排序和topk问题

堆排序

  • 1.堆排序的实现
    • 1.1排序
  • 2.TOP-K问题
  • 3.向上调整建堆与向下调整建堆
    • 3.1对比两种方法的时间复杂度

我们在c语言中已经见到过几种排序,冒泡排序,快速排序(qsort)

冒泡排序的时间复杂度为O(N2),空间复杂度为O(1);qsort排序的时间复杂度为
O(nlogn),空间复杂度为O(logn),而今天所讲到的堆排序在时间与空间复杂度上相比于前两种均有优势

堆排序可以在原数组上进行,其空间复杂度为O(1);
堆排序提供了稳定的 (O(nlogn)) 时间复杂度

接下来我们进行讲解

首先我们来看这组代码:

int main()
{
	int a[] = { 6,3,5,7,11,4,9,13,1,8,15 };
	Heap hp;
	HeapInit(&hp);
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		HeapPush(&hp, a[i]);
	}
	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	printf("\n");

	return 0;

}

上节课我们知道,hp这个堆里面,a[i]并不一定是有序的

在这里插入图片描述
这里我们每次打印首元素,即最小元素,再删除掉,下一次获得到的堆顶元素仍为最小的,所以打印出来结果为有序的。但这个并不是堆排序,他只是每次获取堆顶最小元素

堆排序是直接在数组上实现的

1.堆排序的实现

堆排序的实现可以分为两部分:构建最大堆(或最小堆)和执行排序过程

首先我们来看建堆过程

在上述代码中,我们是通过HeapPush(&hp, a[i]);来实现堆的插入,推其本质,是每次插入元素后进行向上调整,我们构建一个堆排序函数,其参数为传入的数组,和数组的元素个数:

void HeapSort(HPDataType* a, int n);

首先建堆,这里我们用向上调整建堆,在文章末尾会给大家引入向下调整建堆

for (int i = 1; i < n; i++)
{
	Ajustup(a, i);
}

从第二个元素开始,每次向上调整,完成堆的构建
在这里插入图片描述

建好之后我们则需要排序

1.1排序

思考一下,如果我们想要进行升序排序,需要建立大堆还是小堆呢?

在上述示例中,如果我们想进行升序,该怎么操作???

这里,如果我们想要升序排序,则需要建立大堆

小堆如果我们想要升序,堆顶元素在对应位置,剩余元素重新建立小堆,则时间复杂度大大增加

上述示例中,我们建了一个小堆,可以将Ajustup父节点与子节点大小关系改变来建立为大堆:

在这里插入图片描述
那思考一下,建立了大堆,我们如何实现升序呢?

这里我们就需要与删除堆顶元素相同的思路

  1. 排序过程
    在大堆构建完成后,数组的根节点(即数组的第一个元素)是当前堆中的最大元素。通过将它与堆的最后一个元素交换,然后减少堆的大小(实际上是忽略数组的末尾元素),可以确保最大元素位于数组的正确位置上。

  2. 调整堆
    交换根节点和最后一个节点之后,新的根节点可能破坏了大堆的性质,因此需要进行调整。调整的方法是将新的根节点“下沉”,直到恢复大堆的性质。

  3. 重复过程
    重复对堆顶元素进行移除并调整堆的过程,直到堆的大小减少到1。在每一次重复过程中,都会将当前的最大元素放置到它在数组中的最终位置上。

所以我们代码实现就两步:

  • 交换首尾元素
  • 向下调整
void HeapSort(HPDataType* a, int n)
{
	//建堆
	for (int i = 1; i < n; i++)
	{
		Ajustup(a, i);
	}
	while (n>1)
	{
		Swap(&a[0], &a[n - 1]);
		n--;
		Ajustdown(a, n, 0);
	}
}

我们进行代码测试
在这里插入图片描述

所以,堆这里可以促进我们快速选数,它的本质是选择排序

2.TOP-K问题

TOP-K问题指的是从一个大规模的数据集中找出“最重要”或“最优”的K个元素的问题,对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决

思路如下:

  1. 用数据集合中前K个元素来建堆
    • 前k个最大的元素,则建小堆
    • 前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

基于已经提供的堆操作函数,我们可以实现一个特定的PrintTopK函数,其目的是从数组a中找到最大的k个元素。

实现这一目标的思路是:

  • 首先,使用数组a中的前k个元素建立一个最小堆。
  • 然后,遍历剩余的n-k个元素。对于每个元素,如果它大于堆顶元素,则用它替换堆顶元素,然后对堆顶元素进行向下调整以维护最小堆的性质。
  • 遍历完成后,堆中的k个元素即为整个数组中最大的k个元素。
void PrintTopK(int* a, int n, int k)
{
    Heap php;
    HeapInit(&php);
    
    for (int i = 0; i < k; ++i) {
        HeapPush(&php, a[i]);
    }
    for (int i = k; i < n; ++i) {
        if (a[i] > HeapTop(&php)) { // 如果当前元素比堆顶大
            HeapPop(&php); // 移除堆顶
            HeapPush(&php, a[i]); // 将当前元素加入堆中
        }
    }
    
    // 打印堆中的元素,即TOP K元素
    for (int i = 0; i < k; ++i) {
        printf("%d ", php.a[i]);
    }
    printf("\n");
    HeapDestroy(&php);
}

  1. 用a中前k个元素建立堆
  2. 将剩余n-k个元素与堆顶比较,替换并调整

测试代码:
在这里插入图片描述

3.向上调整建堆与向下调整建堆

对于数组a,进行向上调整建堆:

for (int i = 1; i < n; i++)
{
	Ajustup(a, i);
}

要通过向下调整的方式建立堆,我们通常是从最后一个非叶子节点开始,逐层向上进行调整,这能保证每个子树都满足堆的性质

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

3.1对比两种方法的时间复杂度

向下调整建堆

这个方法从最后一个非叶子节点开始,逆序对数组中的元素执行向下调整的操作。每个节点需要执行的向下调整操作取决于其高度,而数组中大约一半的节点是叶子节点,它们不需要被向下调整。对于剩下的节点,只有很少的节点需要移动到树的较低层次。具体地说,树的每一层上的节点数量减半,而向下移动的最大深度从0开始线性增加。

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

在这里插入图片描述
设向下调整的累计次数为T(h).

  • 倒数第二层调整次数:2h-2*1
  • 倒数第三层调整次数:2h-3*2
  • ……
  • 第一层调整次数:20*(h-1);

对其进行累加和:

为等差×等比求和,通过错位相减则可求出结果:

T(h)=2^h-1-h;
h=log (n+1);
T(n)=n-log(n+1)

导致最大影响的项为n
所以向下调整的时间复杂度为O(N)

向上调整建堆

从第二层开始向上调整:

  • 第二层调整次数:21*1
  • 第三层调整次数:22*2;
  • 倒数第二层:2h-2*(h-2);
  • 倒数第一层:2h-1*(h-1);

向上调整建堆

对于一个节点来说,向上调整可能需要比较和移动直到它的根节点,这在最坏的情况下是树的高度,对于一个完全二叉树来说,树的高度是 O ( log ⁡ n ) O(\log n) O(logn)。对于代码段:

for (int i = 1; i < n; i++) {
    AdjustUp(a, i);
}

这个方法从第二个元素开始,逐一对数组中的元素执行向上调整的操作。对于数组中的第i个元素,最坏情况下向上调整操作需要沿着一条从叶节点到根节点的路径移动,路径的长度大约等于树的高度 h h h,即 O ( log ⁡ i ) O(\log i) O(logi)。因此,对于所有元素的总时间复杂度为:

T ( n ) = ∑ i = 1 n O ( log ⁡ i ) = O ( log ⁡ n ! ) = O ( n log ⁡ n ) T(n) = \sum_{i=1}^{n} O(\log i) = O(\log n!) = O(n \log n) T(n)=i=1nO(logi)=O(logn!)=O(nlogn)

使用斯特灵公式( n ! ≈ 2 π n ( n e ) n n! \approx \sqrt{2\pi n}(\frac{n}{e})^n n!2πn (en)n),可以推导出 O ( log ⁡ n ! ) O(\log n!) O(logn!) 的大致等于 O ( n log ⁡ n ) O(n \log n) O(nlogn),所以向上调整建堆的时间复杂度大约为 O ( n log ⁡ n ) O(n \log n) O(nlogn)

向上调整建堆的时间复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn),而向下调整建堆的时间复杂度是 O ( n ) O(n) O(n)。因此,对于从零开始构建堆的场景,通常更倾向于使用向下调整的方法,因为它更加高效。

本节内容到此结束!感谢大家支持!

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

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

相关文章

光伏发电预测

XGB、LGB在datacamp(学习网站) data fountain与国家电投系列赛,光伏发电预测 题目:给一组特征,预测瞬时发电量,训练集9000个点,测试集8000个点,特征包含光伏板的属性和外部环境等。 数据字段:ID、光伏电池板背侧温度、光伏电站现场温度、计算得到的平均转换效率、数…

Javaweb之SpringBootWeb案例之自动配置案例的自定义starter分析的详细解析

3.2.4.1 自定义starter分析 前面我们解析了SpringBoot中自动配置的原理&#xff0c;下面我们就通过一个自定义starter案例来加深大家对于自动配置原理的理解。首先介绍一下自定义starter的业务场景&#xff0c;再来分析一下具体的操作步骤。 所谓starter指的就是SpringBoot当…

数据删除

目录 数据删除 删除员工编号为 7369 的员工信息 删除若干个数据 删除公司中工资最高的员工 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 数据删除 删除数据就是指删除不再需要的数据 delete from 表名称 [where 删…

HTML+CSS+BootStrap景区官网

一、技术栈 支持pc、pad、手机访问&#xff0c;页面自适应&#xff01;&#xff01; html5cssbootstrapjs 二、项目截图 接受项目定制&#xff0c;站内联系博主&#xff01;&#xff01;&#xff01;

算法学习03:前缀和与差分(互逆)

算法学习03&#xff1a;前缀和与差分&#xff08;互逆&#xff09; 文章目录 算法学习03&#xff1a;前缀和与差分&#xff08;互逆&#xff09;前言一、前缀和1.一维2.二维 二、差分1.一维在这里插入图片描述2.二维在这里插入图片描述 ![在这里插入图片描述](https://img-blog…

CSS全局样式的设置,web开发交流

面试题 HTML 1&#xff0c;html5有哪些新特性&#xff1f; 2&#xff0c;html5移除了那些元素&#xff1f; 3&#xff0c;如何处理HTML5新标签的浏览器兼容问题 戳这里领取完整开源项目&#xff1a;【一线大厂前端面试题解析核心总结学习笔记Web真实项目实战最新讲解视频】…

Vue+腾讯地图-实现关键词输入提示功能

不废话&#xff0c;上代码~~~ 效果图&#xff1a; 1、先去腾讯地图后台创建自己的应用获取到应用的 Key 腾讯地图后台地址&#xff1a;腾讯位置服务 - 立足生态&#xff0c;连接未来 创建应用的 Key 如下&#xff1a; 2、在项目中添加腾讯地图API的js插件&#xff0c;如…

Android开发者该学习哪些东西提高竞争力,Github上最值得学习的10个Android开源项目

什么是 HTTPS? HTTPS (基于安全套接字层的超文本传输协议 或者是 HTTP over SSL) 是一个 Netscape 开发的 Web 协议。 你也可以说&#xff1a;HTTPS HTTP SSL HTTPS 在 HTTP 应用层的基础上使用安全套接字层作为子层。 为什么需要 HTTPS &#xff1f; 超文本传输协议 (…

2024第十二届济南国际生物发酵产品与技术装备展览会胜利开幕

聚焦生物新产品新技术 引领生物产业发展新趋势 3月5日&#xff0c;2024第十二届济南国际生物发酵产品与技术装备展览会在济南市山东国际会展中心隆重举行。这次展览会&#xff0c;由中国生物发酵产业协会主办&#xff0c;山东省生物发酵产业协会协办&#xff0c;上海信世展览…

通过人工智能增强的对话建立有意义的联系

人工智能如何重塑我们的交流&#xff1f;2024年最新对话AI趋势 在技术和人类互动比以往任何时候都更加复杂地交织在一起的时代&#xff0c;人工智能增强的对话已成为建立有意义的联系的关键要素。 这种转变不仅关乎效率&#xff0c;还关乎效率。 这是为了丰富沟通的结构。 在这…

关于我使用numpy.random.choice()遇到坑这件事

做仿真时经常使用到随机数&#xff0c;下面是一个场景&#xff1a;使用np.random.choice([0,1],p[0.5,0.5],size1)去进行随机的二选一&#xff0c;假设需要随机选择1000次&#xff0c;为了保证结果的稳健性&#xff0c;对前述过程重复50次&#xff0c;为了保证可复现性&#xf…

YOLOv9推理详解及部署实现

目录 前言零、YOLOv9简介一、YOLOv9推理(Python)1. YOLOv9预测2. YOLOv9预处理3. YOLOv9后处理4. YOLOv9推理 二、YOLOv9推理(C)1. ONNX导出2. YOLOv9预处理3. YOLOv9后处理4. YOLOv9推理 三、YOLOv9部署1. 源码下载2. 环境配置2.1 配置CMakeLists.txt2.2 配置Makefile 3. ONNX…

DHCP自动获取IP地址实验(思科)

华为设备参考&#xff1a;DHCP自动获取IP地址实验&#xff08;华为&#xff09; 一&#xff0c;实验目的 路由器搭载DHCP&#xff0c;让PC通过DHCP自动获取IP地址 二&#xff0c;不划分vlan 实验拓扑 配置命令 Switch Switch>enable Switch#configure terminal Switch(c…

关于vue创建项目以及关于eslint报错的问题

vue创建完项目以后如果报parsing error no babel config file。。。这样的错误的话&#xff0c;关闭项目&#xff0c;用vscode进入项目中打开项目就可以解决了。 1 代码保存的时候会自动将单引号报错为双引号 导致eslint报错的问题&#xff0c; 解决思路&#xff1a; 在项目根…

Homomorphic CNNs

参考文献&#xff1a; [GDL16] Gilad-Bachrach R, Dowlin N, Laine K, et al. Cryptonets: Applying neural networks to encrypted data with high throughput and accuracy[C]//International conference on machine learning. PMLR, 2016: 201-210.[HTG17] Hesamifard E, T…

基于java的宠物常规护理知识管理系统

项目源码&#xff1a;https://gitee.com/oklongmm/biye2 在设计一个宠物常规护理知识管理系统时&#xff0c;我们需要考虑系统的可扩展性&#xff0c;易用性和稳定性。以下是系统设计的功能模块&#xff1a; 一、用户模块&#xff1a; 1. 注册与登录&#xff1a;用户可以通过…

Day18:信息打点-小程序应用解包反编译动态调试抓包静态分析源码架构

目录 小程序获取-各大平台&关键字搜索 小程序体验-凡科建站&模版测试上线 小程序抓包-Proxifier&BurpSuite联动 小程序逆向-解包反编译&动态调试&架构 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系…

Python成功解决AttributeError: ‘Series‘ object has no attribute ‘set_value‘

Python成功解决AttributeError: ‘Series‘ object has no attribute ‘set_value‘ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&am…

[机器视觉]halcon应用实例 用户自定义多ROI模板匹配

本示在前面几个halcon ROI示例的基础上继续扩展&#xff0c;更靠进实标情况。为了使ROI匹配更灵活&#xff0c;就要求可以让用户或工程根据实际使用情况自己去画ROI&#xff0c;想画几个是几个。数量不能在代码里写死。 这次升级的主要是增加了一个while循环根据用户的鼠标按键…

入门LLMs开发 — LangChain

像OpenAI的GPT-4这样的大型语言模型&#xff08;LLMs&#xff09;已经风靡全球。它们可以自动执行各种任务&#xff0c;如回答问题、翻译语言、分析文本等。LLMs是第一种真正感觉像“人工智能”的机器学习类型。 然而&#xff0c;在将LLMs应用于实际产品时仍然存在挑战。特别是…