0
点赞
收藏
分享

微信扫一扫

海量数据排序—外排序之归并实现外排序排序

以前干嘛去了 2022-04-24 阅读 100
c语言c++

目录

一,什么是外排序

二,准备工作

三,归并排序文件写法

1.准备

2,分割字文件

1,把大文件分割成子文件。

2,分割代码

2,对分割好的子文件取排序,

3.给子文件取名字    1,2,3,4,5,6,7....这样子。

4,放数据到子文件

5,迭代,剩下的数据依次放入不同子文件

三,子文件进行归并。

1.画图:

2.归并子函数:也就是一次归并

3.归并代码:

4.完整代码

四,结果展示

五,代码链接


一,什么是外排序

1.其实外排序就是在文件中进行排序,那我们知道在文件中访问数据有很多限制,比如不可以随机访问啊,只能通过文件指针访问啊,等等。那我们为什么还要在文件中排序也就是外排序呢?

【1.】数据量过大,比如海量数据:举个例子,比如又100亿整形需要你排序,我们算一算100亿个整形放在内存中要多大空间呢。

1KB=1024byte;

1MB=1024KB:

1G=1024MB;

我们知道一个整形是4个字节,也就是4byte;

1024*1024*1024大概估计是10亿的样子,也就是说1G内存大概就只能运行10亿字节的样子。

而100亿个整形是400亿个字节,全部加载到内存中的话将需要40个G的内存,一般的电脑根本没有,就算是公司的操作系统也是要跑其他很多的程序的,这个代价显然是太大了,并且是没有的,更何况如果数据更大呢?,加内存是不能解决这类问题的。

二,准备工作

先来一个快排:QuickSort(int* a, int left, int right):

快排写法前面总结有的,这里就不介绍了。

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

int GetMidIndex(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid])
	{
		if (a[mid] < a[end])
			return mid;
		else if (a[begin] > a[end])
			return begin;
		else
			return end;
	}
	else // a[begin] > a[mid]
	{
		if (a[mid] > a[end])
			return mid;
		else if (a[begin] < a[end])
			return begin;
		else
			return end;
	}
}

void QuickSort(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
		return;

	int midIndex = GetMidIndex(a, left, right);
	Swap(&a[midIndex], &a[right]);

	int prev = left - 1;
	int cur = left;
	int keyindex = right;

	while (cur < right)
	{
		if (a[cur] < a[keyindex] && ++prev != cur)
			Swap(&a[prev], &a[cur]);

		++cur;
	}
	Swap(&a[++prev], &a[keyindex]);

	int div = prev;

	QuickSort(a, left, div - 1);
	QuickSort(a, div + 1, right);

}

三,归并排序文件写法

1.准备

我们写的是外排序嘛,那就是在文件中排序,我们先准备一个文件,里面放入100个数据,这里我们只是进行模拟,不需要真的放100亿个数据,哈哈。文件里面数据大小随便写,但是数据的格式一定要统一,不然后面文件读取不好搞了就。

2,分割字文件

这里其实有俩个步骤我们和在一起搞了。

1,把大文件分割成子文件。

这里我们有100个数据,我们按照10个一组的去分为是个。

2,分割代码

 【注意1】

这里为什么放了n-1个呢,因为第n个放到else里面放了

原因如下:

因为在while里面的从文件中读取数据,他读取了之后,文件指针就自己指向下一个数据了,我们是无法控制的。这就会导致如下原因:如果这里看不清的话,后面会有完整的代码的,这里只是分为子文件+子文件排序代码。

原因:为什么if那里不直接读n个数据。


            这里结合前面仔细看,比较难想
            错误原因:
            不然if哪里直接小于n的话,其实n个数据确实读进去了,但是又读了一个数据就要走else,进行排序,排好序之后
            while哪里又读了一个开始放第二个文件,那么这个本来该数据第二个文件的数据就被跳过了,没有放入第二个文件。
            排序出来的数据会丢失,分组也不正确。

2,对分割好的子文件取排序,

利用快排去排

3.给子文件取名字    1,2,3,4,5,6,7....这样子。

 

4,放数据到子文件

 

5,迭代,剩下的数据依次放入不同子文件

 

三,子文件进行归并。

我们只按照这样归并的,先把俩个子文件归并,再用归并后的子文件去归并下一个,在下一个,直到归并完所有子文件。

1.画图:

 这里的数字是文件的名字。

利用归并排序的思想,把有序的子文件归并成为一个大的有序文件

2.归并子函数:也就是一次归并

3.归并代码:

这里归并不加载到内存,在文件里面进行归并。

4.完整代码

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

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

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

	int num1, num2;
	int ret1 = fscanf(fout1, "%d\n", &num1);
	int ret2 = fscanf(fout2, "%d\n", &num2);
	while (ret1 != EOF && ret2 != EOF)
	{
		if (num1 < num2)
		{
			fprintf(fin, "%d\n", num1);
			ret1 = fscanf(fout1, "%d\n", &num1);
		}
		else
		{
			fprintf(fin, "%d\n", num2);
			ret2 = fscanf(fout2, "%d\n", &num2);
		}
	}

	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 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 = 1;

	memset(a, 0, sizeof(int) * n);


	// 分割成一段一段数据,内存排序后写到,小文件,
	//假设我们这次总数据100个,分十个小文件,每个小文件10个数据。

	while (fscanf(fout, "%d\n", &num) != EOF)
	{
		if (i < n - 1)
		{
			//这里先放入n-1个数据,else那里在放入一个。
			a[i++] = num;
		}
		else
		{
			//原因:为什么if那里不直接读n个数据。
			// 
		
			// 错误原因:
			//不然if哪里直接小于n的话,其实n个数据确实读进去了,但是又读了一个数据就要走else,进行排序,排好序之后
			//while哪里又读了一个开始放第二个文件,那么这个本来该数据第二个文件的数据就被跳过了,没有放入第二个文件。
			//拍出来的数据会丢失,分组也不正确。

			a[i] = num;


			///分割好之后对子文件排好序
			QuickSort(a, 0, n - 1);

			//给子文件取名字    1,2,3,4,5,6,7....这样子。
			sprintf(subfile, "%d", filei++);

			FILE* fin = fopen(subfile, "w");
			if (fin == NULL)
			{
				printf("打开文件失败\n");
				exit(-1);
			}
			for (int j = 0; j < n; j++)
			{
				fprintf(fin, "%d\n", a[j]);
			}
			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 = 2; i <= n; ++i)
	{
		// 读取file1和file2,进行归并出mfile
		_MergeFile(file1, file2, mfile);

		strcpy(file1, mfile);

		sprintf(file2, "%d", i + 1);

		//给归并后的文件取名字,这里的i+1 是追加到mfile后面的。
		sprintf(mfile, "%s%d", mfile, i + 1);
	}

	printf("%s文件排序成功\n", file);
	fclose(fout);
}

int main()
{
	MergeSortFile("DataTest.txt");

	return 0;
}

四,结果展示

画图:

划分好的是个子文件第一个,第二个。

第一次归并

后面都是类似的,最后一次归并文件里面有所有的拍好序的数据。 

五,代码链接

data structure: 数据解构练习 - Gitee.com

举报

相关推荐

0 条评论