对海量数据进行外排序的算法

对海量数据进行外排序的算法

需求:一种数据包中包括int, string, long, double 四种类型的数组,数组长度均为4096.(即4096行,每行有int, string, long, double四个数据)。对1000个随机产生的数据包,按int列进行排序。将int列最小的4096个数据及其对应的其他数据保存到一个新的数据包中。要求不修改1000个数据包。
分析:由于需要排序的数据量巨大,我们选择外排序算法来处理这道题。用.TXT文件来表示需求中的数据包,利用归并算法生成已排好序的顺串文件,然后利用败者树算法来进行排序。示例代码先以10个数据包为例。

具体函数代码:

#include<iostream>
#include<fstream>
#include<sstream>
#include <string>
#include<vector>
#include <map>
using namespace std;
#define MAX_INT 0x7fffffff
const int kMaxSize = 4096;
const int kMaxWay = 10;

int buffer[kMaxSize];  //假设内存只能放4096个整型.
int heap_size;


struct Run {
    int *buffer; // 每个顺串的缓冲区
    int length;// 缓冲区内元素个数
    int idx;// 当前所读元素下标

};

int ls[kMaxWay];  //败者树,ls[0]是最小值的位置,其余是各败者的位置
Run *runs[kMaxWay];

将vector转换为arr数组

template<class elemType>    //将vector转化为arr
    elemType* vec2arr(vector<elemType> vec) {
        elemType* arr = new elemType [vec.size()];
        for (size_t i = 0; i < vec.size(); i++)
             arr[i] = vec[i];
        return arr;
    }
    template<class elemType>

快排函数

void quickSort(elemType a[], int begin, int end) {    //对array进行快速排序
    if (begin < end) {
        int i = begin, j = end;
        elemType key = a[i];
        while (i < j) {
            while (i<j&&a[j]>key)
                j--;    //j向前找比key小的数,注意随时检查i<j
            if (i < j)
                a[i++] = a[j];
            while (i < j&&a[i] < key)
                i++;    //i向后找比key大的数
            if (i < j)
                a[j--] = a[i];
        }    //将key赋值给i与j相遇的地方
        a[i] = key;
        quickSort(a, begin, i - 1);    //两侧递归
        quickSort(a, i + 1, end);
    }
}

构建以及调整败者树函数

void Adjust(Run **runs, int n, int s) {    //首先根据s计算出对应的ls中哪一个下标
    int t = (s + n) / 2;
    int tmp;
    while (t > 0) {
        if (s == -1)
            break;
        if (ls[t] == -1 || runs[s]->buffer[runs[s]->idx] > runs[ls[t]]->buffer[runs[ls[t]]->idx]) {
            tmp = s;
            s = ls[t];
            ls[t] = tmp;
        }
        t /= 2;
    }
    ls[0] = s;
}

void CreateLoserTree(Run **rus, int n) {
    for (int i = 0; i < n; i++)
        ls[i] = -1;
    for (int i = n - 1; i >= 0; i--)
        Adjust(runs, n, i);

归并排序函数

void  MergeSort(Run** runs, int num_of_runs, const char* file_out) {
    //初始化Run
    if (num_of_runs > kMaxWay)
        num_of_runs = kMaxWay;
    int length_per_run = kMaxSize / num_of_runs;
    for (int i = 0; i < num_of_runs; i++)
        runs[i]->buffer = buffer + i*length_per_run;

    ifstream in[kMaxWay];
    char file_name[20];
    for (int i = 0; i < num_of_runs; i++) {
        sprintf(file_name, "%d.txt", i + 1);
        in[i].open(file_name);
    }
    // 将顺串文件的数据读到缓冲区中
    for (int i = 0; i < num_of_runs; i++) {
        int j = 0;
        while (in[i] >> runs[i]->buffer[j]) {
            j++;
            if (j == length_per_run)
                break;
        }
        runs[i]->length = j;
        runs[i]->idx = 0;
    }

    CreateLoserTree(runs, num_of_runs);
    ofstream out(file_out);

    int live_runs = num_of_runs;
    while (live_runs > 0) {
        out << runs[ls[0]]->buffer[runs[ls[0]]->idx++] << endl;

        if (runs[ls[0]]->idx == runs[ls[0]]->length) {
            int j = 0;
            while (in[ls[0]] >> runs[ls[0]]->buffer[j]) {
                j++;
                if (j == length_per_run)
                    break;
            }
            runs[ls[0]]->length = j;
            runs[ls[0]]->idx = 0;
        }
        if (runs[ls[0]]->length == 0) {
            runs[ls[0]]->buffer[runs[ls[0]]->idx] = MAX_INT;
            live_runs--;
        }
        Adjust(runs, num_of_runs, ls[0]);
    }
}

具体实现代码:

生成10个排序好的顺串文件

map<int, string> mapData;
    for(int i = 0;i < 10;i++) {
        string bb,fn;
        bb = to_string(static_cast<long long>(i));    //将int转化为string类型
        fn = "file"+bb+".txt";
        fstream infile;
        infile.open(fn);    //读取指定行的int数据,读入vector radius
        if (!infile.is_open()) {
            cout << "Unable to open myfile";
            system("pause");
            exit(1);
        }
        vector<string> vec;
        string temp;
        while (getline(infile, temp)) {    //利用getline()读取每一行,并按照行为单位放入到vector
            vec.push_back(temp);
        }
        vector <int> radius;

        for (auto it = vec.begin(); it != vec.end(); it++) {
            istringstream is(*it);    //用每一行的数据初始化一个字符串输入流;
            string s;
            int pam = 0;
            string temp_str;
            while (is >> s) {    //以空格为界,把istringstream中数据取出放入到依次s中
                int r;
                if (pam == 1) {    //获取第二列的数据
                    r = atof(s.c_str());    //做数据类型转换,将string类型转换成int
                    radius.push_back(r);
                }
                if(pam > 1) {    //将后三列形成字符串存入map
                    string kong = " ";
                    string res = kong + s.c_str();
                    temp_str += res;
                }
                if(pam == 4) {
                    mapData.insert(pair<int, string>(r, temp_str));
                    temp_str.clear();
                }
                pam++;
            }
        }infile.close();
        int *arr = new int[4096];
        arr = vec2arr(radius);
        quickSort(arr,0,4096);
        fstream file;
        string bbl,fnl;
        map<int, string>::iterator iter;
        bbl = to_string(static_cast<long long>(i+1));    //将int转化为string类型
        fnl = bbl+".txt";
        file.open(fnl,ios::out|ios::trunc);
        for(int j = 0;j < 4096;j++) {
                iter = mapData.find(arr[j]);    //找到索引对应的map
                if(iter != mapData.end())
                file<<arr[j]<<endl;
                cout<<j<<endl;
        }
        file.close();
    }

归并排序并将结果保存在sorted.txt中

 for (int i = 0; i < 10; i++)
        runs[i] = new Run();
    MergeSort(runs, 10, "sorted.txt");

将最小的4096个元素取出并保存在result.txt中

ifstream filet;
    filet.open("sorted.txt");
    if(!filet)
        cout<<"error"<<endl;
    vector<int> radius;
    string temp;
    int tempt;
    while (getline(filet, temp)) {    //利用getline()读取每一行,并按照行为单位放入到vector
        tempt = atof(temp.c_str());
        radius.push_back(tempt);
    }
    filet.close();
    int *arr = new int[4096];
    arr = vec2arr(radius);

    fstream dataFile;    //定义文件操作对象
    dataFile.open("result.txt",ios::out);
    map<int, string>::iterator iter;
    for(int i = 0;i < 4096;i++) {    //向result中写入最小的4096个结果
        iter = mapData.find(arr[i]);
        dataFile<< i<<"    "<<arr[i]<<iter->second<<endl;
    }
    dataFile.close();

总结:本题的难点:

1.循环打开10个txt文件的方法,将i与文件名一起转化为字符串,并打开。
2.败者树的建立以及调整
3.getline来以行为单位读取TXT中的i数据,并根据空格来读取需要排序的int型数据,将其余三列数据看做字符串
4.map 根据key,value来补全排序后,int列数据后面的三列数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值