题意:将百万级数据排序,已知内存最大处理4K数据
4K内存容量是4000字节,也就是说能处理1000个数假如说现在有N数据进行排序,N为百万级,而内存一次只能处理1000个数据,我们做法是分部做,即先把N分成N/1000份数据,这样每份数据就都能用内部排序方法实现(本题使用2路),每份数据存在一个文件里然后再把这些文件合并。
具体实现过称为:假设原文件存储在test.txt中分别从总数据test中fscanf读取1000个数据,每个文件依次命名1,2,3……理论是N/1000个,即filenum个文件(最后一个文件数据个数不一定为1000),每个文件中数据使用二路归并排序SortFile排好,接下来就是对filenum组数据进行排序,即调用MergeFile函数归并,一般来说这一步使用多路归并、败者树方法,当然也可以二路归并本题采用”二路归并“:使用非递归,两两合并(个数为奇数最后一个不用归),使用file_tag标记进行到第几组了,file_tag==filenum那么第一次循环归并就结束了,get_filenum记录得到的归并文件数,之前的文件使用remove删去,每次循环文件总数总要用新得到的归并文件总数代替filenum=get_filenum,直到文件数filenum为1那么就完成了
纯C代码:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#define MAXNUM 2000
///全局变量
int filenum;///当前文件数
int file_tag;///标记归并到哪个文件了
int get_filenum;///记录归并得到的文件数
///函数声明
void CreatFile();//用于获取100万个随机数
void SortFile();//将100万个随机数拆分为filenum个文件,每拆分好一个便内部排序,调用了函数CreatdataFile、merge_sort
void CreatdataFile(int data[], int count);//将data数组中排好的数据写入到文件中
void merge_sort(int num[], int start, int end);//二路归并排序函数
void merge(int num[], int start, int mid, int end);//两个有序数组的合并,与上一个函数构成完整二路归并排序函数
void MergeFile();//对于已经排好序的filenum个文件采取两两合并
int main()
{
CreatFile(); //生成10000个随机数存储在test.txt文件中
SortFile(); //初次切割并排序为有序文件
MergeFile(); //对文件进行归并排序
return 0;
}
void CreatFile()//用于获取100万个随机数
{
FILE *f;
f = fopen("test.txt", "w+");
//srand((unsigned)time(NULL));
for (int i = 0; i < 10000; ++i)
{
int data = rand() % 100; //产生0-100的随机数
fprintf(f, "%d\n", data);
}
fclose(f);
}
void SortFile()//将100万个随机数拆分为filenum个文件,每拆分好一个便内部排序,调用了函数CreatdataFile、merge_sort
{
FILE *f;
f = fopen("test.txt", "r");
int *data;
data = (int *)malloc(MAXNUM * sizeof(int));
char datachar;
int count = 0;
while (fscanf(f, "%d", &data[count])!=EOF)
{
count++;
if (count == MAXNUM)
{
filenum++;
merge_sort(data, 0, count - 1);
CreatdataFile(data, count);
count = 0;
}
}
if (count != 0)
{
filenum++;
CreatdataFile(data, count);
count = 0;
}
fclose(f);
free(data);
}
///将data中排好的数据写入到文件中1,2,3,4
void CreatdataFile(int data[], int count)
{
FILE *f;
char filename[10] = { "" };
filename[0] = filenum + '0';
strcat(filename, ".txt");
f = fopen(filename, "w+");
for (int i = 0; i < count; ++i)
{
fprintf(f, "%d\n", data[i]);
}
fclose(f);
}
///二路归并排序
void merge_sort(int num[], int start, int end)
{
int mid;
if (start < end)
{
mid = (start + end) / 2;
merge_sort(num, start, mid);
merge_sort(num, mid + 1, end);
merge(num, start, mid, end);
}
}
void merge(int num[], int start, int mid, int end)
{
int n1 = mid - start + 1;
int n2 = end - mid;
int *left, *right;
left = (int*)malloc(n1 * sizeof(int));
right = (int*)malloc(n2 * sizeof(int));
int i, j, k;
for (i = 0; i < n1; i++)
left[i] = num[start + i];
for (j = 0; j < n2; j++)
right[j] = num[mid + 1 + j];
i = j = 0;
k = start;
while (i < n1 && j < n2)
if (left[i] < right[j])
num[k++] = left[i++];
else
num[k++] = right[j++];
while (i < n1)
num[k++] = left[i++];
while (j < n2)
num[k++] = right[j++];
free(left);
free(right);
}
///file_tag标记进行到第几组
///get_filenum归并得到的文件数目
void MergeFile()
{
file_tag = 0;
while (filenum != 1)
{
while (file_tag < filenum)
{
if ((filenum - file_tag) == 1)
{
FILE *f1, *f;
char filename1[10] = { "" };
file_tag++;
filename1[0] = file_tag + '0';
strcat(filename1, ".txt");
f1 = fopen(filename1, "r");
///
get_filenum++;
char filename[10] = { "" };
filename[0] = get_filenum + '0';
strcat(filename, "data.txt");
f = fopen(filename, "w+");
int num1;
while (fscanf(f1, "%d", &num1) != EOF)
{
fprintf(f, "%d\n", num1);
}
fclose(f1);
fclose(f);
}
else
{
FILE *f1, *f2, *f;
char filename1[10] = { "" };
file_tag++;
filename1[0] = file_tag + '0';
strcat(filename1, ".txt");
f1 = fopen(filename1, "r");
char filename2[10] = { "" };
file_tag++;
filename2[0] = file_tag + '0';
strcat(filename2, ".txt");
f2 = fopen(filename2, "r");
get_filenum++;
char filename[10] = { "" };
filename[0] = get_filenum + '0';
strcat(filename, "data.txt");
f = fopen(filename, "w+");
int data;
int count = 0;
int num1, num2;
fscanf(f1, "%d", &num1);
fscanf(f2, "%d", &num2);
while (1)
{
if (num1 < num2)
{
fprintf(f, "%d\n", num1);
if (fscanf(f1, "%d", &num1) == EOF)
{
fprintf(f, "%d\n", num2);
while (fscanf(f2, "%d", &num2) != EOF)
{
fprintf(f, "%d\n", num2);
}
break;
}
}
else
{
fprintf(f, "%d\n", num2);
if (fscanf(f2, "%d", &num2) == EOF)
{
fprintf(f, "%d\n", num1);
while (fscanf(f1, "%d", &num1) != EOF)
{
fprintf(f, "%d\n", num1);
}
break;
}
}
}
fclose(f1);
fclose(f2);
fclose(f);
}
char filename1[10] = { "" };
char filename2[10] = { "" };
filename1[0] = get_filenum + '0';
filename2[0] = get_filenum + '0';
strcat(filename1, "data.txt");
strcat(filename2, ".txt");
char filename3[10] = { "" };
char filename4[10] = { "" };
//假如说归并得到第3个文件,那么产生它的对应的文件为5,6两个
filename3[0] = get_filenum * 2 - 1 + '0';
filename4[0] = get_filenum * 2 + '0';
strcat(filename3, ".txt");
strcat(filename4, ".txt");
int r1 = remove(filename3);
int r2 = remove(filename4);
printf("r1=%d r2=%d\n", r1, r2);
// int rename ( const char * oldname, const char * newname )
rename(filename1, filename2);
//printf("filenum=%d file_tag=%d get_filenum=%d\n", filenum, file_tag, get_filenum);
if (file_tag == filenum&&filenum != 1)
{
filenum = get_filenum;
file_tag = 0;
get_filenum = 0;
}
}
}
char filename1[20] = { "" };
char filename2[20] = { "" };
filename1[0] = 1 + '0';
strcat(filename1, ".txt");
strcat(filename2, "test_sort.txt");
rename(filename1, filename2);
printf("排序完成,有序序列保存在:test_sort.txt文件中\n");
}