外排序

存储器

  • 计算机存储器分为主存储器(内存)和外存储器
  • 内存性能高,价格高;外存性能低,价格也低,并且便携。
  • 外存的基本存储单位:(page),长度固定的存储块

文件的组织和管理

  • 文件(file),外存上的数据结构,由大量相同的记录组成。
  • 记录(record ),具有独立逻辑意义的数据块,可以由字符、二进制序列、字段或域组成。
  • 操作系统的文件:连续字符序列,没有明显结构。
  • 数据库文件:有结构,每个记录由一个或多个不可再分割的数据项组成。
  • 定长文件:每一条记录是等长的。
  • 不定长文件:记录的长度不相等。
  • 单关键码文件
  • 多关键码文件
  • 实时和批量两种处理方式
  • 文件的逻辑组织三种方式:顺序结构的定长记录、顺序结构的不定长记录、按关键码存取的记录
  • 文件物理结构:顺序、索引、散列、倒排
  • C++文件流(file stream),以外存文件为输入输出对象的数据流。
  • istream, ostream, iostream, ifstream, ofstream, fstream
  • fstream成员函数
  • 文件三种操作:指针设定到指定位置、从当前位置读取字节、向当前位置写入字节

外排序

  • 外排序(external sort),外存文件排序技术,对存放在外存中的数据进行排序。
  • 思路:根据内存大小,将外存中的数据文件划分为若干段,每次把其中一段读入内存并排序
  • 顺串归并段run),对每一段排序的结果子文件
  • 外排序两个阶段:形成尽可能长的顺串、逐趟归并顺串。
  • 时间:外存信息读写、在内存内排序 、内部归并
  • 排序的关键:减少外存信息的读写次数,也就是归并的趟数
  • m 个初始顺串,每次归并k个,归并趟数[ logkm]
  • 减少归并次数:渐少初始顺串或增大每次归并个数

置换选择排序

  • 算法处理过程:输入文件-输入缓冲区-内存-输出缓冲区-输出文件
    这里写图片描述
    当输入缓冲区空了的时候才从外部文件中一次性读取数据,当输出缓冲区满了的时候才一次性向外部文件中写数据。

  • 置换选择排序

    • 读取M个记录到RAM中
    • 建立最小值堆
    • 在堆变成空之前,循环以下操作
      • 每次输出一个堆顶到输出缓冲区
      • 再比较输入缓冲区一个记录是否比刚刚输出的堆顶更大,如果是,将这个数据读入并放在堆顶,如果不是,把这个数据放在堆尾,堆的长度减1
      • 重建堆
    • 输出一个顺串
    • 再读取新的 M 个数据到RAM中
template<class T>
void replacementSelection(T* A,int n,const char *in,const char* out)
{
    T mval; //存放最小值
    T r;  //存放从输入缓存区读入的数据
    FILE inputFile;  //输入文件句柄
    FILE outputFile;  //输出文件句柄  
    Buffer<T> input;  //输入缓冲区
    Beffer<T> output;  //输出缓冲区
    initFiles(inputFile,outputFile,in,out);  //初始化输入输出文件
    MinHeap<T> H(A,n);  //建立最小值堆
    initMinHeapArry(inputFile,n,A);  //从输入文件读取n个数据并初始化最小值堆
    initInputBuffer(input,inputFile);  //初始化输入缓冲区,读入一部分数据
    for(int last = n - 1;last >= 0;)
    {
        mval = H.heapArray[0];  //获得堆顶最小值
        sendToOutputBuffer(input,output,inputFile,outputFile,mval);  //把mval送到输出缓冲区
        input.read(r);  //从输入缓冲区读取一个数据
        if(!less(r,mval){
            H.heapArray[0] = r;  //如果r大于mval,r放到堆顶
        }
        else{
            H.heapArray[0] = H.heapArray[last];
            H.heapArray[last] = r;
            H.setSize(last);
            last --;
            }
        last --;
        if(last != 0) 
            H.SiftDown(0);  //重建堆
    }
    endUp(output,inputFile,outputFile);  //处理输出缓冲区,关闭输入输出文件
}

用这个方法得到的顺串的长度是不确定的,最短是M(恰好逆序),最长可能和文件一样长(恰好顺序),平均是 2M

二路外排序

过程

  • 把数据文件划分成若干段
  • 用内排序方法将各段排序,变成顺串
  • 逐躺合并顺串,直到变成一个顺串

策略

  • 创建尽可能大的初始顺串
  • 把初始顺串长度视为权,利用Huffman方法优化归并

多路归并

  • k路归并 每次将 k 个顺串合并排序成一个顺串。如果共有m个顺串,需要的归并趟数为 logkm
  • 选择树 一种完全二叉树,多路归并中用到的数据类型,分为赢者树和败者树。

用赢者树多路归并

赢者树示意图

这里写图片描述

赢者树类的实现

template<class T>
class WinnerTree(){
private:
    int MaxSize;  //最大选手数
    int n;  //当前选手数
    int LowExt;  //最底层外部结点数
    int offset;  //最底层之上的结点数
    int *B;  //赢者树存储数组
    int *L;  //叶结点数组
    void Play(int p,int lc,int rc,int(*winner)(T A[],int b,int c));  //从内部结点B[p]处开始从右分支向上比赛
public:
    WinnerTree(int Maxsize); 
    ~WinnerTree(){delete [] B;}
    void Initialize(T A[],int size,int(*winner)(T A[],int b, int c));  //初始化赢者树
    int Winner();  //返回赢者索引
    void RePlay(int i,int(*winner)(T A[],int b, int c));  //外部L[i]改变之后重建赢者树
};
  • 用数组实现赢者树,并且区分内部结点和叶结点
  • 选手用叶结点表示,每场比赛的结果(赢者的索引)用内部结点记录
  • 一个选手的分值改变(下一个记录),只需要改变这个结点到根节点经过路径上的结点。

用败者树多路归并

  • 比赛的过程和赢者树一样
  • 关键的区别就是父结点记录的是败者的索引
  • 根结点处添加一个结点记录胜者
  • 优点是方便重构:重构时胜者只需要和上一级的父结点比赛(赢者树还要找兄弟结点比赛)
  • k 个顺串,生成长度n的文件,赢者树的时间复杂度 O(kn) ,败者树 O(nlogk)

主要参考Wang Tengjiao课件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值