堆中的时间复杂度+TOP K问题

news2024/11/14 23:34:26

堆中的时间复杂度分析

回顾: 堆在物理上:数组 逻辑上:完全二叉树

1.堆排序是什么?

// 排升序

void HeapSort(int* a, int n)

{
	// 建大堆 -
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		// 选出次大的
		AdjustDown(a, end, 0);
		--end;
	}
}

2.堆的接口实现回顾

typedef int HPDataType;

typedef struct Heap
{
	//数组
	HPDataType*a;
	int size;
	int capacity; 
};

void HeapInit(HP* php)
{
	assert(php);
	php->a=NULL;
	php->size=php->capacity=0;
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(php);
	free(php->a);
	php->a=NULL;
	php->size=php->capacity=0;
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(php);
	//涉及到有没有空间插入
	if(php->size==php->capacity)
	{
		int newcapacity=php->capacity==0?4:php->capacity*2;//原来就没空间先给4个,空间不够直接扩2倍	
		HPDataType*tmp=(HPDataType*)realloc(php->a,newcapacity*sizeof(HPDataType));
	} 
	if(tmp==NULL)
	{
		perror("realloc fail");
		return;
	}
	//忘了 扩完容重新赋值啊 
	php->a=tmp;
	php->capacity=newcapacity; 
	//插到尾
	php->a[php->size]=x;//不是插在size-1 是插在size-1的后面 
	php->size++;
	//破坏了原来的大小关系,通过向上调整法
	Adjustup(php->a,php->size); 
}
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;
	}
	
}
void Swap(HPDataType*p1,HPDataType*p2)
{
	HPDataType tmp=*p1;//这个是代表的p1解引用,所以tmp无需* 
	*p1=*p2;
	*p2=tmp; 
}
// 堆的删除
void HeapPop(Heap* hp)
{
	//顶和尾互换后删掉尾,删尾很简单直接--
	assert(php);
	assert(php->size>0);
	Swap(&php->a[0],&php->a[php->size-1]);
	php->size--;
	//向下调整会原来的堆 
	Adjustdown(php->a,php->size,0);//从头开始向下调整 
}
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;
	}
	
} 
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(php);
	assert(php->size>0);//堆不为空,有东西可取
	return php->a[0]; 
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(php);
	assert(php->size>0);
	return php->size;
}
// 堆的判空
bool HeapEmpty(Heap* hp)
{
	assert(php);
	return php->size==0;
}

完全二叉树的高度范围

最多:满二叉树 最后一层是满的 h=log(N+1)
最少: 最后一层只有一个 h=logN+1

所以无论是完全二叉树还是满二叉树
高度的量级都是logN

所以时间复杂度都是O(logN)

堆排序有两种:向上调整建堆 向下调整建堆 我们通常用向下建堆

这里详细讲一下,为什么是向下调整建堆

向下调整建堆是从哪里开始调的?
从最后一个分支节点,–调
首先:最后一层的叶子结点是不需要调的

在这里插入图片描述
这里的时间复杂度不是看代码就能看出来的,需要找规律列式子算出来

最终得到向下调整建堆时间复杂度是:O(N)

Top K问题

在N个数据中找到最大的前K个
其中
若N和K相差不多就使用方法一
若N>>>K就是用方法二

方法一:建一个N个数的大堆 O(N)
Pop K次 O(K*logN)
取一个删一个,取出最大的K个

但是这个方法有个缺陷
若N>>>K,比如 N=10亿+

让我们来算算他所需要的空间
1G=1024MB=1024 x 1024KB=1024 x 1024 x 1024Byte

若有10亿个数据 大概需要3.8个G

这个数组的空间太大 是开辟不出来的

所以当N>>>K我们需要
方法二:
用前K个数,建小堆 O(K)
剩下的数据与堆顶的数据比较,比堆顶大就代替顶进堆,覆盖根再向下调整
O(logK*(N-K))

数字 int 一个4个字节

所以这个方法只需要4K个字节

最后这个小堆的K个数,就是最大的前K个数
合计复杂度:O(N)

#include<stdio.h>
#include<stdlib.h>
void CreatNData()
{
	//造数据函数
	int n = 100000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		reutrn NULL; 
	}
	for (int i = 0; i < n; i++)
	{
		int x = (rand() + i) % 100000;//因为rand只能做到三万个随机数据不重复,+i可以大大减少重复率
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

int main()
{
	int k;
	scanf("%d", &k);
	int* kminheap(int*)malloc(sizeof(int) * k);//因为k是我们手动输入的所以只能用malloc
	if (kminheap == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		reutrn NULL;
	}
	for (int i = 0; i < k; i++)//读取前K个数
	{
		fscanf(fout, "%d\n", &kminheap[i]);
	}
	//建K个数的小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		Adjustdown(kminheap, k, i);
	}
	int x = 0;
	while (fscanf(fout, "%d", &kminheap[i]));
	{
		if (x > kminheap[0])//大于堆顶
		{
			kminheap[0] = x;//f覆盖
			Adjustdown(kminheap, k, 0);
		}
	}
	printf("打印最大的前%d个", k);
	for (int i = 0; i < k; i++)
	{
		printf("%d", kminheap[i]);
	}
	printf("\n");
	return 0;
}

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

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

相关文章

学Linux的第八天

目录 管理进程 概念 程序、进程、线程 进程分类 进程前后台调用 查看进程 ps命令 unix 风格 bsd风格 GNU风格 top命令 格式 统计信息区 进程信息区&#xff1a;显示了每个进程的运行状态 kill命令 作用 格式 管理进程 概念 程序、进程、线程 程序&#x…

网络初识--Java

一、网络通信基础 1.IP地址 IP地址主要⽤于标识⽹络主机、其他⽹络设备&#xff08;如路由器&#xff09;的⽹络地址。简单说&#xff0c;IP地址⽤于定位主 机的⽹络地址。 就像我们发送快递⼀样&#xff0c;需要知道对⽅的收货地址&#xff0c;快递员才能将包裹送到⽬的地。…

Linux软件包管理与Vim编辑器使用指南

目录 一、Linux软件包管理器yum 1.什么是软件包&#xff1f; 2.什么是软件包管理器&#xff1f; 3.查看软件包 4.安装软件 ​编辑 5.卸载软件 Linux开发工具&#xff1a; 二、Linux编辑器---vim 1.vim的基本概念 (1) 正常/普通模式&#xff08;Normal mode&#xff0…

标准库 -- 为什么 EXTI中断需要使能复用时钟与为什么不需要使能?

在STM32中&#xff0c;使用外部中断&#xff08;EXTI&#xff09;时需要使能复用功能&#xff0c;这和其他中断&#xff08;如串口中断、定时器中断&#xff09;有所不同。以下是为什么在使用外部中断时需要使能复用&#xff0c;以及其他中断不需要复用的原因。 一、为什么 EX…

深入理解ECDSA:椭圆曲线数字签名算法的原理与应用

目录 引言一、什么是ECDSA二、ECDSA的基本原理三、椭圆曲线四、ECDSA签名生成过程1、 生成私钥和公钥2、签名3、签名对的保存 五、ECDSA签名验证过程六、ECDSA的安全性七、篡改的消息如何被检测到八、 为什么B能够知道篡改&#xff1f;九、python代码示例总结 引言 在数字通信…

rocketmq——docker-compose安装

rocketmq安装 创建文件夹&#xff0c;这里我们分别部署namesrv和broker 1、namesrv.conf listenPort98762、broker.conf # 所属集群名字 brokerClusterNameDefaultCluster # broker 名字&#xff0c;注意此处不同的配置文件填写的不一样&#xff0c;如果在 broker-a.propert…

微积分复习笔记 Calculus Volume 1 - 5.5 Substitution

5.5 Substitution - Calculus Volume 1 | OpenStax

初试js反混淆

一、目标 ​ 最近js玩的花样越来越多了&#xff0c;本来简洁方便的一门开发语言&#xff0c;现在混淆的一塌糊涂。今天我们就介绍几种常见的反混淆方案。 混淆的本质就是等价替换&#xff0c;把 a 12 ,替换成 a 100 - 8 5 - 15 - 70。 把 “push” 替换成 “\u0070\u0075…

基于微信小程序的乡村研学游平台设计与实现,LW+源码+讲解

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自…

odoo17 前端 在头像下拉 dropdown 自定义菜单

odoo17 前端 在头像下拉 dropdown 自定义菜单 其实很简单, 我们先找到原来已经创建好的, 找到代码位置 使用 我的资料 为例 odoo-17.0\addons\hr\static\src\user_menu\my_profile.js /** odoo-module **/import { _t } from "web/core/l10n/translation"; import …

【解决】Layout 下创建槽位后,执行 Image 同步槽位位置后表现错误的问题。

开发平台&#xff1a;Unity 6.0 编程语言&#xff1a;CSharp 编程平台&#xff1a;Visual Studio 2022   一、问题背景 | 开发库存系统 图1 位置同步失败问题 图2 位置正常同步效果表现 黑框 作用于 UnityEngine.UI.GridLayoutGruop&#xff0c;形成 4x6 布局&#xff0c;如…

2023年MathorCup数学建模B题城市轨道交通列车时刻表优化问题解题全过程文档加程序

2023年第十三届MathorCup高校数学建模挑战赛 B题 城市轨道交通列车时刻表优化问题 原题再现&#xff1a; 列车时刻表优化问题是轨道交通领域行车组织方式的经典问题之一。列车时刻表规定了列车在每个车站的到达和出发&#xff08;或通过&#xff09;时刻&#xff0c;其在实际…

07-案例-图书管理

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

vue 依赖注入(Provide、Inject )和混入(mixins)

Prop 逐级透传问题​ 通常情况下&#xff0c;当我们需要从父组件向子组件传递数据时&#xff0c;会使用 props。想象一下这样的结构&#xff1a;有一些多层级嵌套的组件&#xff0c;形成了一棵巨大的组件树&#xff0c;而某个深层的子组件需要一个较远的祖先组件中的部分数据。…

九州未来再度入选2024边缘计算TOP100

随着数智化转型的浪潮不断高涨&#xff0c;边缘计算作为推动各行业智能化升级的重要基石&#xff0c;正在成为支持万物智能化的关键点。近日&#xff0c;德本咨询(DBC)联合《互联网周刊》(CIW)与中国社会科学院信息化研究中心(CIS)&#xff0c;共同发布《2024边缘计算TOP100》榜…

git相关知识

前言&#xff1a;在学习git之前首先需要了解几个概念&#xff1a;工作区&#xff0c;暂存区&#xff0c;版本库。 工作区&#xff1a;是电脑上写代码或者文件的目录。 暂存区&#xff1a;一般存放在.git目录下的index中&#xff0c;也称索引。&#xff08;git add&#xff09…

应用程序部署(IIS的相关使用,sql server的相关使用)

数据服务程序&#xff08;API&#xff09;部署 1、修改配置文件 打开部署包中的web.config配置文件&#xff0c;确认数据库登录名和密码正确 修改ip为电脑IP&#xff08;winR输入cmd&#xff0c;输入ipconfig&#xff0c;IPv4对应的就是本机IP&#xff09; 2、打开IIS&#x…

conda和conda的常用命令

目录 一、什么是conda 1. conda的定义和作用 2. conda的特点 3. conda与pip的区别 二、conda的常用命令 1. 环境管理 2.包管理 3. 查看信息 4. 清理和维护 5. 频道(channel)管理 6. 导出和复制环境 7. 加速相关(镜像) 一、什么是conda 1. conda的定义和作用 2. co…

mysql每日一题(上升的温度,date数据的计算)

日期之间的运算 日期类型的加法运算 data_add(now_data,interval 1 month) select date_add(now(), interval 1 day); -- 加1天 select date_add(now(), interval 1 hour); -- 加1小时 select date_add(now(), interval 1 minute); -- 加1分钟 select date_add(now(), inter…

kafka生产经验——消费者事务

如果想完成Consumer端的精准一次性消费&#xff0c;那么需要Kafka消费端将消费过程和提交offset 过程做原子绑定。此时我们需要将Kafka的offset保存到支持事务的自定义介质&#xff08;比 如MySQL&#xff09;。这部分知识会在后续项目部分涉及。 事务的四大特征&#xff1a;AC…