【数据结构】—堆排序以及TOP-K问题究极详解(含C语言实现)

news2024/11/18 21:39:17

                                       食用指南:本文在有C基础的情况下食用更佳 

                                       🔥这就不得不推荐此专栏了:C语言

                                       ♈️今日夜电波:ルミネセンス—今泉愛夏

                                                                1:01 ━━━━━━️💟──────── 5:05
                                                                    🔄   ◀️   ⏸   ▶️    ☰ 

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍 


目录

☸️一、前置知识:两种调整方法

       向上调整方法   

       向下调整方法 

✡️二、堆排序

      堆排序的思想

      记住一个公式!(非常重要!!!)

      代码实现

🔯三、TOP-K问题

        什么是TOP-K问题?

        基本思路

        🌰


☸️一、前置知识:两种调整方法

         向上调整方法   

        堆的向上调整方法将新插入的节点从下往上逐层比较,如果当前节点比其父节点大(或小,根据是大根堆还是小根堆),则交换这两个节点。一直向上比较,直到不需要交换为止。这样可以保证堆的性质不变。

        具体步骤如下:

        1.将新插入的节点插入到堆的最后一位。

        2.获取该节点的父节点的位置,比较该节点与其父节点的大小关系。

        .如果该节点比其父节点大(或小,根据是大根堆还是小根堆),则交换这两个节点。

        4.重复步骤2-3,直到不需要交换为止,堆的向上调整完成。

        堆的向上调整的时间复杂度为O(logn),其中n为堆的大小。

        一图让你了解~(以大堆为例)

         实现如下: 

void swap(HPDataType* s1, HPDataType* s2)
{
	HPDataType temp = *s1;
	*s1 = *s2;
	*s2 = temp;
}

void Adjustup(HPDataType* a, int child)//向上调整
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])//建大堆,小堆则<
		{
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}

	}
}

       向下调整方法 

        堆的向下调整方法是指将某个节点的值下放至其子节点中,以维护堆的性质的过程。

        假设当前节点为 i,其左子节点为 2i+1,右子节点为 2i+2,堆的大小为 n

        则向下调整的步骤如下:

  1. 从当前节点 i 开始,将其与其左右子节点中较小或较大的节点比较,找出其中最小或最大的节点 j。

  2. 如果节点 i 小于等于(或大于等于,取决于是最小堆还是最大堆)节点 j,则说明它已经满足堆的性质,调整结束;否则,将节点 i 与节点 j 交换位置,并将当前节点 i 更新为 j。

  3. 重复执行步骤 1 和步骤 2,直到节点 i 没有子节点或已经满足堆的性质。

        一图让你了解~(以大堆为例) 

         实现如下:

void swap(HPDataType* s1, HPDataType* s2)
{
	HPDataType temp = *s1;
	*s1 = *s2;
	*s2 = temp;
}

void Adjustdown(HPDataType* a, int n, int parent)//向下调整
{
	int child = parent * 2 + 1;

	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])//找出两个孩子中较大的那个,此为大堆,如果要实现小堆则 改 >
		{
			++child;
		}

		if (a[child] > a[parent])//此为大堆,如果要实现小堆则 改 >
		{
			swap(&a[child], &a[parent]);

			parent = child;
			child = parent * 2 + 1;

		}
		else
		{
			break;
		}
	}
}


✡️二、堆排序

        堆排序的思想

        将待排序的序列构建成一个大根堆或小根堆,然后将堆顶元素与堆底元素交换,再重构堆,重复操作直到有序。堆排序的时间复杂度为O(nlogn),空间复杂度为O(1)。算是一种较为高效的排序方法。

         具体的实现步骤如下:

  1. 构建最大堆或最小堆。(建大堆排升序,建小堆排降序

  2. 将堆顶元素(最大或最小值)与堆底元素交换。

  3. 从堆顶开始逐级向下调整堆,保证每个节点都符合堆的性质。

  4. 重复步骤2和步骤3,直到整个序列有序。

        通常而言我们用的都是向下调整法来建堆以及排序,为什么呢?

        向下调整法具有较好的时间复杂度:与向上调整法相比,向下调整法的时间复杂度更低,因为向下调整法只需要考虑每个非叶子节点的子树是否满足堆性质,而向上调整法需要考虑每个节点到根节点是否满足堆性质,时间复杂度较高。

       记住一个公式!(非常重要!!!)

                parent=(n - 1 - 1) / 2

        这个公式是用来干什么的呢?用来找第一个有叶子节点的父节点的!

        一图让你了解~

       你可能有一个疑惑,我们这样建堆的意义是什么?答案是我们要将所有节点的左子树以及右子树都建成一个我们需要的堆建大堆排升序,建小堆排降序)。这样做的意义是:让堆顶的元素在同最后一个堆的元素进行调换位置后,能够仅仅通过一次向下调整,(以大堆为例)就能让堆的最大元素排到队尾并且不打乱顺序!!!

        在理解了怎么建堆后,对于排序这件事实际上已经很简单了!

        一图让你了解~

      代码实现

void HeapSort(int* a, int n)//整体时间复杂度为nlog(n)
{
	//建大堆排升序,建小堆排降序

	//用的都是向下调整法来建堆以及排序

	//这里演示升序,如果要降序则修改向下调整法中的 > 变为 < ,使得建立的为小堆,并且后面的排序也将为降序!
	//建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)//注意这里的i表示为第一个有叶子结点的父节点
	{
		Adjustdown(a, n, i);
	}

	//排序
	int end = n - 1;
	while (end > 0)
	{
		swap(&a[0], &a[end]);
		Adjustdown(a, end, 0);
		--end;
	}

}


🔯三、TOP-K问题

        什么是TOP-K问题?

        TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

        对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决。

        基本思路

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

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

        🌰

        在随机的10000000个数据中找出前5大的数据。(通过文件建立以及读取实现)

         该🌰的堆实现在这篇博文中:堆详解(点我跳转!!!)

         实现如下:

void PrintTopK(const char* filename, int k)
{
	// 1. 建堆--用a中前k个元素建堆
	FILE* fout = fopen(filename, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc fail");
		return;
	}

	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
	}

	// 前k个数建小堆
	for (int i = (k-2)/2; i >=0 ; --i)
	{
		AdjustDown(minheap, k, i);
	}


	// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minheap[0])
		{
			// 替换你进堆
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}


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

	free(minheap);
	fclose(fout);
}

// fprintf  fscanf

void CreateNDate()
{
	// 造数据
	int n = 10000000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; ++i)
	{
		int x = (rand() + i) % 10000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

int main()
{
	//CreateNDate();
	PrintTopK("data.txt", 5);

	return 0;
}


                    感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                         给个三连再走嘛~  

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

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

相关文章

[刷题记录]牛客面试笔刷TOP101(一)

牛客笔试算法必刷TOP101系列,每日更新中~(主要是记录自己的刷题,所以描述的可能不是很清楚 但如果刚好能帮助到你就更好了) 后续后头复习的时候,记得是看正解啊,别对着错的例子傻傻看了... 目录 1.合并有序链表2023.9.3 2.链表是否有环2023.9.4 3.判断链表中环的入口点 …

学Python的漫画漫步进阶 -- 第三步

学Python的漫画漫步进阶 -- 第三步 三、数字类型的数据3.1 Python中的数据类型3.2 整数类型3.3 浮点类型3.4 复数类型3.5 布尔类型3.6 数字类型的相互转换3.6.1 隐式类型的转换3.6.2 显式类型的转换 3.7 练一练3.8 数字类型的总结全部16步完成后 &#xff0c;后续就是介绍项目实…

走进甄云,探寻SRM独角兽成功背后的故事

随着科技的快速发展和全球商业环境的不断变化&#xff0c;中国企业对灵活性、创新性、全球化和效率的需求是迫切的&#xff0c;数字化转型已经成为企业生存和发展的关键因素&#xff0c;对企业具有重要意义&#xff0c;是组织生存和发展的必然趋势。数字化转型涉及整个组织、多…

PMP-项目规划过程组的重要性

一、什么是项目规划过程组 规划过程组包括明确项目全部范围、定义和优化目标&#xff0c;并为实现目标制定行动方案的一组过程。规划过程组中的过程制定项目管理计划的组成部分&#xff0c;以及用于执行项目的项目文件。取决于项目本身的性质&#xff0c;可能需要通过多轮反馈来…

片上网络(1)概述

前言 NoC&#xff1a;On-Chip Networks&#xff0c;片上网络。 由于多核乃至众核时代的到来&#xff0c;用于连接它们的可扩展、低延迟、大带宽的通信结构变得至关重要。 在核心较少时&#xff0c;总线Bus和矩阵/交叉开关Crossbar是主要的互联结构。总线可以提供较低的传输延迟…

云原生Kubernetes:pod基础与配置

目录 一、理论 1.pod 2.pod容器分类 3.镜像拉取策略 4.pod 的重启策略 二、实验 1.Pod容器的分类 2.镜像拉取策略 三、问题 1.apiVersion 报错 2.pod v1版本资源未注册 3.格式错误 4.取行显示指定pod信息 四、总结 一、理论 1.pod (1) 概念 Pod是kubernetes中…

pgzrun 拼图游戏制作过程详解(4,5)

4. 将小拼图位置随机打乱 建立swap_Square(i&#xff0c;j)坐标互换函数 将Gird[i]和Gird[j] 中的小拼图信息进行互换 def swap_Square(i,j): # 两个拼图的位置互换temp_posGird[i].posGird[i].posGird[j].posGird[j].postemp_pos 导入随机数模块 import random 随机抽取…

ruoyi-nbcio移植过程中的一些问题记录

1、打包去掉测试出现 Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test 错误 在pom.xml里增加下面 去掉测试 <!--添加配置跳过测试--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId…

2023.05.27系统分析师考试案例分析及解析

案例分析真题1 阅读以下关于软件系统分析与建模的叙述&#xff0c;在纸上回答问题1至3. 说明: 某软件公司拟开发一套汽车租赁系统&#xff0c;科学安全和方便的管理租赁公司的各项业务&#xff0c;提高公司效率&#xff0c;提升利率。注册用户在使用系统镜像车辆预约时需执行…

利用Windows搭建Emby媒体库服务器,轻松实现无公网IP的远程访问

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

css+js:实现tab切换线条跟随效果

目录 实现效果原理解析代码实现 实现效果 使用css和js实现一个tab切换的效果 原理解析 如上图 红色框框代表盒子,总宽度记作 totalWidth绿色框框代表每一项,宽度记作itemWidth深蓝色框框代表的是下划线,宽度记作activeWitdh 那么我们可以得到线的左边距是每一项的左边距加上…

通讯网关软件002——利用CommGate X2HTTP-U实现HTTP访问OPC UA Server

本文介绍利用CommGate X2HTTP-U实现HTTP访问OPC UA Server。CommGate X2HTTP是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现上位机通过HTTP来获取OPC UA Server的数据。 【解决方案】设置网关机…

c++ 中的函数指针

以下图片演示了c中函数指针的用法。如下图可见&#xff0c;把函数地址赋值给函数指针&#xff0c;用函数名或者函数名的地址&#xff0c;都可以&#xff0c;c编译器不报错。即 ptr f 和 ptr &f 都对。但准确的话&#xff0c;函数名就是地址&#xff0c;在编译时候&#x…

19 视图定义 union 是根据第一个 select 字段列表顺序,来进行 merge 的

前言 这个问题主要是 在之前存在这样的一个问题, 在生产环境上面 按照 我的直观理解, mysql 应该是根据 key 进行 merge, 所以 select 的顺序应该是 “不重要”??, 但是 结果我理解错了 然后 线上的查询也出现了问题, 发现很奇怪的问题, 明明 key01 列 是 id, 但是有一部…

【Linux问题】This account is currently not available.

在切换mysql用户时候出现下面问题 Last login: Fri Sep 15 09:32:46 CST 2023 on pts/0 This account is currently not available.查看 mysql用户信息 cat /etc/passwd | grep mysql他是 mysql:x:27:27:MySQL Server:/var/lib/mysql:/sbin/nologin把 /sbin /nologin 改成 /b…

使用阿里PAI DSW部署Stable Diffusion WebUI

进入到网址https://pai.console.aliyun.com/里边。 点击创建实例。 把实例名称填写好&#xff0c;选择GPU规格&#xff0c;然后选择实例名称是ecs.gn6v-c8g1.2xlarge。 选择stable-diffusion-webui-env:pytorch1.13-gpu-py310-cu117-ubuntu22.04&#xff0c;然后点击下一步。…

云原生Kubernetes:K8S集群使用带凭证的harbor仓库

目录 一、理论 1.部署harbor 创建私有项目&#xff0c;使用凭证登录 二、实验 1.部署harbor 创建私有项目&#xff0c;使用凭证登录 三、问题 1.harbor页面无法打开 2.生成harbor 登录凭据资源清单报错 3.tomcat-deployment 资源生成报错 一、理论 1.部署harbor 创建私…

CSV文件打开乱码解决方案

思路 借助第三方软件转码即可。 案例 打开CSV文件显示如下&#xff1a; 导入到python中&#xff0c;借助pandas文件读取 另存为csv文件格式即可 代码如下&#xff1a; import pandas as pd df pd.read_csv(rD:\BaiduNetdiskDownload\csv&xlsx版数据\城市产业创新指数…

人工智能现在可以从文本中生成具有CD音质的音乐,而且只会越来越好

想象一下&#xff0c;键入“戏剧性的介绍音乐”并听到一首飙升的交响乐&#xff0c;或者编写“令人毛骨悚然的脚步声”并获得高质量的音效。这是稳定音频的承诺&#xff0c;一个文本到音频的人工智能模型周三宣布由能合成立体声的稳定人工智能44.1千赫来自文字描述的音乐或声音…

antv-G6知识图谱安装--使用(实例)--连接线修改成动态,并添加跟随线移动的光圈,设置分支跟踪定位功能

这系列文章主要是完成一个图谱的自定义修改&#xff08;最近太忙了长篇分段更新自己使用流程&#xff09; 1. 连接线修改成动态&#xff0c;并添加跟随线移动的光圈 2. 自定义卡片样式和文字内容 3. 自定义伸缩节点的样式&#xff0c;并添加动画样式 3. 自定义弹窗样式 4. 自定…