拿捏数据结构-top_k问题

top_k问题时间复杂度的计算

这里提前说明,时间复杂度的计算的目的是来计算向上调整的更优还是向下调整更优,从肉眼看的话向下调整优于向上调整,接下来我们进行时间复杂度的计算。

此时我们会用到等比数列求和以及裂项相消

如图

首先我们假设求的是满二叉树,我们求节点的个数

满二叉树节点个数

建堆问题:

建堆的话往往的倒数第一个非叶子结点建堆,会时间复杂度最优解:也就是

在构建堆(尤其是二叉堆)时,从最后一个非叶子节点开始进行调整是时间复杂度最优解的原因是,这种方法可以减少不必要的调整操作。

为什么从最后一个非叶子节点开始?

  1. 叶子节点:在完全二叉树中,叶子节点不包含任何子节点,因此不需要进行调整。

  2. 非叶子节点:从最后一个非叶子节点开始,向上逐个进行调整,可以确保每个节点在调整时,其子树已经是堆结构。这样可以减少调整的深度,因为每个节点最多只需要与其子节点交换一次。

  3. 减少调整次数:如果从根节点开始调整,那么每个节点可能需要多次调整才能达到堆的性质,特别是那些位于树底部的节点。而从底部开始,每个节点只需要调整一次即可。

时间复杂度分析

构建堆的过程涉及对每个非叶子节点进行调整。对于一个具有 𝑛n 个节点的完全二叉树:

  • 叶子节点:有 ⌈𝑛/2⌉⌈n/2⌉ 个叶子节点,它们不需要调整。

  • 非叶子节点:有 ⌊𝑛/2⌋⌊n/2⌋ 个非叶子节点,需要进行调整。

对于非叶子节点,从最后一个非叶子节点开始向上调整,每个节点最多只需要进行 log⁡𝑘logk(𝑘k 是节点的深度)次交换。但是,由于树的结构,底部的节点不需要进行多次交换,因此整个调整过程的时间复杂度比 𝑂(𝑛log⁡𝑛)O(nlogn) 要低。

实际上,构建堆的时间复杂度是 𝑂(𝑛)O(n),这是因为:

  • 从最后一个非叶子节点开始,每个节点的调整次数与其深度成反比。

  • 根节点的调整次数最多,但只需要一次。

  • 越往下,节点的深度越小,但需要调整的节点数量越多。

总结

从最后一个非叶子节点开始建堆,可以确保每个节点的调整次数与其深度成反比,从而减少总的调整次数。这种方法利用了完全二叉树的性质,使得整个建堆过程的时间复杂度达到最优,即 𝑂(𝑛)O(n)。这是构建堆的最优策略,因为它最小化了必要的调整操作,从而提高了算法的效率。

建堆复杂度讲解:(向下调整建堆计算)

如图:

这里为什么-2呢,因为我们的向下调整只是调整h-1层,第h层的节点的个数是2^h-1,所以第h-1层自然就是-2

所以我们发现,建堆的时候我们h-1高度的节点的个数相加得出的结果

为T(n)

所以我们进行计算

从而得出时间复杂度,为什么时间复杂度是高度,因为向下调整的时候,我们循环终止条件是循环的高度,也就是当父亲节点不小于sz的时候,所以计算出高度也就计算出了时间复杂度

建堆复杂度讲解:(向上调整建堆计算) 

如图:

计算图解

所以我们得出结论,这里多了n次

对比

向上调整(AdjustUp)和向下调整(AdjustDown)的时间复杂度通常与堆的高度相关,即 log⁡𝑘logk,其中 𝑘k 是堆中元素的数量。然而,在特定情况下,特别是在构建堆的过程中,这些操作的总时间复杂度可以是 𝑂(𝑛)O(n),这里的 𝑛n 是堆中元素的数量。

单个操作的时间复杂度:

  • 向上调整 (AdjustUp):对于单个元素,向上调整的时间复杂度是 𝑂(log⁡𝑘)O(logk),因为它可能需要从叶子节点一直调整到根节点,最多涉及 log⁡𝑘logk 层的比较和交换。

  • 向下调整 (AdjustDown):同样,对于单个元素,向下调整的时间复杂度也是 𝑂(log⁡𝑘)O(logk),因为它可能需要从根节点调整到叶子节点,同样最多涉及 log⁡𝑘logk 层的比较和交换。

构建堆的总时间复杂度:

  • 当我们讨论构建一个包含 𝑛n 个元素的堆时,所有元素的向上调整操作的总时间复杂度是 𝑂(𝑛)O(n)。这是因为:

    • 树的非叶子节点大约是 𝑛/2n/2(因为叶子节点也是 𝑛/2n/2 左右)。

    • 每个非叶子节点的调整操作最多涉及 log⁡𝑘logk 的时间,但是由于树的结构,从根到叶的路径上的节点数量总和大致是 𝑛n。

    • 因此,所有节点的向上调整操作加起来的时间复杂度是 𝑂(𝑛)O(n)。

为什么是 𝑂(𝑛)O(n) 而不是 𝑂(𝑛log⁡𝑘)O(nlogk)?

  • 树的结构特性:在完全二叉树中,每个层级的节点数量是指数增长的。从根节点(1个节点)到第二层(2个节点),再到第三层(4个节点),等等。因此,较低层级的节点数量远多于较高层级的节点数量。

  • 调整深度:根节点的调整可能需要 log⁡𝑘logk 的时间,但较低层级的节点只需要较少的调整时间。由于底部层级的节点数量较多,它们较短的调整时间在总体上对总时间复杂度的贡献较小。

总结:

  • 对于单个元素,向上调整和向下调整的时间复杂度是 𝑂(log⁡𝑘)O(logk)。

  • 在构建堆的过程中,所有元素的向上调整操作的总时间复杂度是 𝑂(𝑛)O(n),而不是 𝑂(𝑛log⁡𝑘)O(nlogk),这是由于完全二叉树的结构特性和调整操作的分布。

因此,向上调整和向下调整在构建堆的过程中的总时间复杂度是 𝑂(𝑛)O(n),而不是 𝑂(log⁡𝑛)O(logn)。这个线性时间复杂度是构建堆算法的一个重要特性,使得它在处理大量数据时非常高效。

向上调整和向下调整虽然最后计算的都是O(N)

但是满二叉树最后一层占据一半的节点

所以我们得出结论,向下调整的复杂度优于向上调整的复杂度

top_k问题的实现逻辑

1,首先我们创建一个文件,写入随机数值1000w个

2,如果需要读取文件里面最大的10个数值,那么我们就需要,创建一个小堆

原因:

这样的话,输入数值的时候,如果读取的数值比堆顶大,就会替换堆顶从而进堆,然后进行堆排序。

3,在读取文件的时候,我们需要读取一个接收一个,然后进行数值的对比,从而进行交换。

4,最后打印最大的数值

5,备注:我们如何判断我们的找到的最大的前十个数值的正确的,

也是很简单的,我们设定的随机数值是10000以内的,然后设定完之后,我们不调用,进入TXT里面更改一些数值。设定一些大于一万的数值,此时我们就可以发现我们筛选的数值对不对。

当然如果我们需要找最小的数值,那么我们设定数值最好为-1,因为十万个数值,很可能是有很多0的。但是我们肉眼看不出来。

top_k计算的代码实现

//进行计算
void TOP_K()
{
	int k = 10;
	//scanf("%d", &k);
	FILE* ps = fopen("data.txt", "r");
	if (ps == NULL)
	{
		perror("Error:opening:file");
		exit(1);
	}
	//创建空间存储
	int* tmp = (int*)malloc(sizeof(int) * k);
	if (tmp == NULL)
	{
		perror("TOP_K():Heap* tmp:error");
		exit(1);
	}
	//读取个数
	for (int i = 0; i < 10; i++)
	{
		fscanf(ps, "%d", &tmp[i]);
	}
	// 建堆,从最后一个非叶子节点开始建堆,
	// 这里的 -1-1 实际上看起来像是一个错误。
	// 通常,当我们需要找到最后一个非叶子节点的索引以开始建堆过程时,我们会从倒数第二个节点开始(因为数组索引从0开始)。对于大小为 k 的数组,最后一个非叶子节点的索引计算如下:
	// 简单的说就是,k是数值,我们需要传参传递是下标,找到父亲节点需要减去1 除以2 所以就有了-2的情况
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(tmp, k, i);
	}
	//排序
	int val = 0;
	int ret = fscanf(ps, "%d", &val);
	while (ret != EOF)
	{
		if (tmp[0] < val)
		{
			tmp[0] = val;
			AdjustDown(tmp, k, 0);
		}
		ret = fscanf(ps, "%d", &val);
	}

	//打印
	for (int i = 0; i < k; i++)
	{
		printf("%d ", tmp[i]);
	}
	fclose(ps);

}

top_k完整代码

//TOP_K问题的实现 小堆寻找最大值
//创建随机数值
void TOP_K_fopen_w()
{
	FILE* ps = fopen("data.txt", "w");
	if (ps == NULL)
	{
		perror("FILE* ps :fopen:error");
		exit(1);
	}
	srand(time(0));
	for (int i = 0; i < 100000; i++)
	{
		int s = rand() % 10000;
		fprintf(ps, "%d\n", s);
	}

	fclose(ps);
}
//进行计算
void TOP_K()
{
	int k = 10;
	//scanf("%d", &k);
	FILE* ps = fopen("data.txt", "r");
	if (ps == NULL)
	{
		perror("Error:opening:file");
		exit(1);
	}
	//创建空间存储
	int* tmp = (int*)malloc(sizeof(int) * k);
	if (tmp == NULL)
	{
		perror("TOP_K():Heap* tmp:error");
		exit(1);
	}
	//读取个数
	for (int i = 0; i < 10; i++)
	{
		fscanf(ps, "%d", &tmp[i]);
	}
	// 建堆,从最后一个非叶子节点开始建堆,
	// 这里的 -1-1 实际上看起来像是一个错误。
	// 通常,当我们需要找到最后一个非叶子节点的索引以开始建堆过程时,我们会从倒数第二个节点开始(因为数组索引从0开始)。对于大小为 k 的数组,最后一个非叶子节点的索引计算如下:
	// 简单的说就是,k是数值,我们需要传参传递是下标,找到父亲节点需要减去1 除以2 所以就有了-2的情况
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(tmp, k, i);
	}
	//排序
	int val = 0;
	int ret = fscanf(ps, "%d", &val);
	while (ret != EOF)
	{
		if (tmp[0] < val)
		{
			tmp[0] = val;
			AdjustDown(tmp, k, 0);
		}
		ret = fscanf(ps, "%d", &val);
	}

	//打印
	for (int i = 0; i < k; i++)
	{
		printf("%d ", tmp[i]);
	}
	fclose(ps);

}

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

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

相关文章

Github Actions/workflow的使用

背景 Github提供了免费的Actions执行workflows工作流&#xff0c;在CI/CD场景下可用于跑测试用例、构建、打包、部署/发版等操作。 使用介绍 工作流简介 1个project可以配置多个workflow&#xff0c;每个workflow使用一个yaml文件配置&#xff1b;单个workflow可以配置多个…

身份认证页面该怎么设计更加合理?

一、认证页面的作用 认证页面在应用程序中具有以下几个重要的作用&#xff1a; 验证用户身份&#xff1a;认证页面的主要作用是验证用户的身份。通过要求用户提供正确的凭据&#xff08;如用户名和密码、生物特征、验证码等&#xff09;&#xff0c;认证页面可以确认用户是合法…

使用华为快传同步文件至电脑

使用华为快传同步文件至电脑&#xff0c;电脑端未发现设备解决办法 1、手机和电脑连同一网络 2、打开手机华为分享&#xff0c;打开电脑网络 3、网络中找到设备&#xff0c;输入账户密码进行连接&#xff08;未找到设备往下继续看&#xff09; 未找到设备解决办法&#xff1…

图解 Transformer

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

拿捏数据结构- 链式二叉树

链式二叉树的概念&#xff1a; 链式二叉树解决的是非完全二叉树解决不了的问题 什么意思呢&#xff0c;简单的说就是&#xff0c;链式二叉树 可以是下面三种二叉树 但是非链式二叉树只能是前两种 链式二叉树的存储 节点结构&#xff1a;首先定义一个结构体或类来表示二叉树的节…

Java跨Docker容器备份数据库数据

Java跨Docker容器备份数据库数据 前置背景思路整理编写备份脚本容器启动检验效果Java容器MySQL容器 Java代码执行备份 我的个人博客&#xff1a;Lichg&#xff0c;欢迎大家访问。 前置背景 在我们的开发部署场景中&#xff0c;通常多数使用Docker进行部署。当你的数据库和项目…

Ubuntu22.04之扩展并挂载4T硬盘(二百三十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Java | Leetcode Java题解之第115题不同的子序列

题目&#xff1a; 题解&#xff1a; class Solution {public int numDistinct(String s, String t) {int m s.length(), n t.length();if (m < n) {return 0;}int[][] dp new int[m 1][n 1];for (int i 0; i < m; i) {dp[i][n] 1;}for (int i m - 1; i > 0; …

文刻创作ai工具官网免费工具

文刻创作ai工具官网免费工具 Docshttps://iimenvrieak.feishu.cn/docx/O0UedptjbonN4UxyEy7cPlZknYc 文刻是一种可以帮助用户进行创作的AI工具。 它使用自然语言处理和机器学习技术&#xff0c;可以生成文章、故事、诗歌等文本内容。 用户可以通过输入一些关键词或指定一定的…

MobaXterm连接eNSP设备

1、开启一台交换机 2、右键设置查看交换机串口号&#xff08;2000&#xff09; 3、打开MBX&#xff0c;点击session。 4、配置MBX 5、右键点击 6、配置为force off&#xff0c;点击回车就可以看到效果了

Golang | Leetcode Golang题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; func connect(root *Node) *Node {if root nil {return root}// 每次循环从该层的最左侧节点开始for leftmost : root; leftmost.Left ! nil; leftmost leftmost.Left {// 通过 Next 遍历这一层节点&#xff0c;为下一层的节点更新 Next …

损失函数篇 | YOLOv8更换损失函数之Inner-IoU | 通过辅助边界框计算IoU损失

前言:Hello大家好,我是小哥谈。损失函数是机器学习中用来衡量模型预测值与真实值之间差异的函数。在训练模型时,我们希望通过不断调整模型参数,使得损失函数的值最小化,从而使得模型的预测值更加接近真实值。为弥补现有IoU损失函数在不同的检测任务中的泛化能力较弱且收敛…

HTTPS加密过程

今天我们说https具体工作原理。 HTTPS概念 HTTPS是一种网络协议&#xff0c;传统的HTTP是明文传输&#xff0c;非常 不安全&#xff0c;所以HTTPS是基于HTTP基础上进行加密传输内容。 HTTPS使用加密传输方式 第一种是非对称加密&#xff0c;是前期建立连接时候使用的数据加密…

Golang | Leetcode Golang题解之第115题不同的子序列

题目&#xff1a; 题解&#xff1a; func numDistinct(s, t string) int {m, n : len(s), len(t)if m < n {return 0}dp : make([][]int, m1)for i : range dp {dp[i] make([]int, n1)dp[i][n] 1}for i : m - 1; i > 0; i-- {for j : n - 1; j > 0; j-- {if s[i] …

R实验 正交试验设计与一元线性回归分析

实验目的&#xff1a; 掌握正交试验设计记号的意义&#xff1b;掌握正交试验设计的直观分析和方差分析&#xff1b;掌握一元线性回归模型的相关概念&#xff1b;掌握最小二乘法的思想&#xff1b;掌握一元线性回归方程的显著性检验和预测。 实验内容&#xff1a; &#xff11;…

Python | Leetcode Python题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; class Solution:def connect(self, root: Node) -> Node:if not root:return root# 从根节点开始leftmost rootwhile leftmost.left:# 遍历这一层节点组织成的链表&#xff0c;为下一层的节点更新 next 指针head leftmostwhile head:#…

mumu 模拟器安装

1.下载安装 下载地址 Win 历史版本&#xff1a;http://mumu.163.com/update/win/Mac 历史 版本&#xff1a;http://mumu.163.com/20200515/25905_880858.html 2.设置为竖屏 在设置中心--界面设置页面设置宽720&#xff0c;高1280&#xff0c;DPI为240&#xff0c;如下图所示。…

Go语言之GORM框架(三)——Hook(钩子)与Gorm的高级查询

Hook(钩子) 和我们在gin框架中讲解的Hook函数一样&#xff0c;我们也可以在定义Hook结构体&#xff0c;完成一些操作&#xff0c;相关接口声明如下&#xff1a; type CreateUser interface { //创建对象时使用的HookBeforeCreate() errorBeforeSave() errorAfterCreate() …

C++习题(1)

一、题目描述&#xff1a; 二、代码展示&#xff1a; #include <iostream> #include <iomanip> using namespace std; struct Student{char name[20];int id;int age;float score; }; int main() {int n;cin>>n;Student student[n];float sum0.0;for(int i0…

小易大数据:大数据报告查询领域的黑马,这些优势让你无法忽视!

随着大数据技术被运用到各行各业&#xff0c;风控领域也不例外&#xff0c;形成了基于大数据技术的大数据信用&#xff0c;也就是我们常说的大数据报告或者网贷大数据&#xff0c;在众多的查询平台中&#xff0c;小易大数据平台在市面上是比较受欢迎的&#xff0c;那在小易平台…