排序篇:外排序(排序文件中的数据)

news2024/11/20 11:27:17

目录

前言:

一:大体思路

二:分割成有序的小文件

(1)先给代码

(2)解析

三:进行文件归并

(1)主逻辑

(2)归并两个有序文件

四:全部代码


前言:

如果要排序的数据量非常大,内存无法容纳,我们就需要借助磁盘来进行排序了。

在之前的文章中我们介绍了归并排序,外排序就是利用了归并排序来进行排序

注意:本文排序时生成的文件默认在项目目录下,为了方便观察,分割出的子文件放在sub目录下(使用前请检查是否有sub文件夹),大文件存储的数据格式统一为数据+换行。

归并排序链接(有兴趣可以看,不看也没影响):https://blog.csdn.net/2301_76269963/article/details/130542554?spm=1001.2014.3001.5502


一:大体思路

(1)进行文件分割

我们可以把大文件分割成很多有序的小份,一小份的数据量依据实际情况来决定。


(2)进行文件归并

将一个个有序的小文件进行归并,最后合成一个有序的大文件。

 


二:分割成有序的小文件

(1)先给代码

//文件分割
int FSplit(FILE* pf)
{
	//分割出的文件数
	int file_num = 0;
	//一份要多少,可以自己修改,这里取10
	int count = 0;
	//存储数组
	int* tmp = (int*)malloc(sizeof(int) * 10);
	if (tmp == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}

	//读取数据
	int x = 0;
	char SubFile[30] = "";
	int i = 1;
	//先读取一个
	int ret = fscanf(pf, "%d\n", &x);
	while (1)
	{
		if (count < 10 && ret!= EOF)
		{
			tmp[count++] = x;
			ret = fscanf(pf, "%d\n", &x);
		}
		else
		{
			file_num++;
			//先排序临时数组
			QuickSort(tmp, 0, count-1);
			//排好了读取到文件中
			//为了不使文件太凌乱,我们把排序过程产生的子文件放在sub目录下
			//使用前检查项目目录下是否有sub文件夹
			sprintf(SubFile, "sub\\SubSort%d.txt", i++);
			FILE* ChildF = fopen(SubFile, "w");
			for (int j = 0; j < count; j++)
			{
				fprintf(ChildF, "%d\n", tmp[j]);
			}
			fclose(ChildF);
			//计数器归0
			count = 0;

			//如果最后一组排序完成,跳出
			if (ret == EOF)
			{
				break;
			}
		}
	}
	return file_num;
}

(2)解析

 ①要使分割出的小文件有序,我们先用临时数组来保存读取的数据,把数组进行排序后再进行数据读入。

②file_num用来记录分割出的子文件数,x用来记录读取数据,count用来记录一份子文件的数据量。

循环的过程中我们需要不断改变文件名来进行区分,i用来更改文件名,每次生成一个子文件就加1,SubFile用来保存子文件名。


 

ret用来接收fscanf的返回值,用来判断文件是否读取到结尾

②如果文件未读取到结尾并且一份没有达到10,就把数据放在tmp数组中,然后再进行一次读取。

如果文件读取到了结尾或者一份到达10,file_num加1,排序临时数组

利用sprintf生成子文件名,打开子文件,将数组元素读入到子文件中,关闭文件后count置0,如果这个时候文件已经读取到结尾,就结束循环。


三:进行文件归并

(1)主逻辑

 

 

图解:

 


(2)归并两个有序文件

代码:

//合并两个有序文件
void MergeOrderFile(const char* f1, const char* f2, const char* m1)
{
	//先打开文件
	FILE* pf1 = fopen(f1, "r");
	if (pf1 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}
	FILE* pf2 = fopen(f2, "r");
	if (pf2 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}
	FILE* pm1 = fopen(m1, "w");
	if (pm1 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}

	//归并
	//记录两个文件数据
	int num1 = 0;
	int num2 = 0;
	//记录两个文件读取状态
	int ret1 = fscanf(pf1, "%d\n", &num1);
	int ret2 = fscanf(pf2, "%d\n", &num2);

	while (ret1 != EOF && ret2 != EOF)
	{
		if (num1 < num2)
		{
			fprintf(pm1, "%d\n", num1);
			ret1 = fscanf(pf1, "%d\n", &num1);
		}
		else
		{
			fprintf(pm1, "%d\n", num2);
			ret2 = fscanf(pf2, "%d\n", &num2);
		}
	}

	//把没结束的一方写入文件
	while (ret1 != EOF)
	{
		fprintf(pm1, "%d\n", num1);
		ret1 = fscanf(pf1, "%d\n", &num1);
	}
	while (ret2 != EOF)
	{
		fprintf(pm1, "%d\n", num2);
		ret2 = fscanf(pf2, "%d\n", &num2);
	}

	//关闭文件
	fclose(pf1);
	fclose(pf2);
	fclose(pm1);
}

图解:

 


四:全部代码

​
​// 三数取中
int GetMidIndex(int* a, int left, int right)
{
	int midi = left + (right - left) / 2;

	if (a[midi] > a[right])
	{
		if (a[midi] < a[left])
			return midi;
		else if (a[right] > a[left])
			return right;
		else
			return left;
	}
	else // a[right] > a[mid]
	{
		if (a[midi] > a[left])
			return midi;
		else if (a[left] < a[right])
			return left;
		else
			return right;
	}
}

//前后指针法
int partion3(int* a, int left, int right)
{
	int midi = GetMidIndex(a, left, right);
	swap(&a[midi], &a[left]);

	int keyi = left;
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		//++prev和cur相等,无效交换,不换
		if (a[cur] < a[keyi] && ++prev!= cur)
		{
			swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	swap(&a[keyi], &a[prev]);
	return prev;
}

//快速排序
void QuickSort(int* a, int left, int right)
{
	//如果left>right,区间不存在
	//left==right,只有一个元素,可以看成是有序的
	if (left >= right)
	{
		return;
	}

    //单次排序
	int keyi = partion3(a, left, right);

	//分成左右区间,排序左右区间
	QuickSort(a, left, keyi - 1);
	QuickSort(a, keyi + 1, right);	
}

//文件分割
int FSplit(FILE* pf)
{
	//分割出的文件数
	int file_num = 0;
	//一份要多少,可以自己修改,这里取10
	int count = 0;
	//存储数组
	int* tmp = (int*)malloc(sizeof(int) * 10);
	if (tmp == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}

	//读取数据
	int x = 0;
	char SubFile[30] = "";
	//更改文件名
	int i = 1;
	//先读取一个
	int ret = fscanf(pf, "%d\n", &x);
	while (1)
	{
		if (count < 10 && ret!= EOF)
		{
			tmp[count++] = x;
			ret = fscanf(pf, "%d\n", &x);
		}
		else
		{
			file_num++;
			//先排序临时数组,这里使用快排
			QuickSort(tmp, 0, count-1);
			//排好了读取到文件中
			//为了不使文件太凌乱,我们把排序过程产生的子文件放在sub目录下
			//使用前检查项目目录下是否有sub文件夹
			sprintf(SubFile, "sub\\SubSort%d.txt", i++);
			FILE* ChildF = fopen(SubFile, "w");
			for (int j = 0; j < count; j++)
			{
				fprintf(ChildF, "%d\n", tmp[j]);
			}
			fclose(ChildF);
			//计数器归0
			count = 0;

			//如果最后一组排序完成,跳出
			if (ret == EOF)
			{
				break;
			}
		}
	}
	return file_num;
}

//合并两个有序文件
void MergeOrderFile(const char* f1, const char* f2, const char* m1)
{
	//先打开文件
	FILE* pf1 = fopen(f1, "r");
	if (pf1 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}
	FILE* pf2 = fopen(f2, "r");
	if (pf2 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}
	FILE* pm1 = fopen(m1, "w");
	if (pm1 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}

	//归并
	//记录两个文件数据
	int num1 = 0;
	int num2 = 0;
	//记录两个文件读取状态
	int ret1 = fscanf(pf1, "%d\n", &num1);
	int ret2 = fscanf(pf2, "%d\n", &num2);

	while (ret1 != EOF && ret2 != EOF)
	{
		if (num1 < num2)
		{
			fprintf(pm1, "%d\n", num1);
			ret1 = fscanf(pf1, "%d\n", &num1);
		}
		else
		{
			fprintf(pm1, "%d\n", num2);
			ret2 = fscanf(pf2, "%d\n", &num2);
		}
	}

	//把没结束的一方写入文件
	while (ret1 != EOF)
	{
		fprintf(pm1, "%d\n", num1);
		ret1 = fscanf(pf1, "%d\n", &num1);
	}
	while (ret2 != EOF)
	{
		fprintf(pm1, "%d\n", num2);
		ret2 = fscanf(pf2, "%d\n", &num2);
	}

	//关闭文件
	fclose(pf1);
	fclose(pf2);
	fclose(pm1);
}

//文件排序
void MergeSortFile(const char* ParentFile)
{
	//先打开父文件
	FILE* pf = fopen(ParentFile, "r");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return;
	}

	//分割成小文件
	int file_num = FSplit(pf);
	fclose(pf);

	//小文件进行归并
	//File1和MergeFile给大一点,不然可能越界
	char File1[500] = "sub\\SubSort1.txt";
	char File2[20] = "sub\\SubSort2.txt";
	char MergeFile[500] = "12";
	for (int i = 2; i <= file_num; i++)
	{
		//归并两个子文件
 		MergeOrderFile(File1,File2,MergeFile);

		//迭代
		strcpy(File1, MergeFile);
		sprintf(File2, "sub\\SubSort%d.txt", i + 1);
		sprintf(MergeFile, "%s%d", MergeFile, i + 1);
	}
}

实际演示

待排序文件为sort.txt,sub文件夹放子文件

 

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

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

相关文章

零基础自学软件测试我只用了105天,从月薪3000到15K, 我整理的超全学习指南!

我21年毕业于管理类专业&#xff0c;干了大半年行政打杂&#xff0c;工作平淡无快感。性格较内向&#xff0c;思维严谨独立。喜欢软件测试工作内容的系统性&#xff0c;技术性&#xff0c;丰富性。以上便是转行的最初心理&#xff0c;大家觉得可妥&#xff1f;说干就干去年6月底…

(前期准备工作2)白嫖Replit的免费云服务器搭建属于自己的应用

Replit介绍 Replit(原来是https://repl.it)是一个基于浏览器的云端协同开发平台,可用于构建开发环境、实时协作、托管网络应用等。Replit提供可创建动态或者静态网站的容器,并会自动生成免费https域名(格式为:项目名.用户名.http://repl.co)。这代表着任何人都可以试用…

Kafka的工作原理

一、Kafka是什么&#xff1f; Kafka是一个分布式的基于发布/订阅模式的消息队列。分布式消息队列可以看成是将这种先进先出的数据结构独立部署在服务器上&#xff0c;应用程序可以通过远程访问接口使用它。 二、Kafka的工作机制是什么&#xff1f; 1.基本概念 2.消息模型 发…

WiFi基础学习到实战(六:Beacon帧字段解析)

欢迎大家一起学习探讨通信之WLAN。上节我们基于Android设备分析了WiFi扫描的代码实现&#xff0c;具体执行WiFi网络扫描由WiFi模块实现。WLAN协议定义扫描方式有“被动扫描”和“主动扫描”。本节继续分析“被动扫描”依赖Beacon帧中的字段。 好。我们先来看Android11 WiFi扫描…

会计转行数据分析,可行性多高?

看到这样的问题&#xff0c;第一个想法是想劝退&#xff0c;毕竟通过不明真相的网友身上找自己的未来&#xff0c;这件事听着就不靠谱。转行难&#xff0c;转行做好更好&#xff0c;虽然会计也与数据有关&#xff0c;但是数据分析涉及的技术内容明显有很大的差别。所以&#xf…

重塑职业未来:在竞争激烈的职场上脱颖而出的关键策略

在竞争激烈的职场上&#xff0c;各种职场难题时常出现&#xff0c;如何进行有效沟通、如何应对工作压力、如何提升职业能力等&#xff0c;这都是需要去克服的问题。下面分享一下职场老人的经验&#xff01; 一、你遇到过哪些职场问题&#xff1f;分享一下你是怎么解决的呢&…

Type-C边充电边OTG转接器方案

随着生活水平的提高&#xff0c;大家的电子设备也多了起来&#xff0c;更有甚者会凑齐“全家桶”&#xff0c;手机&#xff0c;平板&#xff0c;笔记本电脑&#xff0c;智能手表&#xff0c;无线耳机&#xff0c;Switch&#xff0c;PS5&#xff0c;一样不落。那么多的电子设备&…

hibernate入门项目(一)

本节我们将演示如何搭建一个 Hibernate 工程。 搭建 Hibernate 工程需要以下 7 步&#xff1a; 下载 Hibernate 开发包 新建工程 创建数据库表 创建实体类 创建映射文件 创建 Hibernate 核心配置文件 测试 1. 下载 Hibernate 开发包 浏览器访问 Hibernate 官网 下载 Hibern…

520告白日!小红书关键词热度查询,今年的心动密码是什么?

520&#xff0c;又是一个有爱的日子&#xff0c;人们借机表达爱意的日子&#xff0c;品牌不会错过的好时机。今年520什么东西比较热呢&#xff1f;消费者比较关注什么&#xff1f;品牌有什么样动作&#xff1f;下面&#xff0c;借助小红书关键词热度查询、热词排行榜&#xff0…

基于C3D卷积神经网路的动作识别

对于基于视频分析的问题&#xff0c;2D卷积&#xff08;卷积核为二维&#xff09;不能很好得捕获时序上的信息&#xff0c;因此《3D convolutional neural networks for human action recognition》 这片论文提出了3D卷积并用于行为识别的&#xff0c;论文中将其用于行为识别&a…

商家中心之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

cpp test

1. 以下程序在linux 64位系统的输出结果&#xff08; &#xff09; #include <stdio.h> int main(void) {int buf[100] { 0 };printf("%d,%d,%d,%d,%d",sizeof(int), sizeof(long long), sizeof(buf),sizeof(buf)/sizeof(buf[0]), sizeof(&buf));retur…

【Linux Network】网络层协议——IP

目录 网络层 IP协议 基本概念 协议头格式 网段划分 特殊的IP地址 IP地址的数量限制 私有IP地址和公网IP地址 路由 路由表生成算法 Linux网络编程&#x1f337; 网络层 在复杂的网络环境中确定一个合适的路径&#xff1b; IP协议 基本概念 主机 : 配有 IP 地址 , 但是不进行路…

前端必学,crud,magic-Api

CRUD还要后端写&#xff1f; 前端自己搞定&#xff0c;只需要会写sql就行。 文档写的比我写的好太多&#xff0c;直接看文档 创建springBoot&#xff08;springBoot版本要小于3.0&#xff09; 引入pom <?xml version"1.0" encoding"UTF-8"?> &…

接口自动化——har 生成用例

这里写目录标题 一、目标二、应用场景三、Har 简介四、实现思路五、模板技术六、模版技术-环境安装&#xff08;Python&#xff09;七、har 生成用例实现思路1、python模板模板文件生成的测试文件 2、java模板模板文件生成的测试文件 3、httprunner模板模板文件生成的测试文件 …

数据库界的科技与狠活: 创邻科技Galaxybase X英特尔SGX数据加密解决方案正式发布

引言 近日&#xff0c;创邻科技入选与英特尔合作&#xff0c;在基于第四代英特尔至强处理器的支持下&#xff0c;利用软件防护扩展&#xff08;Software Guard Extension,SGX&#xff09; 技术&#xff0c;打造出了具备可信执行环境的图数据库产品&#xff0c;保护企业释放关联…

STM32单片机多功能电子秤点数秤食物热量卡路里称重

实践制作DIY- GC0132-多功能电子秤 一、功能说明&#xff1a; 基于STM32单片机设计-多功能电子秤 二、功能介绍&#xff1a; STM32F103C系列最小系统lcd1602HX7115Kg电子秤去皮键模式选择按键重量设置键上键下键 有3种模式普通模式、点数模式、卡路里模式。通过模式选…

Requests-get方法的使用

Requests-get方法使用 打开网页使用代码获取页面内容查看结果页面格式修改 爬取书名完整代码以及注释代码注释 翻页查询所有 以https://books.toscrape.com/网站为例&#xff1a; 打开网页 先把网页打开&#xff0c;然后右键检查&#xff0c;找到网络一栏&#xff0c;这个时候…

【Android】【Java】【每日练手1】字符串的二维码生成器

文章目录 一、需求二、创建android stdio工程三、设置好JDK四、创建activity五、编写activity_main.xml布局六、build.gradle增加zxing依赖七、MainActivity 一、需求 一个界面&#xff0c;界面上一个文本输入框、一个按钮、一个二维码显示view。可在文本输入框输入字符串&…

为什么分布函数的概率分布为均匀分布?

为什么分布函数的概率分布为均匀分布&#xff1f; 我先来解决这样一个常见的问题&#xff1a;已知随机变量X的概率分布&#xff0c;求Yg(X)的概率分布&#xff08;分布律或者概率密度函数&#xff09; 这种问题有三种类型&#xff08;如下图&#xff09; 我们以随机变量X为连续…