外排序

外排序和内排序是两种截然不同的排序算法,因为外排序是对文件进行排序,通常文件很大我们不能一次性读入内存。因此我们需要使用外排序。外排序常用的算法是多路归并排序。多路归并排序是指把文件分成n个已经排好序的文件,然后对每个子文件同时读入,选出当前指针最小的值输出,然后该指针后移,直到处理完所有子文件。
现在我们模拟外排序,内存使用最多1M,对1 ~ 10000000个数进行外排序。
首先是数据生成部分:

#include <iostream>
#include <time.h>
#include <assert.h>
using namespace std;

const int size = 10000000;
int num[size];

int main() {
    int n;
    FILE *fp = fopen("data.txt", "w");
    assert(fp);
    for(n = 1;n <= size;n++) {
        num[n] = n;
    }
    srand((unsigned)time(NULL));
    int i, j;
    for(n = 0;n < size;n++) {
        i = (rand() * RAND_MAX + rand()) % 10000000;
        j = (rand() * RAND_MAX + rand()) % 10000000;
        swap(num[i], num[j]);
    }
    for(n = 0;n < size;n++) {
        fprintf(fp, "%d ", num[n]);
    }
    fclose(fp);
    return 0;
}

接下来是排序部分,排序的过程分成两部分:切分文件和归并文件。切分文件的原则是将文件切分成内存可以处理的小部分,然后一部分一部分处理并输出。归并文件比较容易思考的是二分归并,但是这样IO操作太多,一般我们可以在内存满足的情况下读入尽可能多的文件处理。
切分文件部分如下:

int sort_num = 10000000;
int memory_size = 250000;

int read_data(FILE *fp, int *space) {
    int index = 0;
    while (index < memory_size && fscanf(fp, "%d ", &space[index]) != EOF)
        index++;
    return index;
}

void check_fp(FILE *fp) {
    if(fp == NULL) {
        cout << "the file pointer is invalid" << endl;
        exit(1);
    }
}

int compare(const void *first_num, const void *second_num) {
    return *(int *)first_num - *(int *)second_num;
}

string new_file_name(int n) {
    string file_name = "data" + to_string(n);
    return file_name;
}

void write_data(FILE *fp, int *space, int num) {
    for(int i = 0;i < num;i++) {
        fprintf(fp, "%d " , space[i]);
    }
}

int memory_sort() {
    FILE *fp_in_file = fopen("data.txt", "r");
    check_fp(fp_in_file);
    int counter = 0;
    while(true) {
        int *space = new int[memory_size];
        int num = read_data(fp_in_file, space);
        if (num == 0) {
            break;
        }
        qsort(space, num, sizeof(int), compare);
        string file_name = new_file_name(++counter);
        FILE *fp_aux_file = fopen(file_name.c_str(), "w");
        check_fp(fp_aux_file);

        write_data(fp_aux_file, space, num);
        fclose(fp_aux_file);
        delete []space;
    }
    fclose(fp_in_file);
    return counter;
}

归并排序代码如下:

void merge_sort(int file_num) {
    if (file_num <= 0) {
        return;
    }
    FILE *fp_out_file = fopen("result.txt", "w");
    check_fp(fp_out_file);
    FILE **fp_array = new FILE *[file_num];
    int i;
    for(i = 0;i < file_num;i++) {
        string file_name = new_file_name(i + 1);
        fp_array[i] = fopen(file_name.c_str(), "r");
        check_fp(fp_array[i]);
    }
    int *first_data = new int[file_num];
    bool *finish = new bool[file_num];
    memset(finish, false, sizeof(bool) * file_num);
    for(i = 0;i < file_num;i++) {
        fscanf(fp_array[i], "%d ", &first_data[i]);
    }
    while(true) {
        int index = 0;
        while(index < file_num && finish[index] ) {
            index++;
        }
        if (index >= file_num) {
            break;
        }
        int min_data = first_data[index];
        for (i = index + 1;i < file_num;i++) {
            if (min_data > first_data[i] && !finish[i]) {
                min_data = first_data[i];
                index = i;
            }
        }
        fprintf(fp_out_file, "%d ", min_data);
        if (fscanf(fp_array[index], "%d ", &first_data[index]) == EOF) {
            finish[index] = true;
        }
    }
    fclose(fp_out_file);
    delete []finish;
    delete []first_data;
    for(int i = 0;i < file_num;i++) {
        fclose(fp_array[i]);
    }
    delete []fp_array;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值