堆排序,TopK问题|向上调整建堆|向下调整建堆(C)

news2024/11/17 13:33:36

堆排序

void HeapSort(int* a, int n)
{
	HP hp;
	HeapInit(&hp);
	for (int i = 0; i < n; i++)
	{
		HeapPush(&hp, a[i]);
	}

	int i = 0;
	while (!HeapEmpty(&hp))
	{
		a[i++] = HeapTop(&hp);
		HeapPop(&hp);
	}

	HeapDestroy(&hp);
}
  • 先初始化一个堆
  • 将数组中的数据一个一个顺序插入堆里
  • 然后将堆顶的数据赋给数组首元素
  • 接着删除堆顶,然后读下一个堆顶,再赋给数组的下一位
  • 直到整个堆空,数组整个赋了一遍,排序完毕
    缺点
  1. 得先有一个堆的数据结构

  2. 空间复杂度的消耗

  3. 建堆
    升序:建大堆
    降序:建小堆

  4. 利用堆删除思想来进行排序

向上调整建堆

  • 升序建大堆
    要排升序,不能建小堆,虽然选出了最小的
    建小堆之后,关系全乱了,剩下的数据,不一定是堆
    要选次小,只能用剩下的数据重新建堆

升序要建立大堆
把最大的数据选出来以后,把堆顶的数据和最后一个数据交换,最大的数据排好了,在把这个最后的最大的数据不看做堆里的数据,
这样,剩下的数据的相对关系没动,左子树还是大堆,右子树还是大堆,把剩下的数看作堆,向下调整选出次大的,代价是 O ( log ⁡ 2 N ) O(\log_{2}N) O(log2N)
合计是 O ( n ∗ log ⁡ 2 N ) O(n*\log_{2}N) O(nlog2N)

排升序建大堆示意

![[Pasted image 20240927155920.png]]

  1. 交换a0和a5
    ![[Pasted image 20240927160033.png]]

  2. 从堆顶开始向下调整,直到称为大顶堆
    ![[Pasted image 20240927160148.png]]

![[Pasted image 20240927160225.png]]

  1. 交换a0与a4
    ![[Pasted image 20240927160313.png]]

  2. 继续调整为大堆
    ![[Pasted image 20240927160420.png]]

  3. 交换a0与a3
    ![[Pasted image 20240927160457.png]]

  4. 继续调整为大堆
    ![[Pasted image 20240927160543.png]]

  5. 交换a0与a2
    ![[Pasted image 20240927160628.png]]

  6. 交换a0与a1
    ![[Pasted image 20240927160741.png]]

  7. 完成
    这样最终数组就是升序的

堆排序是一种选择排序

  • 排降序建小堆
//升序
void HeapSort(int* a, int n)
{
	//建大堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}
  • 传参传数组的地址和数组大小
  • 从数组第一个数开始遍历,每个数都向上调整,这样遍历完之后,这组数据就形成大堆
  • 用end指向最后一个元素,也就是最后一个节点
  • 将根节点与最后一个节点交换
  • 从根节点开始向下调整,直到最后一个节点的最后一个节点
  • end–,将最大的一个数从堆中剔除出去
  • 然后开始循环,将根节点也就是次大的与当前最后一个节点交换,再向下调整,找出第三大的,再次end–
  • 循环直到end从数组尾开始遍历到数组头
时间复杂度

![[Pasted image 20240927154406.png]]

第1层, 2 0 2^{0} 20个节点,不调整
第2层, 2 1 2^{1} 21个节点,向上调整1次
第3层, 2 2 2^{2} 22个节点,向上调整2次
第4层, 2 3 2^{3} 23个节点,向上调整3次

第h-1层, 2 h − 2 2^{h-2} 2h2个节点,向上调整h-2次
第h层, 2 h − 2^{h-} 2h个节点,向上调整h-1次
Expected node of symbol group type, but got node of type cr
T ( h ) = − ( 2 1 + 2 2 + ⋯ + 2 h − 1 + 2 h ) + 2 h ⋅ h T(h)=-(2^{1}+2^{2}+\dots+2^{h-1}+2^{h})+2^{h}\cdot h T(h)=(21+22++2h1+2h)+2hh
T ( h ) = − ( 2 0 + 2 1 + 2 2 + ⋯ + 2 h − 2 + 2 h − 1 ) + 2 h ⋅ ( h − 1 ) + 2 0 T(h)=-(2^{0}+2^{1}+2^{2}+\dots+2^{h-2}+2^{h-1})+2^{h}\cdot (h-1)+2^{0} T(h)=(20+21+22++2h2+2h1)+2h(h1)+20
T ( h ) = − 2 h + 1 + 2 h ⋅ ( h − 1 ) + 1 T(h)=-2^{h}+1+2^{h}\cdot (h-1)+1 T(h)=2h+1+2h(h1)+1
换算到N
T ( h ) = T ( N ) = − N + ( N + 1 ) ⋅ ( log ⁡ 2 ( N + 1 ) − 1 ) + 1 T(h)=T(N)=-N+(N+1)\cdot (\log_{2}(N+1)-1)+1 T(h)=T(N)=N+(N+1)(log2(N+1)1)+1
T ( N ) = O ( N ⋅ log ⁡ 2 N − N ) T(N)=O(N\cdot \log_{2}N-N) T(N)=O(Nlog2NN)

向下调整建堆

建大堆
需要左右子树都是大堆

从下面开始往上调整:
找倒数第一个非叶子节点,也就是最后一个节点的父亲开始调
找到以后,在这个非叶子节点的子树里,进行向下调整
再往前一棵子树走,继续向下调整,
继续往前遍历每一棵子树,直到根节点

![[Pasted image 20240927164044.png]]

  1. 找到导数第一个非叶子节点,进行向下调整
2,3,5,7,4,6,8,65,100,70,32,50,60

设整个数组有n个数据,60的下标就是n-1,这里是12
通过公式计算出它的父节点的下标,13-2除以2等于5也就是6
2. 以6为根节点,建大堆,向下调整
![[Pasted image 20240927164611.png]]

  1. 往前遍历,将小标5之前的节点全部作为根节点,向下调整建大根堆
    ![[Pasted image 20240927164725.png]]

  2. 到最后三个节点
    ![[Pasted image 20240927164922.png]]

  3. 直到根节点
    ![[Pasted image 20240927165015.png]]

//升序
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;
	}
}
  • 传参传数组的地址和数组的大小
  • 从倒数第一个非叶子节点开始往前遍历,直到根节点
  • 每次遍历把当前节点视为根节点,进行向下调整,循环直到根节点
  • 这样从最底层开始,将每棵子树都变为大堆,这样最终从根节点向下调整,整棵树就变为大堆
时间复杂度

用满二叉树来计算
因为最差的情况就是节点最多的时候
总共有h层
最后一层不需要调整,从第h-1层开始调整,每个节点向下调整一次
合计要调整
T ( h ) = 2 h − 2 + 2 h − 3 ⋅ 2 + ⋯ + 2 1 ⋅ ( h − 2 ) + 2 0 ⋅ ( h − 1 ) T(h)=2^{h-2}+2^{h-3}\cdot2+\dots+2^{1}\cdot(h-2)+2^{0}\cdot(h-1) T(h)=2h2+2h32++21(h2)+20(h1)
每层的数据个数乘以向下移动的层数
等比每项乘以等差每项
Expected node of symbol group type, but got node of type cr
T ( h ) = 2 h − 1 − h T(h)=2^{h}-1-h T(h)=2h1h
T ( h ) T(h) T(h)是向下调整建堆,最坏情况下的合计调整次数
换算关于N的式子,得到时间复杂度
T ( N ) = N − log ⁡ 2 ( N + 1 ) T(N)=N-\log_{2}(N+1) T(N)=Nlog2(N+1)
约等于N

TopK问题

一般情况下,直接建堆,然后popk次

#include "Heap.h"

int main()
{
	int a[] = { 2,3,4,7,4,6,8 };
	HP hp;
	HeapInit(&hp);
	for (int i = 0; i < sizeof(a) / sizeof(int))
	{
		HeapPush(&hp, a[i]);
	}
	HeapPrint(&hp);

	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}

	HeapDestroy(&hp);

	return 0;
}

假设10亿个数据,内存存不下,数据在文件中,找出最大的k个
假设k等于100

  1. 读取文件中的前100个数据,在内存数组中建立一个小堆
  2. 再依次读取剩下的数据,跟堆顶数据比较,大于堆顶,就替换他进堆,向下调整
  3. 所有数据读完,堆里面的数据就是最大的前100个

不能使用大堆,否则最大的数进堆以后,就挡在这里,剩下的数就无法进堆
所以使用小堆,最大的前100个数任意一个数进来都可以进堆
小堆里面,大的数回沉到下面去

时间效率很高,有N个数据
时间复杂度 O ( N ⋅ log ⁡ 2 K ) O(N\cdot \log_{2}K) O(Nlog2K)
空间复杂度 O ( K ) O(K) O(K)
如果N远大于K
时间复杂度 O ( N ) O(N) O(N)
空间复杂度 O ( 1 ) O(1) O(1)

在文件当中找到最大的前k个

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>

void PrintTopK(const char* filename, int n, int 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);
	}

	//将剩余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");

	fclose(fout);
}  

void CreateNote()
{
	int n = 10000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

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

	fclose(fin);
}

int main()
{
	//CreateNote();
	PrintTopK("data.txt", 10);
	
	return 0;
}

如何证明程序是对的,找到的是最大的前k个?
前面建立随机数文件的时候mod了个1000000,在文件里随机添加几个超过一百万的数,看是否出现在程序结果里,如果把这些随机插进去的数都能找到,就代表程序是正确的

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

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

相关文章

【Python大语言模型系列】在阿里云ECS服务器上部署dify大模型应用开发平台(完整教程)

这是我的第360篇原创文章。 一、阿里云ECS简介 云服务器( Elastic Compute Service ,简称ECS )是一-种简单高效、处理能力可弹性伸缩的计算服务&#xff0c;帮助您快速构建更稳定、安全的应用,提升运维效率,降低IT成本&#xff0c;使您更专注于核心业务创新。 这个活动需要满…

Vatee万腾平台:企业智能化生态的领航者

在科技日新月异的今天&#xff0c;企业的智能化转型已成为不可逆转的趋势。Vatee万腾平台&#xff0c;凭借其前瞻性的技术视野和不懈的创新精神&#xff0c;正逐步构建起一个以智能化为核心的新型商业生态&#xff0c;成为推动企业智能化升级的核心力量。 Vatee万腾平台深谙技…

软件设计之Maven(1)

软件设计之Maven(1) 路线图推荐&#xff1a; 【Java学习路线-极速版】【Java架构师技术图谱】 尚硅谷新版Maven教程&#xff08;高效入门maven&#xff0c;上手又快又稳&#xff09; 资料可以去尚硅谷官网免费领取 学习内容&#xff1a; Maven简介安装与环境配置Maven工程GA…

【机器学习(九)】分类和回归任务-多层感知机(Multilayer Perceptron,MLP)算法-Sentosa_DSML社区版

文章目录 一、算法概念二、算法原理&#xff08;一&#xff09;感知机&#xff08;二&#xff09;多层感知机1、隐藏层2、激活函数sigma函数tanh函数ReLU函数 3、反向传播算法 三、算法优缺点&#xff08;一&#xff09;优点&#xff08;二&#xff09;缺点 四、MLP分类任务实现…

【html网页制作】国庆节日主题网页制作含js轮播(5页面附效果源码)

HTMLCSS节日国庆主题网页制作 &#x1f354;涉及知识&#x1f964;写在前面&#x1f367;一、网页主题&#x1f333;二、网页效果菜单切换效果PageA、整体页Page1、首页Page2、节日由来Page3、节日视频Page4、庆祝活动Page5、留言 &#x1f40b;三、网页架构与技术3.1 脑海构思…

# linux从入门到精通(二)

linux从入门到精通&#xff08;二&#xff09; 一、Linux系统的安装&#xff1a;新建VM虚拟机 1、Linux版本的选择 1&#xff09;版本选择&#xff1a;CentOS 6.5 【镜像一般都是xxx.iso文件】 2&#xff09;为什么不选择7.x版本&#xff1f; 6.x的各种系统操作模式是基础…

python绘制图像

柱状图 import os# 输入想要存储图像的路径 os.chdir(D:)import matplotlib.pyplot as plt import numpy as np # 改变绘图风格 import seaborn as snssns.set(color_codesTrue)cell [gen7, xgspon, 3081GB, vettel, totalplay, other] pvalue [21, 20, 18, 13, 7, 34]width…

​​XrayGLM原理与部署

接上一篇&#xff1a;VisualGLM-6B——原理与部署-CSDN博客 XrayGLM技术背景与原理 XrayGLM 是一种基于 VisualGLM-6B 微调开发的多模态医学影像诊断模型&#xff0c;专门用于处理医学影像&#xff08;如 X 光胸片&#xff09;的自动诊断和报告生成任务。该模型旨在为中文医学…

第8章_索引的创建与设计原则

1. 索引的声明与使用 1.1 索引的分类 MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。 从 功能逻辑 上说&#xff0c;索引主要有 4 种&#xff0c;分别是普通索引、唯一索引、主键索引、全文索引。 按照 物理实现方式 &#xff0c;索引可以…

医疗器械中的国产光耦合器浅析

光耦合器&#xff0c;也称为光隔离器&#xff0c;在确保医疗器械的安全性和性能方面发挥着关键作用。通过在系统的不同部分之间提供电气隔离&#xff0c;它们可以防止信号干扰、保护敏感元件并保护患者免受电击。近年来&#xff0c;国产光耦合器制造商一直在加紧生产可靠的高性…

时也命也!转念的力量(深度好文)——早读(逆天打工人爬取热门微信文章解读)

时也命也 引言Python 代码第一篇 洞见 转念的力量&#xff08;深度好文&#xff09;第二篇 意想不到的收入结尾 早上上交所宕机 很多股票都横成直线 我的股却跳了一下水 我怕出什么监管事故 跟着卖出去了 然后查了一下 发现是上交所被买爆了 我想了一下 服务器问题 那么能否事后…

OpenEuler配置本地yum源

0x00 服务器版本 将本地镜像传输至服务器 操作步骤如下 # 创建一个目录用于挂载光盘映像 mkdir /media/cdrom/# 将光盘映像挂载到指定目录 mount /kvm/openeuler.iso /media/cdrom/#进入Yum仓库配置目录 cd /etc/yum.repos.d/# 备份原有的 openEuler.repo 文件 mv openEuler.…

解读: 火山引擎自研vSwitch技术

最近看到一篇文章介绍火山云的网络vSwitch技术&#xff0c;虽然是2022年的比较老的介绍&#xff0c;但是对于我们看到vSwitch技术的发展还是有些参考的。下面就截取了当时火山vSwitch关心的几个问题&#xff0c;做了一下梳理。 背景 在云计算发展过程中&#xff0c;虚拟网络的…

国内光耦合器制造商如何满足特殊行业的需求

在航空航天、国防、电信等行业&#xff0c;对定制解决方案的需求正在增长。通用光耦合器可能并不总是适合这些专业领域的特定要求&#xff0c;因为这些领域必须满足独特的环境条件、尺寸限制和性能基准。国内光耦合器制造商一直在加紧努力&#xff0c;提供可定制的解决方案&…

从 0 到 1:互联网产品经理核心技能全解析

在互联网的浩瀚海洋中&#xff0c;产品经理如同领航员&#xff0c;决定着产品从无到有的走向和命运。从 0 到 1 打造一款互联网产品&#xff0c;需要产品经理具备多方面的核心技能。今天&#xff0c;我们就来详细解读这些关键能力。 一、洞察市场趋势 时刻关注行业动态 互联网…

AOT源码解析4.1-对输入数据和mask进行处理(Associating Objects with Transformers for Video Object Segmentation)

论文阅读 papergithub论文阅读笔记AOT源码解析1-数据集处理AOT源码解析2-encoderdecoderAOT源码解析3-模型训练AOT源码解析4.1-model主体AOT源码解析4.2-model主体AOT源码解析4.3-model主体AOT源码解析4.4-model主体AOT源码解析4.5-model主体 输入数据 VOS的数据集处理操作可…

Python基础语句教学

Python是一种高级的编程语言&#xff0c;由Guido van Rossum于1991年创建。它以简单易读的语法和强大的功能而闻名&#xff0c;被广泛用于科学计算、Web开发、数据分析等领域。 Python的应用领域广泛&#xff0c;可以用于开发桌面应用程序、Web应用、游戏、数据分析、人工智能等…

【中台资料】数字中台建设资料合集(Word,PPT)

目录 1 编写目的 2 背景概述 3 中台概念 4 推动企业组织模式演进 5 建设方法 6 中台内容 7 数据安全体系 8 参考资料 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需…

Redis 的 Java 客户端有哪些?官方推荐哪个?

Redis 官网展示的 Java 客户端如下图所示&#xff0c;其中官方推荐的是标星的3个&#xff1a;Jedis、Redisson 和 lettuce。 Redis 的 Java 客户端中&#xff0c;Jedis、Lettuce 和 Redisson 是最常用的三种。以下是它们的详细比较&#xff1a; Jedis&#xff1a; 线程安全&…

springboot在线教学平台

基于springbootvue实现的在线教学平台 &#xff08;源码L文ppt&#xff09;4-069 4.1系统结构设计 这些功能可以充分满足在线教学平台的需求。此系统功能较为全面如下图系统功能结构如图4-1所示。 图4-1功能结构图 4.2系统功能模块设计 在线教学平台的使用者主要有二类…