归并排序:实现文件归并

目录

引言 

外排序与内排序的不同

1. 基本概念

代码功能概述

代码详细分析

1. 数据生成函数 CreateData

2. 比较函数 compare (用于qsort函数)

3. 读取数据并排序到文件的函数 ReadDataSortToFile

4. 文件归并函数 MergeFile

5. 主函数 main 

总结


 

引言 

在数据处理的过程中,我们经常会遇到需要对大量数据进行排序的情况。当数据量过大,无法一次性全部加载到内存中时,我们可以采用外部排序的方法,其中文件归并是一种常见的实现方式。本文将详细介绍一段用于文件归并的代码,该代码通过分块排序和逐步归并的方式,最终实现对大量数据的排序。

外排序与内排序的不同

1. 基本概念

  • 内排序:内排序是指在排序过程中,所有参与排序的数据都能够一次性加载到计算机的内存(如 RAM)中进行处理。常见的内排序算法有冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序等。这些算法在处理小规模数据时非常高效,因为它们可以直接在内存中对数据进行操作。
  • 外排序:外排序则是针对数据量过大,无法一次性全部加载到内存中的情况。在进行外排序时,数据主要存储在外部存储设备(如硬盘、磁带等)上,排序过程需要在内存和外部存储设备之间进行多次数据交换。外排序通常采用分治策略,将大文件分成多个小块,对每个小块进行内排序,然后再将这些有序的小块逐步归并成一个最终的有序文件。归并排序也可以是一种外排序。

 

代码功能概述

这段代码的主要功能是对一个包含大量整数数据的文件进行排序。具体步骤如下:

  1. 数据生成:生成一个包含随机整数的文件。
  2. 分块排序:将大文件分成多个小块,对每个小块进行排序,并将排序结果保存到临时文件中。
  3. 文件归并:逐步将这些临时文件进行归并,最终得到一个有序的文件。

代码详细分析

1. 数据生成函数 CreateData

void CreateData()
{
    // 数据个数
    int n = 1000000;
    const char* file = "data.txt";
    FILE* fin = fopen(file, "w");
    if(fin == NULL)
    {
        perror("fopen fin fail!");
        exit(1);
    }
    for(int i = 0; i < n; i++)
    {
        int x = rand() + i;
        fprintf(fin, "%d\n", x);
    }

    fclose(fin);
}
  • 该函数用于生成一个包含 1000 个随机整数的文件 data.txt
  • 使用 fopen 函数以写入模式打开文件,如果打开失败则输出错误信息并退出程序。
  • 通过 rand() 函数生成随机整数,并使用 fprintf 函数将整数写入文件。
  • 最后使用 fclose 函数关闭文件。

2. 比较函数 compare (用于qsort函数)

int compare(const void* a, const void* b)
{
    return (*(int*)a - *(int*)b);
}
  • 该函数是 qsort 函数所需的比较函数,用于比较两个整数的大小。
  • 如果 a 小于 b,则返回负数;如果 a 等于 b,则返回 0;如果 a 大于 b,则返回正数。

3. 读取数据并排序到文件的函数 ReadDataSortToFile

// 返回实际读到的数据个数,若没有则返回 0
int ReadDataSortToFile(FILE* fout, int n, const char* file1)
{
    int x = 0;
    int* a = (int*)malloc(sizeof(int) * n);
    if(a == NULL)
    {
        perror("malloc fail!");
        exit(1);
    }
    // 想读取 n 个数据,如果遇到文件结束,应该读到 j 个
    int j = 0;
    for(int 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 fin fail!");
        exit(1);
    }
    // 写入 file1
    for(int i = 0; i < j; i++)
    {
        fprintf(fin, "%d\n", a[i]);
    }
    free(a);
    fclose(fin);
    return j;
}

 

  • 该函数从输入文件 fout 中读取最多 n 个整数,并将这些整数存储在动态分配的数组 a 中。
  • 如果读取的数据个数为 0,则释放数组内存并返回 0。
  • 使用 qsort 函数对数组 a 进行排序。
  • 打开一个新文件 file1,将排序后的数组元素写入该文件。
  • 释放数组内存并关闭文件,最后返回实际读取的数据个数。

4. 文件归并函数 MergeFile

void MergeFile(const char* file1, const char* file2, const char* mfile)
{
    FILE* fin1 = fopen(file1, "r");
    if(fin1 == NULL)
    {
        perror("fopen fin1 fail!");
        exit(1);
    }
    FILE* fin2 = fopen(file2, "r");
    if(fin2 == NULL)
    {
        perror("fopen fin2 fail!");
        exit(1);
    }
    FILE* mfin = fopen(mfile, "w");
    if(mfin == NULL)
    {
        perror("fopen mfin fail!");
        exit(1);
    }

    int x1 = 0;
    int x2 = 0;
    int ret1 = fscanf(fin1, "%d", &x1);
    int ret2 = fscanf(fin2, "%d", &x2);
    while(ret1 != EOF && ret2 != EOF)
    {
        if(x1 < x2)
        {
            fprintf(mfin, "%d\n", x1);
            ret1 = fscanf(fin1, "%d", &x1);
        }
        else
        {
            fprintf(mfin, "%d\n", x2);
            ret2 = fscanf(fin2, "%d", &x2);
        }
    }
    while(ret1 != EOF)
    {
        fprintf(mfin, "%d\n", x1);
        ret1 = fscanf(fin1, "%d", &x1);
    }
    while(ret2 != EOF)
    {
        fprintf(mfin, "%d\n", x2);
        ret2 = fscanf(fin2, "%d", &x2);
    }
    fclose(fin1);
    fclose(fin2);
    fclose(mfin);
}

 

  • 该函数用于将两个已排序的文件 file1 和 file2 归并到一个新文件 mfile 中。
  • 打开三个文件:file1 和 file2 以读取模式打开,mfile 以写入模式打开。
  • 从 file1 和 file2 中分别读取一个整数,比较它们的大小,将较小的整数写入 mfile 中,并继续从相应的文件中读取下一个整数。
  • 当其中一个文件读取完毕后,将另一个文件中剩余的整数全部写入 mfile 中。
  • 最后关闭所有文件。

5. 主函数 main 

int main()
{
    srand((unsigned int)time(NULL));
    // 造数据
    // CreateData();

    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 fout fail!");
        exit(1);
    }
    int m = 100000;
    ReadDataSortToFile(fout, m, file1);
    ReadDataSortToFile(fout, m, file2);
    while(1)
    {
        MergeFile(file1, file2, mfile);
        // 删除 file1 和 file2
        remove(file1);
        remove(file2);
        // 重命名 mfile 为 file1
        rename(mfile, file1);
        // 当再去读取数据,一个都读不到,说明已经没有数据了
        // 已经归并完成,归并结果在 file1
        int n = 0;
        if((n = ReadDataSortToFile(fout, m, file2)) == 0)
        {
            break;
        }
    }

    fclose(fout);
    return 0;
}
  • 初始化随机数种子。
  • 打开包含原始数据的文件 data.txt
  • 调用 ReadDataSortToFile 函数将数据分成两个小块,分别排序并保存到 file1.txt 和 file2.txt 中。
  • 进入循环,不断调用 MergeFile 函数将 file1.txt 和 file2.txt 归并到 mfile.txt 中,然后删除 file1.txt 和 file2.txt,将 mfile.txt 重命名为 file1.txt
  • 再次调用 ReadDataSortToFile 函数尝试读取数据到 file2.txt 中,如果读取的数据个数为 0,则说明所有数据已经归并完成,退出循环。
  • 关闭文件并返回 0。

总结

通过分块排序和逐步归并的方式,这段代码实现了对大量数据的排序,避免了一次性将所有数据加载到内存中。这种方法在处理大规模数据时非常有效,可以在有限的内存资源下完成排序任务。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值