椋鸟数据结构笔记#11:排序·下

news2025/1/13 13:58:29

文章目录

      • 外排序(外部排序)
        • 文件拆分并排序
        • 归并文件
          • 两个文件归并
          • 多文件归并
          • 优化

萌新的学习笔记,写错了恳请斧正。

外排序(外部排序)

数据量非常庞大以至于无法全部写入内存时,我们应该怎么排序这些数据呢?

这时,就要学会直接进行在外存中排序,也就是外部排序。

外排序不是一种独立的排序算法,还是要依赖于之前的那些排序方式。

其基本思想是这样的:

  • 将庞大的数据拆分为一个一个子文件,使得每一个子文件的数据量在内存排序可承受的范围内。
  • 将每一个文件进行正常的排序,可以选用之前学的任何排序方法。
  • 将排序好的文件再进行归并,最终合成为包含有序的所有数据的文件。

看起来不是很复杂,但是归并部分还是很有东西的。下面我们逐步讲解:

文件拆分并排序

这并不复杂,比方说我们将文件拆分为splitCount个:

void FileSort(const char* filename, int splitCount)
{
	clock_t start = clock();

	//分割文件
	FILE* fp = fopen(filename, "r");
	if (fp == NULL)
	{
		perror("fail to open");
		exit(EXIT_FAILURE);
	}
	int* num = (int*)malloc(sizeof(int) * N / splitCount);	//存放每个分割文件的数据
	int iNum = 0;	//num数组的下标
	int iFile = 0;	//分割文件的下标
	int n = 0;	//读取的数据
	char splitFileName[50] = { 0 };	//分割文件名
	while (fscanf(fp, "%d", &n) != EOF)
	{
		if (iNum < N / splitCount - 1)
		{
			num[iNum++] = n;
		}
		else
		{
			num[iNum] = n;
			//排序
			HeapSort(num, N / splitCount);
			//写入文件
			sprintf(splitFileName, "SubSort\\sort_split%d.txt", iFile++);
			FILE* splitFile = fopen(splitFileName, "w");
			if (splitFile == NULL)
			{
				perror("fail to open");
				exit(EXIT_FAILURE);
			}
			for (int i = 0; i < N / splitCount; i++)
			{
				fprintf(splitFile, "%d\n", num[i]);
			}
			fclose(splitFile);
			printf("已分割第%d个文件,总共需分割%d个文件。\n", iFile, splitCount);
			iNum = 0;
		}
	}

	//归并文件
    //这里插入归并文件的代码

	//关闭文件
	fclose(fp);

	clock_t end = clock();

	printf("排序完成,共%d个数据,分割为%d个文件,归并为1个文件。\n", N, splitCount);
	printf("耗时:%f秒。\n", (double)(end - start) / CLOCKS_PER_SEC);
}
归并文件

这是重点也是最复杂的地方。

两个文件归并

我们知道两个排序文件归并的方法:两边一起读取,一直对比两边读取的数,不断取小写入输出文件即可。

void MergeFile(char* file1, char* file2, char* mergeFileName)
{
	FILE* fp1 = fopen(file1, "r");
	FILE* fp2 = fopen(file2, "r");
	if (fp1 == NULL)
	{
		perror("fail to open");
		exit(EXIT_FAILURE);
	}
	if (fp2 == NULL)
	{
		perror("fail to open");
		exit(EXIT_FAILURE);
	}
	FILE* fin = fopen("tmp.txt", "w");
	if (fin == NULL)
	{
		perror("fail to open");
		exit(EXIT_FAILURE);
	}
	int n1, n2;
	int ret1 = fscanf(fp1, "%d", &n1);
	int ret2 = fscanf(fp2, "%d", &n2);
	while (ret1 != EOF && ret2 != EOF)
	{
		if (n1 < n2)
		{
			fprintf(fin, "%d\n", n1);
			ret1 = fscanf(fp1, "%d", &n1);
		}
		else
		{
			fprintf(fin, "%d\n", n2);
			ret2 = fscanf(fp2, "%d", &n2);
		}
	}
	while (fscanf(fp1, "%d", &n1) != EOF)
	{
		fprintf(fin, "%d\n", n1);
	}
	while (fscanf(fp2, "%d", &n2) != EOF)
	{
		fprintf(fin, "%d\n", n2);
	}
	fclose(fp1);
	fclose(fp2);
	fclose(fin);
	remove(file1);
	remove(file2);
	rename("tmp.txt", mergeFileName);
}

注意:这里我们先写入数据至tmp.txt中,最后在将其改名为mergeFileName是为了防止mergeFileName就是file1或者file2的情况(也即直接把file2归并到file1中而不创建新的文件,或者反过来)。

多文件归并

但是我们往往不仅仅要把数据切分为两个,而是几十个上百个。这时我们就需要使用多文件归并的方法。

  1. 直接逐个归并

    这是最容易理解也是最差的方法。就是把拆分文件1和拆分文件2归并,结果再和拆分文件3归并,结果再和拆分文件4归并……最终得到完整的归并文件。

    傻子都能看出来这个方法的不靠谱,但是我们还是将代码贴出来:

    //归并文件,这段代码填入上方FileSort函数中归并的留空位置
    char* mergeFileName = "sort_merge.txt";
    char file1[50] = "SubSort\\sort_split0.txt", file2[50] = {0};
    for (int i = 1; i < splitCount; i++)
    {
    	sprintf(file2, "SubSort\\sort_split%d.txt", i);
    	MergeFile(file1, file2, mergeFileName);
    	strcpy(file1, mergeFileName);
    	printf("已归并%d个文件,总共需归并%d个文件。\n", i + 1, splitCount);
    }
    

    这个方法是真的不靠谱,一亿个数据半小时还没归并完。

  2. 分治归并

    就是不断地两两分组归并,比方说9至16个文件归并成5至8个,5至8个归并位3至4个,再归并为2个,最后合成完整的排序后文件。

    //分治归并文件,这段代码填入上方FileSort函数中归并的留空位置
    MergeFileR(0, splitCount - 1);
    remove("SubSort\\sort_split0.txt");
    

    其中MergeFileR的定义如下:

    void MergeFileR(int left, int right)
    {
    	if (left >= right)
    	{
    		return;
    	}
    	int mid = (left + right) / 2;
    	//递归归并
    	MergeFileR(left, mid);
    	MergeFileR(mid + 1, right);
    	//归并
    	char file1[50] = { 0 }, file2[50] = { 0 }, mergeFileName[50] = { 0 };
    	sprintf(file1, "SubSort\\sort_split%d.txt", left);
    	sprintf(file2, "SubSort\\sort_split%d.txt", mid + 1);
    	sprintf(mergeFileName, "SubSort\\sort_split%d.txt", left);
    	MergeFile(file1, file2, mergeFileName);
    	printf("已归并%d与%d文件至%d。\n", left, mid + 1, left);
    }
    

    这个方法要靠谱一点,测试如下:

优化

进一步优化可以将基础操作从两个文件归并变成3个文件归并或者更多,这样分治时就是3合一或者更多。这样多路归并会增加内存开销和内部时间开销,但是会大大减少外部读写的开销。

更进一步优化可以引入败者树、最佳归并树等,我还不会。

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

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

相关文章

冈萨雷斯数字图像处理资源(课后习题答案+代码+图片)

冈萨雷斯数字图像处理相关资源整理&#xff0c;资源全部来源互联网&#xff0c;方便大家下载 冈萨雷斯数字图像处理相关资源整理 课后习题 冈萨雷斯数字图像处理源代码

16.Nacos环境隔离

环境隔离namespace Namespace->Group->Service/Data->集群->实例 Namespace: Group&#xff1a; nacos控制台新增一个开发环境的命名空间&#xff1a;dev, 会产生命名空间的id。 将命名空间的id配置到微服务的配置文件中&#xff1a; spring:cloud:nacos:server…

MATLAB将多张小图整合到一张大图形成模板图

MATLAB将多张小图整合到一张大图形成模板图 代码如下: clc;close all;clear all;warning off;%清除变量 rand(seed, 100); randn(seed, 100); format long g;foldername字符模板; [datacell,filenamecell,filenameAllcell]readfun_1n(foldername); K2length(filenamecell);% …

25计算机考研院校数据分析 | 上海交通大学

上海交通大学电子信息与电气工程学院成立于2001年12月&#xff0c;其前身可湖源至百年前的电机专科&#xff0c;具有中国电气工程师“摇篮”之美称。50年代根据学科发展需要分为电工与计算机科学系(三系)和电子工程系(四系)。1985年&#xff0c;三系和四系合并&#xff0c;成立…

电子签名在人力资源中的应用:让HR工作更高效

人力资源&#xff08;HR&#xff09;行业是电子签领域的重要应用之一&#xff0c;它可以在以下场景中使用电子签&#xff1a; 1. 在招聘流程中&#xff0c;HR部门可以利用电子签名工具来完成与招聘候选人之间的任何文件签署操作。例如&#xff0c;维护人才库数据库、简历、面试…

RAG:智能图书馆员引领AI的知识之旅

想象一下&#xff0c;你是一个法官&#xff0c;面对一个复杂的案件&#xff0c;你需要查阅大量的法律文献来找到相关的案例和法律条文。在AI的世界里&#xff0c;也有一个类似的“法官”——大型语言模型&#xff08;LLMs&#xff09;。它们能够回答各种问题&#xff0c;但有时…

RISC-V CVA6 在 Linux 下相关环境下载与安装

RISC-V CVA6 在 Linux 下相关环境下载与安装 所需环境与源码下载 CVA6 源码下载 首先&#xff0c;我们可以直接从 GitHub 一次性拉取所有源码&#xff1a; git clone --recursive https://github.com/openhwgroup/cva6.git如果这里遇到网络问题&#xff0c;拉取失败&#x…

vulfocus靶场struts2 命令执行 CVE-2016-3081

远程攻击者利用漏洞可在开启动态方法调用功能的Apache Struts 2服务器上执行任意代码&#xff0c;取得网站服务器控制权。 影响版本: Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3) 工具利用&#xff1a; struts2 综合利用工具&#xff1a; http://i…

4月23号总结

java实现发送邮件 在做聊天室项目的时候&#xff0c;由于需要发送邮箱验证码&#xff0c;所以自己查找了这方面的内容。 首先需要在Maven里面依赖 <dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><versio…

牛客社区所有的表和SQL语句

文章目录 1 帖子表 discuss_post1.1 字段描述1.2 相关功能描述1.2.1 分页查询帖子1.2.2 查询帖子总数量1.2.3 插入一条帖子记录1.2.4 根据帖子ID查询某条帖子1.2.5 更新帖子评论数量1.2.6 更新帖子类型1.2.6 更新帖子状态1.2.7 更新帖子分数 2 用户表 user2.1 字段描述2.2 相关…

spring一二三级缓存和@Lazy解决循环依赖流程

简单对象指的是 实例化后还没有属性注入的时候的早期bean lambda表达式用于判断a是否存在aop代理 假如a和b循环依赖&#xff0c;a实例化时&#xff0c; bean创建流程如下&#xff1a; 0&#xff0c;创建一个set记录当前正在实例化的bean&#xff0c; 1.实例化a的简单对象时…

陆金所控股一季报到底是利好还是利空?

3月底&#xff0c;陆金所控股&#xff08;LU.N;06623.HK&#xff09;因其特别分红方案受到市场高度关注。但在4月23日发布的2024年一季度财报中&#xff0c;陆金所控股营收同比下降30.9%&#xff0c;净亏损8.3亿元。 两者对比&#xff0c;外界不由得对公司的经营状况产生疑惑。…

docker内实现多机多卡分布式训练

docker内实现多机多卡分布式训练 1. 多台docker宿主机网络配置2. 创建overlay 网络3. 注意 1. 多台docker宿主机网络配置 https://docs.docker.com/network/overlay/ 这里需要创建overlay网络使得多台宿主机的容器可以通过网络连接 初始化swarm集群&#xff0c;并设置主节点&a…

条件编译 #和##运算符

目录 1. #运算符2. ##运算符3. 条件编译4. 题目分享总结 正文开始 前言: 本章为C语言语法完结撒花, 下文将进行C语言中#和##操作符以及条件编译的讲解, 来进一步让我们了解C语言. 作者主页: 酷酷学!!! 1. #运算符 #运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带…

【刷题】前缀和入门

送给大家一句话&#xff1a; 既然已经做出了选择&#xff0c;最好还是先假定自己是对的。焦虑未来和后悔过去&#xff0c;只经历一个就够了。 – 张寒寺 《不正常人类症候群》 ☆ミヾ(∇≦((ヾ(≧∇≦)〃))≧∇)ノ彡☆ ☆ミヾ(∇≦((ヾ(≧∇≦)〃))≧∇)ノ彡☆ ☆ミヾ(∇≦((ヾ…

比特币中的符文是什么?

比特币中的符文是什么&#xff1f; 比特币符文是存在于比特币区块链上的独特的、可替代的代币。它们旨在代表具有独特特征和元数据的可替代资产。 Ordinals 协议的创建者 Casey Rodamor 最近放弃了一项替代 BRC-20 可替代代币协议的提案&#xff0c;该替代方案被称为 Runes。 破…

STM32 HAL库F103系列之DAC实验(一)

DAC输出实验 原理图 DAC数据格式 DAC输出电压 DORX - 数据输出寄存器 Vref 3.3V 实验简要 1&#xff0c;功能描述 通过DAC1通道1(PA4)输出预设电压&#xff0c; 然后由ADC1通道1 (PA1) 采集&#xff0c;最后显示ADC转换的数字量及换算后的电压值 2&#xff0c;关闭通道1…

TypeError: Cannot read properties of undefined (reading ‘tapAsync‘)

项目启动&#xff0c;一直报tabAsync未定义&#xff0c;整个项目中没有找到引用的地方&#xff1b; 最终重新安装webpack4版本 解决问题&#xff1b; npm install webpack4

Android studio顶部‘app‘红叉- Moudle ‘XX.app’ dosen’t exist in project

Android studio顶部app红叉- Moudle ‘XX.app’ dosen’t exist in project 1、现象&#xff1a; 运行老项目或者有时候替换项目中的部分代码&#xff0c;明明没有错但是Android studio就编译报错了。 1.1 Android studio顶部app红叉。 1.2 点击Build没有clear菜单&#xff0…

掼蛋赢牌口诀

1、不能做头游&#xff0c;单张暂缓走。 2、起始出单张&#xff0c;表明有大王。 3、单牌先起步&#xff0c;对家应相助。 4、情况尚不明&#xff0c;对子可先行。 5、要想使个坏&#xff0c;就出三不带。 6、小顺往前凑&#xff0c;大顺必殿后。 7、哪张牌先下&#xff0c;倒数…