个人主页:欢迎大家光临——>沙漠下的胡杨
各位大帅哥,大漂亮
如果觉得文章对自己有帮助
可以一键三连支持博主
你的每一分关心都是我坚持的动力
☄: 本期重点:外排序
希望大家每天都心情愉悦的学习工作。
目录
什么叫内排序和外排序
内排序:
内部排序是指待排的记录全部在内存中完成排序的过程,内部排序也称为内排序。若待排序记录的数量庞大,在排序的过程中需要使用到外部存储介质如磁盘等,这种涉及内外存储器数据交换的排序过程称为外部排序,又称为外排序。内排序是外排序的基础,外排序算法的原理和内排序算法的原理在很多方面都类似,但因内存的读写速度与外存的读写速度存在很大差别,因而实际操作中仍有不同。
外排序:
外排序(External sorting)是指能够处理极大量数据的排序算法。通常来说,外排序处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。外排序通常采用的是一种“排序-归并”的策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件。尔后在归并阶段将这些临时文件组合为一个大的有序文件,也即排序结果。
简单来讲,内排序就是全部过程都在内存中完成的,外排序则是需要打开文件进行写入磁盘中,不全是在内存中完成的。常见的排序算法都可以认为为是内排序,而归并排序则可以算是一个外排序。
外排序模拟实现:
我们在前面的知识中已经学会了外排序和文件相关的操作,也对文件有一定的了解啦,下面我们来模拟实现下一个外排序算法吧,我们以100个数据的文件为例子,来进行演示。
外排序的实现流程:
1.首先我们先有一个文件来保存这100个数据。
2.我们要把文件进行切割成小文件,保证每个都能放入内存中进行排序。
3.把每个小文件进行排序,然后准备开始归并。
4.我们把两个文件利用归并排序的思想归并为一个文件,再把这个归并好的文件依次和剩下的文件进行归并,最后生成的文件为有序的文件。
外排序的流程示意图:
简单的示意图如上,下面我们进行详细分析。
外排序代码分析:
创建文件:
首先,我们应该先有一个文件,这个要么是导入的文件,要么是自己创建的文件。
在工程文件夹中,我们创建了sort.txt文件,也可以用其他的后缀。
打开文件并且进行切割。
int main() { //外排序函数,参数为传递的文件 MorgeSortFile("sort.txt"); return 0; }
//切割文件,把文件排序 void CutFile(const char* file) { //打开文件 FILE* fout = fopen(file, "r"); if (fout == NULL) { printf("打开文件失败\n"); exit(-1); } //要切割文件的个数 int n = 10; //切割文件储存的最大值 int a[10]; //看是否切割的文件是否满了 int i = 0; //取文件的值存放的临时位置 int num = 0; //切割后的文件名字 char subfile[20]; //第一个文件的名字 int filei = 1; //把切割文件临时储存的数组内存设置为0. memset(a, 0, sizeof(int)*n); //每次取文件一个到文件结束 while (fscanf(fout, "%d\n", &num) != EOF) { //如果距离取满还差一个时就停止。 if (i < n - 1) { a[i++] = num; } else { //把下一个取出的直接放在数组中进行排序,保证每组刚好满 //并且这个取出的数据没有丢失,否则就会少数据 a[i] = num; //利用快排,排序好写入文件中。 QuickSort(a, n); //生成文件名字。 sprintf(subfile, "%d", filei++); //打开文件 FILE* fin = fopen(subfile, "w"); if (fin == NULL) { printf("打开文件失败\n"); exit(-1); } //把临时数组的元素写文件 for (int i = 0; i < n; i++) { fprintf(fin, "%d\n", a[i]); } //关闭文件 fclose(fin); //把i置为0,数组设置为0,方便下次使用。 i = 0; memset(a, 0, sizeof(int)*n); } } //关闭大文件 fclose(fout); }
然后我们进行两个文件间的归并。
void MorgeSortFile(const char* file) { //分割文件,分割后的文件进行排序 CutFile(file); //合并后的文件名 char mfile[100] = "12"; //初始合并的两个的文件名 char file1[100] = "1"; char file2[100] = "2"; //切割后文件的个数 int n = 10; //循环进行归并成新的文件 for (int i = 2; i <= n; i++) { //归并两个文件,文件1,文件2,和要归并到的文件。 _MergeFile( file1, file2, mfile); //把归并后的文件名拷贝到file1的中,为了进行循环 strcpy(file1, mfile); //把下一个要归并的文件名写入file2中。 sprintf(file2, "%d", i + 1); //输出合并后的文件名 sprintf(mfile, "%s%d", mfile, i + 1); } }
两个文件的归并我们封装为函数:
//归并两个文件 void _MergeFile(const char* file1, const char* file2, const char* mfile) { //打开文件1 FILE* fout1 = fopen(file1, "r"); if (fout1 == NULL) { printf("打开文件失败\n"); exit(-1); } //打开文件2 FILE* fout2 = fopen(file2, "r"); if (fout2 == NULL) { printf("打开文件失败\n"); exit(-1); } //打开文件2 FILE* fin = fopen(mfile, "w"); if (fin == NULL) { printf("打开文件失败\n"); exit(-1); } //取出两个文件中的数据存放到num1和num2中。 int num1, num2; //读取文件1中的数据,放入num1 int ret1 = fscanf(fout1, "%d\n", &num1); //读出文件2中的数据,放入num2 int ret2 = fscanf(fout2, "%d\n", &num2); //但ret1和ret2不为文件结束时继续 while (ret1 != EOF && ret2 != EOF) { if (num1 < num2) { //把num1写入归并后的文件中 fprintf(fin, "%d\n", num1); //再从文件1中读取数据 ret1 = fscanf(fout1, "%d\n", &num1); } else { fprintf(fin, "%d\n", num2); ret2 = fscanf(fout2, "%d\n", &num2); } } //如果ret1不为文件结束,就文件1中的数据插入归并后的文件 while (ret1 != EOF) { fprintf(fin, "%d\n", num1); ret1 = fscanf(fout1, "%d\n", &num1); } while (ret2 != EOF) { fprintf(fin, "%d\n", num2); ret2 = fscanf(fout2, "%d\n", &num2); } //关闭文件 fclose(fout1); fclose(fout2 ); fclose(fin); }
代码整体:
//归并两个文件 void _MergeFile(const char* file1, const char* file2, const char* mfile) { //打开文件1 FILE* fout1 = fopen(file1, "r"); if (fout1 == NULL) { printf("打开文件失败\n"); exit(-1); } //打开文件2 FILE* fout2 = fopen(file2, "r"); if (fout2 == NULL) { printf("打开文件失败\n"); exit(-1); } //打开文件2 FILE* fin = fopen(mfile, "w"); if (fin == NULL) { printf("打开文件失败\n"); exit(-1); } //取出两个文件中的数据存放到num1和num2中。 int num1, num2; //读取文件1中的数据,放入num1 int ret1 = fscanf(fout1, "%d\n", &num1); //读出文件2中的数据,放入num2 int ret2 = fscanf(fout2, "%d\n", &num2); //但ret1和ret2不为文件结束时继续 while (ret1 != EOF && ret2 != EOF) { if (num1 < num2) { //把num1写入归并后的文件中 fprintf(fin, "%d\n", num1); //再从文件1中读取数据 ret1 = fscanf(fout1, "%d\n", &num1); } else { fprintf(fin, "%d\n", num2); ret2 = fscanf(fout2, "%d\n", &num2); } } //如果ret1不为文件结束,就文件1中的数据插入归并后的文件 while (ret1 != EOF) { fprintf(fin, "%d\n", num1); ret1 = fscanf(fout1, "%d\n", &num1); } while (ret2 != EOF) { fprintf(fin, "%d\n", num2); ret2 = fscanf(fout2, "%d\n", &num2); } //关闭文件 fclose(fout1); fclose(fout2 ); fclose(fin); } //切割文件,把文件排序 void CutFile(const char* file) { //打开文件 FILE* fout = fopen(file, "r"); if (fout == NULL) { printf("打开文件失败\n"); exit(-1); } //要切割文件的个数 int n = 10; //切割文件储存的最大值 int a[10]; //看是否切割的文件是否满了 int i = 0; //取文件的值存放的临时位置 int num = 0; //切割后的文件名字 char subfile[20]; //第一个文件的名字 int filei = 1; //把切割文件临时储存的数组内存设置为0. memset(a, 0, sizeof(int)*n); //每次取文件一个到文件结束 while (fscanf(fout, "%d\n", &num) != EOF) { //如果距离取满还差一个时就停止。 if (i < n - 1) { a[i++] = num; } else { //把下一个取出的直接放在数组中进行排序,保证每组刚好满 //并且这个取出的数据没有丢失,否则就会少数据 a[i] = num; //利用快排,排序好写入文件中。 QuickSort(a, n); //生成文件名字。 sprintf(subfile, "%d", filei++); //打开文件 FILE* fin = fopen(subfile, "w"); if (fin == NULL) { printf("打开文件失败\n"); exit(-1); } //把临时数组的元素写文件 for (int i = 0; i < n; i++) { fprintf(fin, "%d\n", a[i]); } //关闭文件 fclose(fin); //把i置为0,数组设置为0,方便下次使用。 i = 0; memset(a, 0, sizeof(int)*n); } } //关闭大文件 fclose(fout); } void MorgeSortFile(const char* file) { //分割文件,分割后的文件进行排序 CutFile(file); //合并后的文件名 char mfile[100] = "12"; //初始合并的两个的文件名 char file1[100] = "1"; char file2[100] = "2"; //切割后文件的个数 int n = 10; //循环进行归并成新的文件 for (int i = 2; i <= n; i++) { //归并两个文件,文件1,文件2,和要归并到的文件。 _MergeFile( file1, file2, mfile); //把归并后的文件名拷贝到file1的中,为了进行循环 strcpy(file1, mfile); //把下一个要归并的文件名写入file2中。 sprintf(file2, "%d", i + 1); //输出合并后的文件名 sprintf(mfile, "%s%d", mfile, i + 1); } } int main() { //外排序函数,参数为传递的文件 MorgeSortFile("sort.txt"); return 0; }
细节分析在注释中啦,今天到这里结束啦!
最后我们看下排序后的文件:
最后一个文件就是有序的。