当前位置: 首页 > news >正文

【外排序】--- 文件归并排序的实现

 Welcome to 9ilk's Code World

       

(๑•́ ₃ •̀๑) 个人主页:        9ilk

(๑•́ ₃ •̀๑) 文章专栏:     数据结构  


我们之前学习的八大排序:冒泡,快排,插入,堆排等都是内排序,这些排序算法处理的都是在内存中的数据,如果我们要处理的数据量过大,不能一次性装进内存中呢?此时我们需要了解我们的新知识 --- 外排序


🏠 什么是外排序

📒 外排序介绍

外排序(External sorting)是指能够处理极⼤量数据的排序算法。

外排序通常采⽤的是⼀种“排序-归并”的策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到⼀个临时文件,依此进行,将待排序数据组织为多个有序的临时文件。然后在归并阶段将这些临时文件组合为⼀个大的有序文件,也即排序结果。

注意:

1. 通常来说,外排序处理的数据不能一次性装入内存,只能放在读写较慢的外存储器(通常是硬盘上)。

2. 我们之前常见的排序是内排,它们排序思想适应的是数据在内存中支持随机访问。归并排序的思想不需要随机访问数据,只需要依次序列读取数据,所以归并排序既是一个内排序,也是一个外排序

📒 文件归并排序思路分析

   也许有朋友看到文件归并排序会想到,将一个大文件先细分成n个小文件,再各自各自地进行归并,如下图:

这种做法是可行的是但是比较麻烦的是一开始划分小文件的个数以及每个小文件的大小,实现较为复杂,因此我们选择如下的归并思路:

  1.  读取n个值排序后写到file1,再读取n个值排序后写到file2
  2. file1和file2利⽤归并排序的思想,依次读取⽐较,取⼩的尾插到mfile,mfile归并为⼀个有序⽂件
  3. 将file1和file2删掉,mfile重命名为file1
  4. 再次读取n个数据排序后写到file2
  5. 继续⾛file1和file2归并,重复步骤2,直到⽂件中⽆法读出数据。最后归并出的有序数据放到了file1中
动图演示:
这种做法就不需要我们自己去确定划分文件的个数,同时文件大小读取数据固定,而且归并过程中操作文件只有file1 file2这两个文件。

🏠 文件归并排序代码实现

📌 生成大文件

这里我们需要复习C语言库里面的几个关于文件的接口:

fopen  : 打开文件,返回一个FILE*的指针。(const char * filename, const char * mode)

fclose : 关闭文件。( FILE * stream)

fprintf : 将格式化数据写进文件。(FILE * stream, const char * format, ...)

fscanf : 从文件中读取格式化数据。(FILE * stream, const char * format, ...)

具体用法可自行查文档哦 ~

参考代码:

void CreateData()
{FILE* fin = fopen("data.txt", "w");if (fin == NULL){perror("fopen fail");return;}const int N = 1000000;srand(time(0));//读入N个数 随机数for (int i = 0; i < N; i++){int num = rand() + i;fprintf(fin, "%d\n", num);}fclose(fin);
}

📌 小文件输入数据并排序

    我们可以利用一个容器利用fscanf从bigfile里读取数据,再用fprintf将读取的数据排序后输入到目标小文件内

//分别读入数据进file1和file2并且排序
void RDatatoSortFile(const char* file1,FILE* bigfile,int n)
{vector<int> v;v.resize(n);FILE* file = fopen(file1, "w");if (file == NULL){perror("fopen fail");return;}//从bigfile读取n个数据进数组for (int i = 0; i < n; i++){int num = 0;fscanf(bigfile, "%d", &num);v.push_back(num);}
//排序sort(v.begin(), v.end());//将数据读入filefor (int i = 0; i < n; i++){fprintf(file, "%d\n", v[i]);}fclose(file);fclose(bigfile);}

注意:

  • 当bigFile里数据要读取完时,读取数据个数可能不足n个数据,这时我们可以利用fscanf的返回值(读到文件末尾->EOF)和一个变量j来判断是否读完了。
  • 同时我们可以修改返回类型为int,当返回0说明文件都读完了。

修改代码:

//分别读入数据进file1和file2并且排序
int RDatatoSortFile(const char* file1,FILE* bigfile,int n)
{vector<int> v;v.resize(n);FILE* file = fopen(file1, "w");if (file == NULL){perror("fopen fail");return;}//从bigfile读取n个数据进数组int j = 0;for (int i = 0; i < n; i++){int num = 0;num = fscanf(bigfile, "%d", &num);if (num == EOF)break;v.push_back(num);j++;}//数据读完提前返回if (j == 0)return 0;sort(v.begin(), v.end());//将数据读入filefor (int i = 0; i < j; i++){fprintf(file, "%d\n", v[i]);}fclose(file);return j;
}

注意:输入完数据后不能先fclose(bigfile),这样会导致bigfile文件指针读到了文件末尾,后续输入不进数据因为一直在文件末尾。

📌 合并file1和file2为mfile

  读取file1和file2内的数据,小的先进mfile继续读取这个文件;最后一定有个文件先读完,将剩下的文件继续读完。

参考代码:

void mergeFile(const char* file1,const char* file2,const char* mfile)
{FILE* File1 = fopen(file1, "r");if (file1 == NULL){perror("fopen fail");return;}FILE* File2 = fopen(file2, "r");if (file2 == NULL){perror("fopen fail");return;}FILE* Mfile = fopen(mfile, "w");if (mfile == NULL){perror("fopen fail");return;}//fscanf读取数据成功都返回的是1 错误返回的是-1 所以要另外开两个变量接收int x1 = 0;int ret1 = 0;ret1 = fscanf(File1, "%d", &x1);int ret2 = 0;int x2 = 0;ret2 = fscanf(File2, "%d", &x2);while (ret1 != EOF && ret2 != EOF){if (x1 <x2){//读进mfilefprintf(Mfile, "%d\n", x1);ret1 = fscanf(File1, "%d", &x1);}else{fprintf(Mfile, "%d\n", x2);ret2 = fscanf(File2, "%d", &x2);}}//没有读完的继续while (ret1 != EOF){fprintf(Mfile, "%d\n", x1);ret1 = fscanf(File1, "%d", &x1);}while (ret2 != EOF){fprintf(Mfile, "%d\n", x2);ret2 = fscanf(File2, "%d", &x2);}//关闭文件fclose(File1);fclose(File2);fclose(Mfile);
}

注意:在从大文件读取数据时,要注意的是,fscanf读取数据成功返回的都是1,因此我们需增设两个变量接收读取到的值。

📌总体逻辑

  • 造大文件
  • 分别从大文件读取数据排序好后输入进file1和file2
  • 将file1和file2合并为mfile
  • 删除file1 file2 ,重命名mfile为file1,继续输入数据进file2
  • 不断重复上述过程直到bigfile数据读取完毕

总体参考代码:

//创建大文件
void CreateData()
{FILE* fin = fopen("data.txt", "w");if (fin == NULL){perror("fopen fail");return;}const int N = 100;srand(time(0));//读入N个数 随机数for (int i = 0; i < N; i++){int num = rand() + i;fprintf(fin, "%d\n", num);}fclose(fin);
}//分别读入数据进file1和file2并且排序
int RDatatoSortFile(const char* file1,FILE* bigfile,int n)
{int num = 0;vector<int> v;//注意先提前开空间 因为后面push_back是继续开空间!FILE* file = fopen(file1, "w");if (file == NULL){perror("fopen fail");return 0;}//从bigfile读取n个数据进数组int j = 0;for (int i = 0; i < n; i++){if (fscanf(bigfile, "%d", &num) == EOF)break;v.push_back(num);j++;}//数据读完提前返回if (j == 0)return 0;sort(v.begin(), v.end());//将数据读入filefor (int i = 0; i < j; i++){fprintf(file, "%d\n", v[i]);}fclose(file);//fclose(bigfile);return j;
}void mergeFile(const char* file1,const char* file2,const char* mfile)
{FILE* File1 = fopen(file1, "r");if (file1 == NULL){perror("fopen fail");return;}FILE* File2 = fopen(file2, "r");if (file2 == NULL){perror("fopen fail");return;}FILE* Mfile = fopen(mfile, "w");if (mfile == NULL){perror("fopen fail");return;}//fscanf读取数据成功都返回的是1 错误返回的是-1 所以要另外开两个变量接收int x1 = 0;int ret1 = 0;ret1 = fscanf(File1, "%d", &x1);int ret2 = 0;int x2 = 0;ret2 = fscanf(File2, "%d", &x2);while (ret1 != EOF && ret2 != EOF){if (x1 <x2){//读进mfilefprintf(Mfile, "%d\n", x1);ret1 = fscanf(File1, "%d", &x1);}else{fprintf(Mfile, "%d\n", x2);ret2 = fscanf(File2, "%d", &x2);}}//没有读完的继续while (ret1 != EOF){fprintf(Mfile, "%d\n", x1);ret1 = fscanf(File1, "%d", &x1);}while (ret2 != EOF){fprintf(Mfile, "%d\n", x2);ret2 = fscanf(File2, "%d", &x2);}//关闭文件fclose(File1);fclose(File2);fclose(Mfile);
}int main()
{//CreateData();FILE* bigfile = fopen("data.txt", "r");if (bigfile == NULL){perror("fopen faile");}const char* file1 = "file1.txt";const char* file2 = "file2.txt";const char* mfile = "mfile.txt";int n = 0;cin >> n;//1.先分别将数据读入file1和file2RDatatoSortFile(file1,bigfile,n);RDatatoSortFile(file2, bigfile, n);//2.将file1和file2内容合并进mfile //3.将file1和file2删除 mfile改为file1while (1){mergeFile(file1, file2, mfile);// 删除file1和file2                                                                                                                                                                                                                    remove(file1);remove(file2);//重命名mfile为file1rename(mfile, file1);//归并mfile(file1)和fle2 判断是否读完int num = 0;num = RDatatoSortFile(file2, bigfile, n);if (num == 0)break;}fclose(bigfile);return 0;
}

总结:本节我们学习了用于排序内存不能一次性处理的数据量的排序 --- 外排序,同时根据外排序的原理设计了文件归并排序。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 实验5-10 使用函数统计指定数字的个数
  • VGA接口驱动设计验证
  • 现代前端架构介绍(第二部分):如何将功能架构分为三层
  • C#中的Winform基础
  • java学习--泛型
  • yum仓库的制作与使用
  • 谷歌开源最强端侧小模型:2B参数越级跑赢GPT-3.5-Turbo,苹果15Pro运行飞快
  • 云计算 docker 管理镜像和容器
  • python pip怎么安装包
  • O’Reilly
  • 人工智能的“智能”本质
  • 开源:LLMCompiler高性能工具调用框架
  • vLLM初识(一)
  • Milvus Cloud向量数据库如何实现高可用
  • 科普文:微服务之分布式链路追踪SkyWalking单点服务搭建
  • (三)从jvm层面了解线程的启动和停止
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • Brief introduction of how to 'Call, Apply and Bind'
  • eclipse的离线汉化
  • iOS编译提示和导航提示
  • Kibana配置logstash,报表一体化
  • log4j2输出到kafka
  • Next.js之基础概念(二)
  • PHP CLI应用的调试原理
  • Python语法速览与机器学习开发环境搭建
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • Spring框架之我见(三)——IOC、AOP
  • 大快搜索数据爬虫技术实例安装教学篇
  • 今年的LC3大会没了?
  • 离散点最小(凸)包围边界查找
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 容器服务kubernetes弹性伸缩高级用法
  • 十年未变!安全,谁之责?(下)
  • 使用SAX解析XML
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 小试R空间处理新库sf
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • gunicorn工作原理
  • # 达梦数据库知识点
  • #### go map 底层结构 ####
  • #《AI中文版》V3 第 1 章 概述
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (6) 深入探索Python-Pandas库的核心数据结构:DataFrame全面解析
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (Python第六天)文件处理
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (每日一问)基础知识:堆与栈的区别
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (原創) 如何安裝Linux版本的Quartus II? (SOC) (Quartus II) (Linux) (RedHat) (VirtualBox)
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (轉)JSON.stringify 语法实例讲解
  • .net mvc部分视图
  • .NET 反射的使用