目录
3.给子文件取名字 1,2,3,4,5,6,7....这样子。
一,什么是外排序
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