外部排序 || c++实现多路归并的败者树算法

29 篇文章 5 订阅
27 篇文章 1 订阅
算法概念:
  • 如果我们要对大规模文件进行排序,不能一次装入内存中,只能从外存一次次读取分别排序,而外存的IO时间复杂度很高,所以我们应该尽量减少针对外存读写的次数。如果我们只是简单地进行二路归并的话,归并路数越少归并的次数就越多,每次归并都要进行一次IO操作。所以我们考虑尽量进行多路归并,而多路归并不同于简单地二次归并只需对两组数进行指针移动归并,为了降低多路归并中归并的时间复杂度。我们考虑使用败者树。

  • 败者树和胜者树相对,就是树的每个结点存储的是比较失败的结点,胜利的结点接着去与更高层次的结点进行比较。

  • 想法应用于竞技比赛就是当选出冠军后,再选择亚军只需要让曾经败给冠军的结点进行比较即可,类推季军…,从而得到全部所有的名次。

代码实现:
  • LoserTree.hpp
//
//  LoserTree.hpp
//  LoserTree
//
//  Created by peiyu wang on 2019/4/1.
//  Copyright © 2019 peiyu wang. All rights reserved.
//

#ifndef LoserTree_hpp
#define LoserTree_hpp

#include <stdio.h>
#include <iostream>
#include <limits.h>
#include <ctime>
#include <algorithm>
using namespace std;

/* 宏定义 */
#ifndef MAXSIZE
#define MAXSIZE 2000
#endif

#define MAX 2500
#define K 5 // k路归并
#define MAXKEY INT_MAX
#define MINKEY INT_MIN

/* k-路归并败者树类型定义 */
typedef int KeyType;
typedef int LoserTree[K];
struct ExNode
{
    KeyType key;
}; // 外结点,只存放待归并记录的关键字
typedef ExNode External[K+1];

/* 败者树函数列表 */
// 利用败者树ls将ke个输入归并段中的记录归并到输出归并段
void K_Merge(FILE *fp, LoserTree ls, External b);

// 从b[s]出发调整败者树
void Adjust(LoserTree ls, External b, int s);

// 依次从b[k-1...0]出发,调整ls为败者树
void CreateLoserTree(LoserTree ls, External b);

int freadNum(FILE *fp, int *i);

// 从文件fp[i]中读取关键字到x
void input(FILE *fp[K], int *x, int i);

// 将x输出到有序表fp
void output(FILE *fp, int x);

// 生成MAX个随机数到fp用作测试
void RandomNum(FILE *fp);

// 将随机数表fp分段排序后分别写入文件[0...k-1].txt中
void Cut_Sort(FILE *fp);

#endif /* LoserTree_hpp */

  • LoserTree.cpp:
//
//  LoserTree.cpp
//  LoserTree
//
//  Created by peiyu wang on 2019/4/1.
//  Copyright © 2019 peiyu wang. All rights reserved.
//

#include "LoserTree.hpp"
#include <stdlib.h>
#include <vector>

// 利用败者树ls将k个输入归并段中的记录归并到输出归并段
void K_Merge(FILE *fp_out, LoserTree ls, External b)
{
    int i, q;
    FILE *fp_in[K] = {};
    for (i = 0; i < K; i++)
    {
        input(fp_in, &(b[i].key), i);
    }
    CreateLoserTree(ls, b);
    while (b[ls[0]].key != MAXKEY)
    {
        q = ls[0];
        output(fp_out, b[q].key);
        input(fp_in, &(b[q].key), q);
        Adjust(ls, b, q);
    }
    //output(fp_out, b[ls[0]].key);
    fclose(fp_out);
}

// 从b[s]出发调整败者树
void Adjust(LoserTree ls, External b, int s)
{
    int t, tmp;
    t = (s + K) / 2;
    while (t > 0)
    {
        if (b[s].key > b[ls[t]].key)
        {
            tmp = s;
            s = ls[t];
            ls[t] = tmp;
        }
        t /= 2;
    }
    ls[0] = s;
}

// 依次从b[k-1...0]出发,调整ls为败者树
void CreateLoserTree(LoserTree ls, External b)
{
    int i;
    b[K].key = MINKEY;
    for (i = 0; i < K; i++)
    {
        ls[i] = K;
    }
    for (i = K - 1; i >= 0; i--)
    {
        Adjust(ls, b, i);
    }
}

int freadNum(FILE *fp, int *i)
{
    int tmp;
    int count = 0;
    while ((tmp = getc(fp)) != EOF)
    {
        if ((tmp >= '0'&& tmp <= '9') || (tmp == '+') || (tmp == '-'))
        {
            ungetc(tmp, fp);
            break;
        }
    }
    if (tmp != EOF)
    {
        count = fscanf(fp, "%d", i);
    }
    return count;
}

// 从文件fp[i]中读取关键字到x
void input(FILE *fp[K], int *x, int i)
{
    int tmp;
    char str1[100], str2[100], str3[100] = ".txt";
    if (!fp[i])
    {
        strcpy(str1, "tmpfile/");
        sprintf(str2, "%d", i);
        strcat(str1, strcat(str2, str3));
        fp[i] = fopen(str1, "r");
    }
    if(freadNum(fp[i], &tmp))
    {
        *x = tmp;
        if (tmp == MAXKEY)
            fclose(fp[i]);
    }
}

// 将x输出到有序表fp
void output(FILE *fp, int x)
{
    fprintf(fp, "%d, ", x);
}

// 生成MAX个随机数到fp用作测试
void RandomNum(FILE *fp)
{
    int i, j, number, tag;
    int x[MAX + 1] = {0};
    srand((unsigned int)time(0));
    for (i = 1; i<= MAX; i++)
    {
        do
        {
            number = rand() % (4*MAX - 1) + 1;
            tag = 0;
            for (j = 1; j <= x[0]; j++)
            {
                if (number == x[j])
                {
                    tag = 1;
                    break;
                }
            }
            if (tag == 0)
            {
                x[0]++;
                x[x[0]] = number;
            }
        }while(tag == 1);
    }
    fprintf(fp, "(%d), ", MAX);
    for (i = 1; i <= x[0]; i++)
    {
        fprintf(fp, "%d, ", x[i]);
    }
    fclose(fp);
}

// 将随机数表fp分段排序后分别写入文件[0...k-1].txt中
void Cut_Sort(FILE *fp)
{
    FILE *fptr;
    int i, j;
    KeyType tmp;
    int len, m, n;
    char str1[100], str2[100], str3[100] = ".txt";
    freadNum(fp, &len);
    m = len / K; // m为一次可以处理的最大数字的个数
    n = K + (len % m != 0); // n为归并段个数
    for (i = 0; i < n; i++)
    {
        vector<KeyType> v;
        strcpy(str1, "./tmpfile/");
        sprintf(str2, "%d", i);
        strcat(str1, strcat(str2, str3));
        fptr = fopen(str1, "w+");
        for (j = 1; j <= m; j++)
        {
            if (freadNum(fp, &tmp) == 1)
            {
                v.push_back(tmp);
            }
            else
                break;
        }
        sort(v.begin(), v.end());
        for (j = 0; j < v.size(); j++)
        {
            fprintf(fptr, "%d, ", v[j]);
        }
        fprintf(fptr, "%d", MAXKEY);
        fclose(fptr);
    }
    fclose(fp);
}

  • main.cpp
//
//  main.cpp
//  LoserTree
//
//  Created by peiyu wang on 2019/4/1.
//  Copyright © 2019 peiyu wang. All rights reserved.
//

#include "LoserTree.hpp"

int main(int argc, const char * argv[]) {
    FILE *fp;
    LoserTree ls;
    External b;
    fp = fopen("test.txt", "w+");
    RandomNum(fp);
    fp = fopen("test.txt", "r");
    Cut_Sort(fp);
    fp = fopen("Order.txt", "w+");
    K_Merge(fp, ls, b);
    printf("END\n");
    return 0;
}

实现效果:

(初始化的文件以及排序后的文件)在这里插入图片描述

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值