c语言 - 归并排序的外排序(使用归并排序,排序文件文件当中的数据)

归并排序的外排序

我们在数据结构里面实现的排序方式都是在内存里面进行排序,而当我们要对10G的数据放到硬盘里面,要排序,怎么排呢?我们知道计算机的内存相对于外存(硬盘之类的)而言不是很大,我们要在内存使用我们数据结构里面的排序方法显然不太合适,因为内存根本放不下这组数据。

那么我们想到在硬盘里使用归并排序来实现排序10G的数据,实现思想:

现在我们有10G的数据文件,我们把这个10G的数据文件,分成10个1G的文件,并且让10个1G的文件有序,也就是说,分成10个1G文件之后,就可以用我们原来的排序方法对这个1G的文件进行排序;一次读文件,每一次读1G到内存中用一个数组存放,利用快速排序堆这个数组进行排序,再写到一个文件里面,一次再读下一个文件,同样操作。

直到这10个文件都是有序之后,再在硬盘中对这个10个文件使用归并思想排序,就可以实现10G文件的排序。

那为什么要用归并排序呢?因为其他的排序不能实现,我们在硬盘中读取数据是依次读取数据,而除了归并排序,其他都不是依次遍历数据的,还有用下标访问数据的,文件当然不支持用下标来访问数据。所以此时就只有归并排序比较好实现外存上的排序。

把这个10个1G文件排序好之后,相邻的两个文件为一组依次用归并排序来组合。

1G和1G的文件归并成2G的文件,2G和2G的文件归并成4G的文件·············最后就是8G的和剩下的一个2G的归并10G文件,这个10G的文件就是之前的文件的有序文件。

注:具体的归并排序思想请看这篇文章中的“归并排序”和“归并排序的另一种方式”标题的内容:

数据结构-5(排序)_chihiro1122的博客-CSDN博客

但是归并排序的外排序,至少要有 O(N) 的空间复杂度,因为至少要归并一次,到新的空白里,而且磁盘相比于缓存的速度是很慢的,我们在磁盘上进行操作,这个速度会很慢。

在磁盘上海量的数据一般会是在文件中, 那么我们在磁盘上进行归并排序一般就是在文件中进行归并,如果都借助磁盘上去归并排序,那么会很慢。

我们可以把文件分为多分,一般是看内存能存多少数据,假设有10G文件,那么内存可以容纳1G内容,那么我就把这个文件分为十份:

 然后再像上述一样去归并:

  也就是说,把大文件平均分割为n个小文件,保证每一份小文件都可以加载到内存,那么就可以把每一个小文件加载到内存中,使用快排来把被一个小文件取出来排成有序,这是归并到大文件中的先决条件,然后再访问到小文件中,在把每一个小文件中数据,归并到大文件中去。

但是有一个问题,有n个小文件,那么取名字该如何取呢?具体该如何实现呢?我们可以这样做:

我们就直接读取大文件中的数据,读取到 EOF 就停止,也就是说直接把大文件中的数据读写完,但是我在读写的时候,假设我的每一个小文件中有10个数据,那么我在这个读大文件这个循环中,按照10个数据来进行读写,也就是说,我每读10个数据,我就对这个数据进行快速排序。那么整体实现就是,直接把大文件在大文件内部直接按10个数据大小分为多个区间,然后每一次取出就把这个区间快排为有序区间,然后再继续归并操作。

如图所示:

然后再归并。

 如上图,我们就把 大文件中 以 10 个数据为小区间的 数据 读写到一个一个小文件中去了。

因为小文件要取文件名,那么我们使用sprintf()函数来写到字符串中,:

 如上图所示,我们把文件名写到这个字符串数组中去,然后这个字符串数组就可以作为,open函数打开文件的文件名:

 那么写入文件中,文件中只有字符,那么我们用int写入就会在编译就会乱码,这时我们需要把这个int类型转化为字符串,或者使用fpirntf()函数,这里我们就使用fprintf()函数,比较方便:

 这个使用,我们的一个小文件就排序好了,也写入了,那么这个是,我们需要排序写入下一个小文件,这时我们需要关闭这个文件对象,然后把 i 置为 0 ,这样才能继续在大文件中读 10 个数据;

 但是上述方法有一个bug,就是如果我先放10 个数据,那么此时num 就是这个小区间的第 10个位置,但是我们上述的循环写的是,if 放数据,else 来排序的顺序,那么每一次读数据就有一个依次循环是 拿来 排序的,但是这一次多余的排序循环中又是读数据的,这就导致每一次分割小区间都会有一个数据没有读写排序上,所以我们要这样处理:
就是在 if 中先只取 9 个数据,然后 else 排序之前,我先把第10个数据 取出 放到数组中去,再继续排序:

 上述就是把大文件分割为有序的小文件,然后就是对这些个小文件进行 归并排序到大文件中:

为了考虑文件取名字问题,我们这样进行归并排序:
 

 我们来先是实现一个函数,这个函数把 小文件中的两个文件中数据读取到 一个新文件中去,然后实现单趟的归并排序:

void _MergeFile(const char* file1, const char* file2, const char* mfile)
{
	FILE* font1 = fopen(file1, "r");
	if (font1 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}

	FILE* font2 = fopen(file2, "r");
	if (font2 == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}

	FILE* fin = fopen(mfile, "w");
	if (fin == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}

	int num1, num2;
	int ret1 = fsacnf(font1, "%d\n", &num1) != EOF;
	int ret2 = fsacnf(font1, "%d\n", &num2) != EOF;
	while (ret1 != EOF && ret2 != EOF)
	{
		// 谁小就把这个数据往文件中写
		if (num1 < num2)
		{
			// 谁写入文件谁就 继续往下读
			fprintf(fin, "&d\n", num1);
			ret1 = fsacnf(font1, "%d\n", &num1);
		}
		else
		{
			fprintf(fin, "%d\n", num2);
			ret2 = fsacnf(font1, "%d\n", &num2);
		}
	}

	// 不知道哪一个文件没有读完,那就两个都写一遍,没有的会自动跳出循环
	while (fsacnf(font1, "%d\n", &num1) != EOF)
	{
		fpirntf(fin, "%d\n", num1);
	}

	while (fsacnf(font2, "%d\n", &num1) != EOF)
	{
		fpirntf(fin, "%d\n", num2);
	}
	fclose(font1);
	fclose(font2);
	fclose(fin);
}

 然后实现总体的归并排序:

void MergeSortFile(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 = 0;

	// 初始化
	memset(a, 0, sizeof(int) * n);
	// 假设大文件中的每一个数据 通过 \n 来区分
	// 读数据
	while (fsacnf(fout, "%d\n", &num) != EOF)
	{
		if (i < n - 1)
		{
			a[i++] = num;
		}
		else
		{
			a[i] = num;
			// 快速排序 先对每一个小文件进行排序
			QuickSort(a, 0, n - 1);
			// 在 sub文件夹下写文件
			sprintf(subfile, "sub//%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;
			memset(a, 0, sizeof(int) * n);
		}
	}

	// 利用互相归并到文件,实现整体有序
	char mfile[100] = "12";
	char file1[100] = "1";
	char file2[100] = "2";

	for (int i = 0; i <= n; i++)
	{
		// 读取file1 和file2 归并出 mfile
		_MergeFile(file1, file2, mfile);

		strcpy(file1, mfile);
		spirntf(file2, "%d", i + 1);
		sprintf(mfile, "%s%d", mfile, i + 1);
	}

	fclose(fout);
}

 上述就是实现了 归并排序在 文件当中的使用。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chihiro1122

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值