外排序(C语言实现)

news2025/1/16 14:05:11

前言

本篇博客讲解一下外排序,看这篇排序你的先去看一下:八大经典排序算法-CSDN博客

💓 个人主页:普通young man-CSDN博客

⏩ 文章专栏:排序_普通young man的博客-CSDN博客

      若有问题 评论区见📝

🎉欢迎大家点赞👍收藏⭐文章

      

目录

快速回忆快速排序和归并排序

函数接口回顾

fscanf/fprintf/sscanf/sprintf

1. fscanf

2. fprintf

3. sscanf

4. sprintf

外排序详解

1. 内存限制

2. 提高效率与可管理性

3. 算法适用性

代码

代码中函数的作用

. 基础函数定义

2. 快速排序算法

3. 文件归并排序

注意


  在本文中,我们将深入探讨如何使用C语言实现快速排序算法,并将其应用于大文件的排序问题上,通过文件归并的方式处理大数据量的排序需求。这不仅是一个理论知识的应用,也是解决实际问题的一个实例。

快速回忆快速排序和归并排序

八大经典排序算法-CSDN博客文章浏览阅读1k次,点赞23次,收藏23次。深入探讨了八大排序算法——冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、以及我们刚刚详析的计数排序之后,我们不仅掌握了一系列解决排序问题的有效策略,更深刻理解了算法设计背后的逻辑与权衡。每种算法,如同八音盒中的音符,各有其独特的旋律与应用场景,它们共同编织了计算机科学领域中关于“排序”这一基本问题的华丽乐章。数据结构-堆(带图)详解-CSDN博客。栈(Stack)是一种基本的数据结构,其特点是只允许在同一端进行插入和删除操作,这一端被称为栈顶。_排序算法https://blog.csdn.net/2302_78381559/article/details/139837523?spm=1001.2014.3001.5501这个博客有这两个排序算法的一个讲解

函数接口回顾

fscanf/fprintf/sscanf/sprintf

1. fscanf

fscanf 函数用于从指定的文件中读取数据并根据特定格式解析。它允许你按照预定义的格式从文件中读取各种类型的数据,如整数、浮点数或字符串等。

原型:

int fscanf(FILE *stream, const char *format, ...);
  • 参数:

    • stream: 指向需要读取的文件的文件指针。
    • format: 一个控制字符串,用于指定输入数据的格式。
    • ...: 可变参数列表,对应于格式字符串中定义的数据类型的地址。
  • 返回值: 成功读取并转换的项目数量,如果遇到文件结束或者读取错误则返回EOF。

2. fprintf

fprintf 函数用于将数据按照指定的格式输出到一个文件中。它与printf类似,但输出目标是文件而非标准输出。

原型:

int fprintf(FILE *stream, const char *format, ...);
  • 参数:

    • stream: 指向要写入的文件的文件指针。
    • format: 控制输出格式的字符串。
    • ...: 与格式字符串匹配的变量列表。
  • 返回值: 成功写入的字符数量,若发生错误则返回负值。

3. sscanf

sscanf 函数用于从字符串中读取数据,与fscanf类似,但它的输入源是一个字符串而不是文件。

原型:

int sscanf(const char *str, const char *format, ...);
  • 参数:

  • ...: 存储读取数据的变量地址列表。

    • format: 指定如何解析字符串的格式控制符。
    • str: 要读取的字符串。
    • 返回值: 成功读取的输入项数量。

4. sprintf

sprintf 函数用于将格式化的数据写入到一个字符串中,类似于printf,但是输出目标是一个字符数组。

原型:

int sprintf(char *str, const char *format, ...);
  • 参数:

    • str: 目标字符串的地址,写入格式化后的数据。
    • format: 格式字符串,定义输出数据的格式。
    • ...: 一系列变量,与格式字符串中的占位符对应。
  • 返回值: 写入到字符串中的字符数量,不包括结尾的空字符\0

外排序详解

我们先看一下思想:

通过这个图我们可以看到是我们先要将文件的数据分成10等份将每一个等份的文件里的数据排序,然后再将10个 文件进行归并,这样所有数据就排好了

为什么要分等份排序嘞?

1. 内存限制

最直接的原因是计算机内存的限制。对于非常大的数据集,一次性将所有数据载入内存进行排序通常是不可行的。操作系统为每个进程分配的内存空间有限,超出这个限制会导致内存溢出错误。因此,通过将大文件切分为多个小文件,可以确保每个小文件都能在内存中进行高效排序,利用快速排序等算法完成局部排序。

2. 提高效率与可管理性

  • 减少磁盘I/O操作:频繁的磁盘读写是影响程序性能的主要因素之一。将大文件分割成小文件,使得每个小文件可以较快地被读入内存进行处理,减少了整体的磁盘读写次数,提高了效率。
  • 并行处理机会:分片后的小文件可以并行排序,尤其在多核处理器或多计算机系统中,每个小文件可以由不同的处理器或机器独立处理,大大加快了排序速度。

3. 算法适用性

快速排序、归并排序等高效的排序算法在小规模数据集上表现优异,但在大规模数据集上直接应用会受到内存限制。分块排序后,每一块数据量较小,可以更好地利用这些算法的优势。


代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//交换
void Swap(int* p1,int* p2) {
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//三数取中
int GetMidIndex(int* a,int left,int right) {
	//计算mid
	int mid = (left + right) / 2;

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

}


//快排
void QuickSort(int* a,int left, int right) {
	assert(a);
	if (left >= right)
	{
		return;
	}
	int Midindex = GetMidIndex(a,left,right);

	Swap(&a[Midindex], &a[left]);

	//前后指针
	int prev = left;
	int cur = left+1;
	int keyi = left;

	//循环
	while (cur <= right)
	{
		if (a[keyi] > a[cur] && ++prev != cur)
			Swap(&a[cur],&a[prev]);
		++cur;
	}
	//交换prev和keyi,得出keyi
	Swap(&a[keyi], &a[prev]);

	//分治 [left-keyi-1]  keyi [keyi+1-right]
	keyi = prev;
	QuickSort(a, left, keyi - 1);
	QuickSort(a, keyi+1, right);
}

//文件归并
void _MergeSortFile(const char* file1,const char* file2,const char* mfile) {
	//打开第一个文件
	FILE* four1 = fopen(file1, "r");
	if (four1 == NULL)
	{
		assert("file1:打开文件失败\n");
	}
	//打开第二个文件
	FILE* four2 = fopen(file2, "r");
	if (four2 == NULL)
	{
		assert("file2:打开文件失败\n");
	}
	//创建归并文件
	FILE* fin = fopen(mfile, "w");
	if (fin == NULL)
	{
		assert("mfile:打开文件失败\n");
	}


	//进行归并
	int num1, num2;
	int ret1 = fscanf(four1, "%d\n", &num1);
	int ret2 = fscanf(four2, "%d\n", &num2);

	while (ret1 != EOF && ret2 != EOF)
	{
		if (num1 < num2) {
			fprintf(fin, "%d\n", num1);
			ret1 = fscanf(four1, "%d\n", &num1);
		}
		else //num1 > num2
		{
			fprintf(fin, "%d\n", num2);
			ret2 = fscanf(four2, "%d\n", &num2);
		}
	}
	//将剩余数据放进归并文件
	while (ret1 != EOF)
	{
		fprintf(fin, "%d\n", num1);
		ret1 = fscanf(four1, "%d\n", &num1);
	}
	while (ret2 != EOF)
	{
		fprintf(fin, "%d\n", num2);
		ret2 = fscanf(four2, "%d\n", &num2);
	}
	
	//关闭文件
	fclose(fin);
	fclose(four1);
	fclose(four2);
}
void MergeSortFile(const char* file) {

	//导入文件数据
	FILE* four = fopen(file, "r");
	if (four == NULL)
	{
		assert("MergeSortFile:fopen");
	}

	//定义变量
	int a[10] = {0};//分组数组
	int n = sizeof(a) / sizeof(a[0]);//分组大小
	int num = 0;//指针指向数据
	char subfile[20];//存储文件名的指针
	int filei = 1;//文件名编号
	int i = 0;//数组下标
	while (fscanf(four,"%d\n",&num) != EOF)
	{
		if (i < n-1) {
			a[i++] = num;//进入8个数据
		}
		else
		{
			a[i] = num;
			QuickSort(a, 0, n - 1);
 			sprintf(subfile,"sub\\sub_sort%d", filei++);
			//创建文件
			FILE* sub_fin =  fopen(subfile, "w");
			if (sub_fin == NULL)
			{
				assert("sub_fin:fopen");
			}
			//将数据写入文件
			for (int j = 0; j < n; j++)
			{
				fprintf(sub_fin, "%d\n", a[j]);
			}
			fclose(sub_fin);

			//初始化一些数据,方便下一次数据写入
			i = 0;
			memset(a, 0, sizeof(int)*n);
		}
	}
	
	//对十个文件进行归并操作
	char file1[100] = "sub\\sub_sort1";//第一个文件
	char file2[100] = "sub\\sub_sort2";//第二个文件
	char mfile[100] = "sub\\sub_sort12";//两个文件归并后存放的位置
	for (int k = 2; k <= n; k++)
	{
		//归并
		_MergeSortFile(file1,file2,mfile);
		//改变file1的位置到mfile
		strcpy(file1, mfile);
		//file2向后走
		sprintf(file2, "sub\\sub_sort%d", k+1);
		//改变mfile文件名,使他在下一次循环创建一个新的文件
		sprintf(mfile,"%s%d",mfile, k+1);
	}
	printf("排序成功\n");
	fclose(four);
	
}
int main()
{
	MergeSortFile("SortData.txt");
	//int arr[] = { 3,5,6,8,10,12,58,1,8,7 };
	//int sz = sizeof(arr) / sizeof(arr[0]);
	//QuickSort(arr, 0, sz - 1);
	//for (int i = 0; i < sz; i++)
	//{
	//	printf("%d ", arr[i]);
	//}
	return 0;
}

代码中函数的作用

. 基础函数定义

  • Swap:用于交换两个整型指针所指向的值。
  • GetMidIndex:实现了“三数取中”策略,用于在数组的一段范围内找出中位数的索引,以优化快速排序的性能。它通过比较数组两端和中间三个元素的值来决定返回哪个索引。

2. 快速排序算法

  • QuickSort:实现快速排序的核心逻辑。首先调用GetMidIndex选取基准元素,通过一次遍历来将数组分为两部分,一部分小于基准,另一部分大于基准,然后递归地对这两部分继续进行快速排序。此过程确保了最终数组的升序排列。

3. 文件归并排序

  • _MergeSortFile:负责两个已排序文件的归并操作。它打开两个输入文件,创建一个输出文件,然后逐行读取两个文件中的数字,比较后将较小的数字写入输出文件,直到某个文件读完。之后,将剩余文件的全部内容追加到输出文件末尾,最后关闭所有文件。
  • MergeSortFile:这是主函数,用于处理大文件的排序。它首先打开原始数据文件,然后分批读取数据到数组a中,每满一组就调用QuickSort排序,将排序后的数据写入到名为sub_sortX的小文件中。之后,通过循环调用_MergeSortFile函数,两两归并这些小文件,最终得到一个完全有序的大文件。此过程动态更新文件名,确保每次归并产生的新文件都能正确参与后续归并。

注意

大家看这儿可能会疑惑,为什么要这么写?

你们可能会想为什么我不这样写

其实这样写num会有一个吞数据行为

这个是我在小本本上写的,字比较撇哈,不过能帮大家解决疑惑就行,从这个图我们可以就看出,每一次循环我们的num都会吞掉一个数据,走十次就会吞掉十个数据,这样的话,我们就只会有9个文件

照成这个原因:

1,后置++

2,fscanf每调用一次指针都会向后走

所以改进了一下这个写法

我们先让数据进去8个,最后一个数据在排序之前放进去,就不会出现这种情况了

好了今天的博客就到这里了,希望能给大家解决到问题,哈哈哈

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

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

相关文章

【七】【QT开发应用】跨UI发送信号,跨线程发送信号

跨UI发送信号 基本框架 新建窗口 自定义信号 跨线程发送信号 新建线程 查看线程号 完整代码 跨UI发送信号 setdialog.h #ifndef SETDIALOG_H #define SETDIALOG_H#include <QDialog>namespace Ui { class setdialog; }class setdialog : public QDialog {Q_OBJECTpub…

QT实现QGraphicsView绘图 重写QGraphicsSvgItem类实现边框动画

目录导读 简述使用 QTimer 实现 QGraphicsSvgItem 边框动画效果 简述 在了解学习WPS的流程图的时候&#xff0c;发现它这个选择图元有个动态边框效果&#xff0c;而且连接线还会根据线生成点从头移动到尾的动画。像这种&#xff1a; 在QML中实现这种动画属性很简单&#xff0…

物联网系统运维——移动电商服务器单点部署,web服务器部署,Nginx Web服务介绍,Nginx性能,部署,架构,及实验:安装并设置Nginx(重点)

一.web服务器介绍 Web服务器一般指网站服务器&#xff0c;是指驻留于因特网上提供某种特定类型计算机的程序&#xff0c;Web服务器可以向浏览器等Web客户端提供文档&#xff0c;也可以放置网站文件&#xff0c;让全世界浏览&#xff0c;可以放置数据文件&#xff0c;让全世界下…

七种常见的前端攻击

随着网络应用程序在商业运作中的重要性日益增加&#xff0c;它们也成为了网络攻击的更具吸引力的目标。不幸的是&#xff0c;与后端和 DevOps 团队相比&#xff0c;许多前端开发人员在构建安全前端方面已经落后了。这种差距增加了破坏性数据泄露的风险。 最近的事件&#xff0…

Linux_软硬链接

目录 1、软链接 2、软链接的使用方式 3、软链接的删除 4、硬链接 5、硬链接的使用方式 6、软硬链接的使用场景 7、软硬链接的区别 结语 前言&#xff1a; 在Linux操作系统中&#xff0c;有软链接和硬链接&#xff0c;他们是一种特殊的文件引用&#xff0c;主要用于与…

AGV机器人的调度开发分析(1)- 内核中的路线规划

准备开始写一个系列&#xff0c;介绍下AGV机器人的调度的开发和应用。 按照openTCS的核心内容&#xff0c;国内多家广泛应用于AGV的调度。那么架构图如下&#xff1a; Kernel中有一个是Routing&#xff0c;这是路由规划模块&#xff0c;需要实现的细节功能包括如下&#xff1a…

理解什么是DSR,嗅探器视角下的IP和MAC地址识别(C/C++代码实现)

网络嗅探器是监控和分析网络流量的一种工具&#xff0c;它能够捕获数据包并提取出关键的信息&#xff0c;比如IP地址和MAC地址。 网络嗅探器工作原理基于网卡的工作模式。正常情况下&#xff0c;网卡只处理发送给它的数据包&#xff0c;忽略其他数据。但是&#xff0c;如果将网…

Java程序之简单“记事本”

要求&#xff1a;如下图所示&#xff0c;记事本具有新建、打开文本、保存、关闭等功能。 算法思路&#xff1a; 这是一个使用Java Swing库创建的简单文本编辑器。它包含一个菜单栏&#xff0c;其中包含“文件”菜单以及四个子菜单项&#xff1a;“新建”、“打开”、“保存”和…

机器学习课程复习——集成学习

1. 基本概念 1.1. 定义 通过构建并结合多个个体学习器来完成学习任务,获得比单一学习器显著优越的泛化性能。 1.2. 分类 名称个体学习器例子同质集成基学习器Boosting、Bagging异质集成组件学习器Stacking1.3. 研究的核心 个体学习器的“准确性”和“多样性”本身就存在冲…

二叉树经典OJ练习

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 二叉树经典OJ练习 收录于专栏【数据结构初阶】 本专栏旨在分享学习数据结构学习的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 前置说…

三、MyBatis实践:提高持久层数据处理效率

三、MyBatis实践&#xff1a;提高持久层数据处理效率 目录 一、Mybatis简介 1.1 简介1.2 持久层框架对比1.3 快速入门&#xff08;基于Mybatis3方式&#xff09; 二、MyBatis基本使用 2.1 向SQL语句传参 2.1.1 mybatis日志输出配置2.1.2 #{}形式2.1.3 ${}形式 2.2 数据输入 2…

cesium 包络线

cesium 包络线 以下为源码直接复制可用 1、实现思路 通过turf.js中union方法来计算包络线官方地址:https://turfjs.fenxianglu.cn/ 闪烁线请查看cesium轨迹线(闪烁轨迹线) 2、示例代码 <!DOCTYPE html> <html lang="en"&g

SpringBoot配置第三方专业缓存技术Memcached 下载 安装 整合测试 2024年5000字详解

Memcached下载和安装 是一个国内使用量还是比较大的技术 打开文件夹 我们需要在命令行窗口启动 注意要以管理员方式运行 先尝试进入指定文件 然后又再次运行 下载 memcached.exe -d install 启动 memcached.exe -d start 停止 memcached.exe -d stop memcached.exe -d i…

Mysql数据库约束的概述 , 逐渐约束 , 主键自增 , 表关系的概念和外键 ,多表关系约束介绍和使用

约束和表设计 1、DQL查询语句-limit语句(掌握) 目标 能够掌握limit语句的使用 讲解 作用&#xff1a; LIMIT是限制的意思&#xff0c;所以LIMIT的作用就是限制查询记录的条数。 LIMIT语句格式: select * from 表名 limit offset, row_count; mysql中limit的用法&#…

轻松恢复丢失数据EasyRecovery你的数据守护神

数据丢失&#xff1f;别怕&#xff01;EasyRecovery来帮忙 大家好呀&#xff0c;今天我要分享一个我超级喜欢的数据恢复软件——EasyRecovery&#xff01;&#x1f389; 如果你也经历过误删文件、硬盘格式化或是意外丢失重要数据的尴尬和焦虑&#xff0c;那你一定要看看这个神器…

Bytebase 对接本地部署的 llama3 开启ChatSQL功能

Bytebase 是为开发人员、测试、DBA和运维工程师构建的数据库 DevOps 领域的&#xff0c;类 GitLab/GitHub 平台。 这篇文章主要关注 Bytebase SQL 编辑器中的 AI 增强功能。使用此功能您可以使用自然语言在 Bytebase SQL 编辑器中查询数据库。同时还能给出针对查询的索引建议&…

千呼新零售2.0【更新日志】持续更新ing

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货等连锁店使用。 详细介绍请查看下…

【CSS in Depth 2 精译】1.5 渐进式增强

文章目录 1.5 渐进式增强1.5.1 利用层叠规则实现渐进式增强1.5.2 渐进式增强的选择器1.5.3 利用 supports() 实现特性查询启用浏览器实验特性 1.5 渐进式增强 要用好 CSS 这样一门不断发展演进中的语言&#xff0c;其中一个重要的因素就是要与时俱进&#xff0c;及时了解哪些功…

HTML静态网页成品作业(HTML+CSS)——动漫猪猪侠网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

测试辅助工具(抓包工具)的使用4 之 断点

抓包作用3&#xff08;绕过界面限制测试&#xff09; 1.为什么要绕过界面限制做测试&#xff1f; 原因&#xff1a;界面限制导致部分异常数据无法输入 2.如何绕过界面限制做测试&#xff1f; 绕过界面限制直接测试服务器 步骤&#xff1a; 1.设置断点 2.修改请求 3.修改响应…