三路分化与文件归并排序

三路分化与文件归并排序

1.快排深入优化探讨之三路划分

算法思想:

当⾯对有⼤量跟key相同的值时,三路划分的核心思想有点类似hoare的左右指针和lomuto的前后指针的结合。核⼼思想是把数组中的数据分为三段【比key小的值】【跟key相等的值】【比key大的值】,所以叫做三路划分算法。

实现思想:

  • key默认取left位置的值;
  • left指向区间最左边,right指向区间最后边,cur指向left+1位置;
  • cur遇到比key小的值后跟left位置交换,换到左边,left++,cur++;
  • cur遇到比key大的值后跟right位置交换,换到右边,right–;
  • cur遇到跟key相等的值后,cur++;
  • 直到cur > right结束。

以下为例:
在这里插入图片描述
在这里插入图片描述

代码实现:

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}

	int begin = left;
	int end = right;

	//随机选key
	int randi = left + (rand() % (right - left + 1));
	Swap(&a[left], &a[randi]);

	int key = a[left];
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < key)
		{
			Swap(&a[cur], &a[left]);
			++left;
			++cur;
		}
		else if (a[cur] > key)
		{
			Swap(&a[cur], &a[right]);
			--right;
		}
		else
		{
			++cur;
		}
	}

	//[begin,left-1] [left,right] [right+1,end]
	QuickSort(a, begin, left - 1);
	QuickSort(a, right + 1, end);
}
int* sortArray(int* nums, int numsSize, int* returnSize)
{
	srand(time(0));
	QuickSort(nums, 0, numsSize - 1);

	*returnSize = numsSize;
	return nums;
}

2.外排序之文件归并排序实现

概念:

  • 外排序(External sorting)是指能够处理 极⼤量数据的排序算法。通常来说,外排序处理的数据不能一次装⼊内存,只能放在读写较慢的 外存储器(通常是硬盘) 上。外排序通常采用的是一种“ 排序-归并 ”的策略。在排序阶段,先读⼊能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件。然后在归并阶段将这些临时文件组合为⼀个大的有序文件,也即排序结果。

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

创建一个随机数据的文件:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 创建N个随机数,写到⽂件中
void CreateNDate()
{
	//造数据
	int n = 1000000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (int i = 0; i < n; ++i)
	{
		int x = rand() + i;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}


int main()
{
	CreateNDate();
	return 0;
}
  • 打开代码所在的文件夹

在这里插入图片描述

  • 显示生成成功

在这里插入图片描述

思路分析:

  1. 读取n个值排序后写到file1,再读取n个值排序后写到file2
  2. file1和file2利⽤归并排序的思想,依次读取⽐较,取⼩的尾插到mfile,mfile归并为⼀个有序⽂件
  3. 将file1和file2删掉,mfile重命名为file1
  4. 再次读取n个数据排序后写到file2
  5. 继续⾛file1和file2归并,重复步骤2,直到⽂件中⽆法读出数据。最后归并出的有序数据放到了file1中

代码实现:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 创建N个随机数,写到⽂件中
void CreateNDate()
{
	//造数据
	int n = 10000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (int i = 0; i < n; ++i)
	{
		int x = rand() + i;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

int compare(const void* a, const void* b)
{
	return (*(int*)a - *(int*)b);
}
//反回实际读到的数据个数,没有数据了,返回0
int ReadNDataSortToFile(FILE*fout, int n, const char* file1)
{
	int x = 0;
	int* a = (int*)malloc(sizeof(int) * n);
	if (a == NULL)
	{
		perror("malloc error");
		return;
	}

	//想读取n个数据,如果遇到文件结束,应该读到j个
	int j = 0;
	for (size_t i = 0; i < n; i++)
	{
		if (fscanf(fout, "%d", &x) == EOF)
			break;
		a[j++] = x;
	}
	if (j == 0)
	{
		free(a);
		return 0;
	}
	//排序
	qsort(a, j, sizeof(int), compare);

	FILE* fin = fopen(file1, "w");
	if (fin == NULL)
	{
		free(a);
		perror("fopen error");
		return 0;
	}

	//写回file1文件
	for (int i = 0; i < n; i++)
	{
		fprintf(fin, "%d\n", a[i]);
	}

	free(a);
	fclose(fin);
	return j;
}

void MergeFile(const char* file1, const char* file2, const char* mfile)
{
	FILE* fout1 = fopen(file1 , "r");
	if (fout1 == NULL)
	{
		perror("fopen error");
		return;
	}
	FILE* fout2 = fopen(file2 , "r");
	if (fout2 == NULL)
	{
		perror("fopen error");
		return;
	}
	FILE* mfin = fopen(mfile, "w");
	if (mfin == NULL)
	{
		perror("fopen error");
		return 0;
	}

	//归并逻辑
	int x1 = 0;
	int x2 = 0;
	int ret1 = fscanf(fout1, "%d", &x1);
	int ret2 = fscanf(fout1, "%d", &x2);
	while (ret1!=EOF && ret2!=EOF)
	{
		if (x1 < x2)
		{
			fprintf(mfin, "%d\n", x1);
			ret1 = fscanf(fout1, "%d", &x1);
		}
		else
		{
			fprintf(mfin, "%d\n", x2);
			ret1 = fscanf(fout2, "%d", &x2);
		}
	}

	while (ret1 != EOF)
	{
		fprintf(mfin, "%d\n", x1);
		ret1 = fscanf(fout1, "%d", &x1);
	}
	while (ret2 != EOF)
	{
		fprintf(mfin, "%d\n", x2);
		ret2 = fscanf(fout2, "%d", &x2);
	}

	fclose(fout1);
	fclose(fout2);
	fclose(mfin);
}

int main()
{
	CreateNDate();

	const char* file1 = "file1.txt";
	const char* file2 = "file2.txt";
	const char* mfile = "mfile.txt";

	FILE* fout = fopen("data.txt", "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}
	ReadNDataSortToFile(fout, 100, file1);
	ReadNDataSortToFile(fout, 100, file2);

	while (1)
	{
		MergeFile(file1, file2, mfile);
		//删除file1和file2
		remove(file1);
		remove(file2);

		//重命名mfile为file1
		rename(mfile, file1);

		//当再次去读取数据,一个都读不到,说明已经没有数据了
		//已经归并完成,归并结构在file1中
		int n = 0;
		if (n = ReadNDataSortToFile(fout, 100, file2) == 0)
			break;
		if (n < 100)
		{
			int x = 0;
		}
	}
	return 0;
}

调试过程文件的变化:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最后,希望这篇文章对你的学习有所帮助,有任何问题欢迎在评论区留言哦~

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值