《堆排序》与《Top—k》

news2024/11/19 19:18:56

目录

​编辑

前言:

关于《堆排序》:

第一步:建堆

第二步:排序

《Top—K问题》

关于Top—k问题:


前言:

我们在前面的blog中,对于《堆》已经有了初步的概念,那么接下来我们可以利用《堆》来解决我们日常生活中存在的问题,本篇我们给出两个常用的应用场景,分别是《排序》以及《Top—k问题》,上一篇blog在:《堆》的模拟实现-CSDN博客

 

关于《堆排序》:

#define _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>

void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void AdjustDown(int* arr, int sz, int parent)
{
	int child = parent * 2 + 1;
	while (child < sz)
	{
		if (child + 1 < sz && arr[child] < arr[child + 1])
		{
			child++;
		}

		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

void AdjustUp(int* arr, int sz, int child)
{
	while (child > 0)
	{
		int parent = (child - 1) / 2;
		if (arr[parent] < arr[child])
		{
			swap(&arr[parent], &arr[child]);
		}
		child = parent;
	}
}

int main()
{
	int arr[] = { 2, 6, 9, 3, 1, 7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, sz, i);
	}//向下调整算法
	

	//for (int i = 1; i<sz; i++)
	//{
	//	AdjustUp(arr, sz, i);
	//}//向上调整算法

	int end = sz - 1;
	while (end > 0)
	{
		swap(&arr[0], &arr[end]);
		AdjustDown(arr, end, 0);
		--end;
	}
	return 0;
}

第一步:建堆

利用《堆》可以方便我们对一个给定的乱序数组实现排序,首先我们应当选择大堆来进行排序操作。

为什么我们不选择使用小堆来进行建堆呢?

通过之前对《堆》的blog说明,小堆就是对顶元素为最小元素,其他的节点数都比第一个元素小,那么如果是小堆,最小的数字已经就是第一个元素,若要找出次小的元素,则又需要在剩下的元素中再进行建堆,重复循环才能完成排序,这样子的时间复杂度高,不利于排序。

因此我们选择利用大堆来建堆,实现大堆后,再将首尾的元素进行交换,再利用向下调整法调整法对剩下的n-1个元素进行调整,再进行交换,如此能实现排序。

    int arr[] = { 2, 6, 9, 3, 1, 7 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (int i = 1; i<sz; i++)
	{
	   AdjustUp(arr, sz, i);
	}

对如图的数组进行向上 调整法建堆:

 

第二步:排序

 首先我们将首尾元素进行交换:

对除最后一个元素外的其他元素进行向下调整法,将其继续成大堆

 

 

 

 

重复上述步骤

最终可得堆为:

 

如此则完成了堆排序。

 

《Top—K问题》

关于Top—k问题:

即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。我们以求n个数据中前K个最大的元素为例进行说明:(假设n=10000) (假设k=10)

 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
const char* file = "data.txt";
void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void AdjustDown(int* arr, int sz, int parent)
{
	int child = 2 * parent + 1;
	while (child < sz)
	{
		if (child + 1 < sz && arr[child + 1] < arr[child])
		{
			child++;
		}

		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

void CreateFile()
{
	//创建随机数的种子
	srand((unsigned int)time(NULL));
	FILE* Fin = fopen(file, "w");
	if (Fin == NULL)
	{
		perror("Fopen error");
		exit(-1);
	}

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

	fclose(Fin);
	Fin = NULL;
}

void Print()
{
	FILE* Fout = fopen(file, "r");
	if (Fout == NULL)
	{
		perror("Fout error");
		exit(-1);
	}

	//取前k个数进小堆
	int* minheap = (int*)malloc(sizeof(int) * 5);
	if (minheap == NULL)
	{
		perror("minheap -> malloc");
		return;
	}


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

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

	//读取数据
	int x = 0;
	while (fscanf(Fout, "%d", &x) != EOF)
	{
		if (minheap[0] < x)
		{
			minheap[0] = x;
		}
		AdjustDown(minheap, 5, 0);
	}

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

	fclose(Fout);
	Fout = NULL;
}

int main()
{
	//CreateFile();
	Print();
	return 0;
}

首先我们先创建10000000个随机数,再对其中的数字进行修改,随机抽5个数,分别修改为

10000001,10000002,10000003,10000004,10000005

再建一个小堆,注意,这里一定是小堆!

如果建的是大堆,若数据先搜索到了10000005,那么该数字一定是在堆顶,当我们查找到次小的数字后,却无法进堆,所以我们采用小堆!

 然后将数据的前5个元素进入小堆中,

再对剩下的9999995个数进行遍历和比较,若大于堆顶元素,则直接替换。

替换完后再进行一次向下调整,当遍历完整个数据后,堆中就是插入的

 10000001,10000002,10000003,10000004,10000005

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

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

相关文章

mapper向mapper.xml传参中文时的乱码问题

1.起因&#xff1a; 在idea中进行模糊查询传参时&#xff0c;发现在idea中查中文查不出记录&#xff0c;在navicate中可以查出来。 2.猜测&#xff1a; 1.idea中的编码问题导致的乱码。 2.idea和navicate的编码一致性导致的乱码。 3.mapper向mapper.xml传参后出现乱码。 3.解…

Transformer从菜鸟到新手(六)

引言 上篇文章介绍了如何在多GPU上分布式训练&#xff0c;本文介绍大模型常用的一种推理加速技术——KV缓存。 KV Cache KV缓存(KV Cache)是在大模型推理中常用的一种技巧。我们知道在推理阶段&#xff0c;Transformer也只能像RNN一样逐个进行预测&#xff0c;也称为自回归。…

IPv6路由协议---IS-ISv6

IS-ISv6概述 中间系统到中间系统IS-ISv6(Intermediate System to Intermediate System)属于内部网关协议IGP(Interior Gateway Protocol),用于自治系统内部。IS-IS也是一种链路状态协议,使用最短路径优先SPF(Shortest Path First)算法进行路由计算。 IS-ISv6产生 IS…

代码随想录刷题笔记(DAY 10)

今日总结&#xff1a;快要期末考试了&#xff0c;现在在疯狂速成&#xff0c;今天稍微缓和了一点&#xff0c;应该能保证继续每天刷题&#xff0c;欠下的那些寒假补上。 Day 10 01. 用栈实现队列&#xff08;No. 232&#xff09; 题目链接 代码随想录题解 1.1 题目 请你仅…

Deep Reinforment Learning Note 1

文章目录 Terminology Terminology st : stateot : observationat : action π θ ( a t ∣ o t ) \pi_\theta (a_t | o_t) πθ​(at​∣ot​) : policy π θ ( a t ∣ s t ) \pi_\theta (a_t | s_t) πθ​(at​∣st​) : policy (fully observed) Observation result from…

[软件工具]pdf多区域OCR识别导出excel工具使用教程

首先我们打开软件&#xff0c;界面如下&#xff1a; 如上图&#xff0c;使用非常简单&#xff0c;步骤如下&#xff1a; &#xff08;1&#xff09;选择工具-取模板选择一个pdf文件划定自己需要识别的区域&#xff0c;如果你选择第2页指定区域则软件统一识别所有pdf第2页指定区…

线性表入门

王有志&#xff0c;一个分享硬核Java技术的互金摸鱼侠加入Java人的提桶跑路群&#xff1a;共同富裕的Java人 从今天开始就进入到数据结构的部分了&#xff0c;整体分为3个部分&#xff1a;线性表&#xff0c;树和图&#xff0c;从认识每种数据结构到它们的高级应用。今天我们先…

如何从 Keras 中的深度学习目录加载大型数据集

一、说明 数据集读取&#xff0c;使用、在磁盘上存储和构建图像数据集有一些约定&#xff0c;以便在训练和评估深度学习模型时能够快速高效地加载。本文介绍Keras 深度学习库中的ImageDataGenerator类等工具自动加载训练、测试和验证数据集。 二、ImageDataGenerator加载数据集…

Bom 和 Dom 区别 ----- 真是DOM 和 虚拟Dom区别

DOM和BOM的区别 我们都指代&#xff0c;javascript由三个部分组成&#xff1a; ECMAScript&#xff1a;描述了JS的语法和基本对象 BOM(浏览器对象)&#xff1a;与浏览器交互的方法和对象 DOM(文档对象模型)&#xff1a;处理网页内容的方法和接 ps&#xff1a;根据宿主&#x…

数学经典教材有什么?

有本书叫做《自然哲学的数学原理》&#xff0c;是牛顿写的&#xff0c;读完之后你就会感叹牛顿的厉害之处! 原文完整版PDF&#xff1a;https://pan.quark.cn/s/5d5eac2e56af 那玩意真的是人写出来的么… 现代教材把牛顿力学简化成三定律&#xff0c;当然觉得很简单。只有读了原…

并发前置知识一:线程基础

一、通用的线程生命周期&#xff1a;“五态模型” 二、java线程有哪几种状态&#xff1f; New&#xff1a;创建完线程Runable&#xff1a;start(),这里的Runnable包含操作的系统的Running&#xff08;运行状态&#xff09;和Ready&#xff08;上面的可运行状态&#xff09;Blo…

单片机中的PWM(脉宽调制)的工作原理以及它在电机控制中的应用。

目录 工作原理 在电机控制中的应用 脉宽调制&#xff08;PWM&#xff09;是一种在单片机中常用的控制技术&#xff0c;它通过调整信号的脉冲宽度来控制输出信号的平均电平。PWM常用于模拟输出一个可调电平的数字信号&#xff0c;用于控制电机速度、亮度、电压等。 工作原理 …

2024,智能座舱不要再过度“装修”了!

文 | AUTO芯球 作者 | 雷慢 还记得2022年小鹏疲劳驾驶检测系统将小眼睛驾驶员识别为“开车睡觉”吗&#xff1f;驾驶员被扣了“智驾分”&#xff0c;你没听错&#xff0c;车主不光要焦虑驾照扣分的时候&#xff0c;还要焦虑被车子扣分。 雷慢还记得另一个奇葩事情是某问界车主…

S7-200SMART实例之冒泡法排序子程序

需求分析 编写程序实现冒泡法排序的算法。 冒泡法排序是一种简单的排序算法。因其过程如同水中气泡最终会上浮到水面一样&#xff0c;故被形象地称为“冒泡法排序”。 实现原理 根据以上需求分析可以按以下步骤实现算法&#xff1a; 1.比较相邻的元素。如果第一个比第二个…

linux 网络基础配置

将Linux主机接入到网络&#xff0c;需要配置网络相关设置一般包括如下内容&#xff1a; 主机名 iP/netmask (ip地址&#xff0c;网关) 路由&#xff1a;默认网关 网络连接状态 DNS服务器 &#xff08;主DNS服务器 次DNS服务器 第三个DNS服务器&#xff09; 一、…

多态、抽象类和接口(深拷贝和浅拷贝)

目录​​​​​​​ 前言&#xff1a; 多态&#xff1a; 多态的定义&#xff1a; 向上转型&#xff1a; 方法重写&#xff1a; 再看toString方法&#xff1a; 动态绑定&#xff1a; 向下转型&#xff1a; 小练习&#xff1a; 抽象类&#xff1a; 什么是抽象类&am…

自旋锁和互斥锁的区别

自旋锁和互斥锁的区别_自旋锁和互斥锁有什么区别?-CSDN博客

Fluids —— MicroSolvers DOP

目录 Gas SubStep —— 重复执行对应的子步 Switch Solver —— 切换解算器 Gas Attribute Swap —— 交换、复制或移动几何体属性 Gas Intermittent Solve —— 固定时间间隔计算子解算器 Gas External Forces —— 计算外部力并更新速度或速度场 Gas Particle Separate…

在Vivado下利用Tcl实现IP的高效管理

在Vivado下利用Tcl实现IP的高效管理https://cloud.tencent.com/developer/article/1736645 在Vivado下&#xff0c;有两种方式管理IP。一种是创建FPGA工程之后&#xff0c;在当前工程中选中IP Catalog&#xff0c;生成所需IP&#xff0c;这时相应的IP会被自动添加到当前工程中&…

FineBI实战项目一(18):每小时上架商品个数分析开发

点击新建组件&#xff0c;创建每小时上架商品个数组件。 选择线图&#xff0c;拖拽cnt&#xff08;总数&#xff09;到纵轴&#xff0c;拖拽hourStr到横轴。 修改横轴和纵轴的文字。 调节连线样式。 添加组件到仪表板。