【C++】快排实现外部排序

提示:算法实践实验


一、问题描述

利用快排实现外部排序

二、算法思想

在这里插入图片描述

1.将存储划分为四个部分,首先从disk中读取数据写满middle
2.接下来的数据读满input,依次与middle中数据比较,若大于middle中最大值放入large,若小于middle中最小值放入small,若处于中间值,将middle中最小值放入small
3.若small或large写满则写回disk
4.全部读取并处理完成后将small,middle,large中剩余数据全部写回disk中
5.此时middle中写回的数据已经有序,再递归地处理两侧的数据直至全部处理完成


三、函数设计

Middle

由于middle中要取最大值或最小值与读入数据进行比较,因此,采用最大最小堆,可以方便的得出所需值,用vector数组来存储其中的数据,方便增删,不受大小影响

int min();//取最大值
int max();//取最小值
void insert(int val);//插入新值
void delete_min();//删除最小值
void delete_max();//删除最大值

(其余函数请见最后部分的详细代码)

Disk

Cache

首先考虑input,large,small,temp,temp2均采用cache结构体,其中包含的内容有:

int cache[CACHE_SIZE];
    int size=CACHE_SIZE;//指可以存的最大长度
    int diskStart=0;//对应磁盘的开始位置
    int diskEnd=0;
    int startPoint=0;
    int endPoint=-1;//用来计算cache中存了多少数据

实现函数

在这里插入图片描述
(1)关键问题
Q1:disk中数据如何存储
使用txt文件进行存储,每个数字占用一行,便于读取和修改。
Q2:small和large怎么写回?
使用int型变量diskStart和diskEnd记录所对应的在磁盘上的位置,写回时从diskStart所对应行开始写入即可。
Q3:large写回时覆盖未读取的数据
因此引入了temp,大小与large相同,当将large写回时,若所对应处的数据还未读取入input,则将其先存储在temp中,同时,每次处理数据时先处理temp,再处理input。
Q4:处理temp中数据时large再次需要写回
因此引入了temp2,此情况发生时先将数据存入temp2中,处理数据顺序为temp,temp2,input,由于large与temp大小相同,因此不会再出现两个temp都满了又覆盖数据的情况。
Q5:由于覆盖提前处理的数据再次读入input
因此引入了end,代表数据读取的结束位置,每次large写入,则前移写入数据的长度,防止再次读取。
Q6:cache采用数组只能覆盖数据不能删除,如何防止数据被重复处理
每次处理后将endpoint设置为-1,代表无将要处理的数据。
(2)函数及注释

class Disk
{
    friend class min_max_heap;
public:
    Disk();
    void adddiskIOCount();
    void readToInputCache(int start,int length);//从disk读到inputcache中
    void readToTempCache(Cache&tempCache,int start,int end);//从disk读到temp中
    void readFromInputCache(Cache&tempCache,int cur);//把input中的数据读进去
    void readToMiddle(int start,int length);//从disk直接读到middle中
    void writeFromCache(Cache&smallCache,int start,bool isSmall);//smallcache中数据写回disk
    void writeFromLargeCache(int end,int cur);//largecache中数据写回disk
    void writeFromMiddle(min_max_heap middleCache,int start,int end);//把middle里的写回
    void quickSort(int start,int end);
    std::string charToStr(char*contentChar);
    int getdiskIOCount();
private:
    Cache inputCache;
    Cache smallCache;
    Cache largeCache;
    Cache tempCache;//用于存放后面被占去位置的数据
    Cache tempCache2;
    min_max_heap middleCache;
    int diskIOCount=0;
};

四、测试结果和分析

有序/无序数据

有序

递减:
在这里插入图片描述
所得:
在这里插入图片描述
在这里插入图片描述
递增:
在这里插入图片描述
所得:
在这里插入图片描述
在这里插入图片描述

无序(随机生成)

在这里插入图片描述
所得:
在这里插入图片描述
在这里插入图片描述

大/小数据量

小数据

在这里插入图片描述
在这里插入图片描述

大数据

在这里插入图片描述
在这里插入图片描述

不同存储空间分配

较小的Cache

在这里插入图片描述
在这里插入图片描述

较大的Cache

在这里插入图片描述
在这里插入图片描述

五、项目总结

通过对结果的分析可以得到和内部快排相似的结论,有序的数据反而会耗费更多的io次数与更多时间,不适合使用快排。而随着数据量的增大,io次数也是呈爆炸式增长,我们可以得知对于磁盘上的数据处理,限制不再是cpu而是io次数。
对于整个实验过程,深深让我体会到不积跬步无以至千里的深刻道理,一开始没有在意边界问题,导致end指针究竟是指向最后一个数字还是最后一个数字后面在各个函数中没有统一,多个函数在递归中的反复调用造成了更多的bug,修改过程中也是极为痛苦的,看似没有大问题,实则处处是问题,在一次次的实验中失去了脾气,不知道测试了多少遍、打了多少断点,终于完成了这个实验。

思路上有参考:学长的文章

六、代码参考

Disk中关键函数实现:

#include "Disk.hpp"
#include<iostream>
#include<fstream>
#include<cstdlib>
#include<ctime>
#include<algorithm>

using namespace std;

Disk::Disk()
{
    ofstream outfile;
    outfile.open("quicksort.txt");
    srand(time(NULL));
    for(int i=0;i<DISK_SIZE;i++)
        outfile<<rand()%1000<<endl;
    outfile.close();
}
int Disk::getdiskIOCount()
{
    return diskIOCount;
}
void Disk::adddiskIOCount()
{
    diskIOCount++;
}
void Disk::readToInputCache(int start,int length)
{
    adddiskIOCount();
    ifstream infile;
    infile.open("quicksort.txt");
    char buf[1024];
    for(int i=0;i<start;i++)
        infile.getline(buf,sizeof(buf));
    for(int i=0;i<length;i++)
    {
        infile>>inputCache.cache[i];
    }
    inputCache.diskStart=start;
    inputCache.diskEnd=start+length-1;
    inputCache.endPoint=length-1;
    infile.close();
}
void Disk::readToTempCache(Cache&tempCache,int start, int end)
{
    adddiskIOCount();
    ifstream infile;
    infile.open("quicksort.txt");
    char buf[1024];
    for(int i=0;i<start;i++)
        infile.getline(buf,sizeof(buf));
    for(int i=0;i<=end-start;i++)
    {
        infile>>tempCache.cache[i];
    }
    tempCache.diskStart=start;
    tempCache.diskEnd=end;
    tempCache.endPoint=end-start;
    infile.close();
}
void Disk::readFromInputCache(Cache&tempCache,int cur)
{
        for(int i=tempCache.startPoint;i<=tempCache.endPoint;i++)
        {
            if(tempCache.cache[i]<=middleCache.min())
            {
                smallCache.endPoint++;
                if(smallCache.endPoint==smallCache.size)
                {
                    writeFromCache(smallCache,smallCache.diskStart,true);
                    smallCache.endPoint=0;
                    smallCache.diskStart+=smallCache.size;
                    smallCache.diskEnd=smallCache.diskStart;
                }
                smallCache.cache[smallCache.endPoint]=tempCache.cache[i];
                smallCache.diskEnd=smallCache.diskStart+smallCache.endPoint;
            }
            else if (tempCache.cache[i]>=middleCache.max())
            {
                largeCache.endPoint++;
                if(largeCache.endPoint==largeCache.size)
                {
                    writeFromLargeCache(largeCache.diskEnd,cur);
                    largeCache.endPoint=0;
                    largeCache.diskEnd-=largeCache.size;
                    largeCache.diskStart=largeCache.diskEnd;
                }
                largeCache.cache[largeCache.endPoint]=tempCache.cache[i];
                largeCache.diskStart=largeCache.diskEnd-largeCache.endPoint;
            }
            else{
                int small=middleCache.min();
                middleCache.delete_min();
                middleCache.insert(tempCache.cache[i]);
                smallCache.endPoint++;
                if(smallCache.endPoint==smallCache.size)
                {
                    writeFromCache(smallCache,smallCache.diskStart,true);
                    smallCache.endPoint=0;
                    smallCache.diskStart+=smallCache.size;
                    smallCache.diskEnd=smallCache.diskStart;
                }
                smallCache.cache[smallCache.endPoint]=small;
//                int large=middleCache.max();
//                middleCache.delete_max();
//                middleCache.insert(tempCache.cache[i]);
//                largeCache.endPoint++;
//                if(largeCache.endPoint==largeCache.size)
//                {
//                    writeFromLargeCache(largeCache.diskEnd,cur);
//                    largeCache.endPoint=0;
//                    largeCache.diskEnd-=largeCache.size;
//                    largeCache.diskStart=largeCache.diskEnd;
//                }
//                largeCache.cache[largeCache.endPoint]=large;
            }
        }
    tempCache.endPoint=-1;
}
void Disk::readToMiddle(int start,int length)
{
    adddiskIOCount();
    ifstream infile;
    infile.open("quicksort.txt");
    char buf[1024];
    for(int i=0;i<start;i++)
        infile.getline(buf,sizeof(buf));
    for(int i=0;i<length;i++)
    {
        string yyh;
        getline(infile,yyh);
        middleCache.insert(atoi(yyh.c_str()));
    }
    infile.close();
    middleCache.diskStart=start;
    middleCache.diskEnd=start+length-1;
}
void Disk::writeFromCache(Cache&smallCache,int start,bool isSmall)
{
    adddiskIOCount();
    ifstream infile;
    char buf[1024];
    infile.open("quicksort.txt");
    int i=0;
    string tempStr;
    while(infile.getline(buf,sizeof(buf)))
    {
        if(i!=start)
        {
//            cout<<buf<<endl;
            tempStr+=charToStr(buf)+'\n';
            i++;
//            cout<<i<<":"<<tempStr;
        }
        else{
            for(int j=0;j<smallCache.endPoint-1;j++)
            {
                string omg=to_string(smallCache.cache[j]);
                tempStr+=omg+'\n';
//                cout<<i+j<<":"<<tempStr<<endl;
                infile.getline(buf,sizeof(buf));
            }
            string omg=to_string(smallCache.cache[smallCache.endPoint-1]);
            tempStr+=omg+'\n';
            i+=smallCache.endPoint;
        }
    }
    infile.close();
//    cout<<tempStr;
    ofstream outfile;
    outfile.open("quicksort.txt");
    outfile<<tempStr;
    outfile.close();
    if(isSmall)
    {
        middleCache.diskStart+=smallCache.endPoint;
        middleCache.diskEnd+=smallCache.endPoint;
    }
    smallCache.endPoint=-1;
}
string Disk::charToStr(char*contentChar)
{
    string tempStr;
    for(int i=0;contentChar[i]!='\0';i++)
        tempStr+=contentChar[i];
    return tempStr;
}
void Disk::writeFromLargeCache(int end,int cur)
{
    int start=end-largeCache.endPoint+1;
    if(cur<start)
    {
        if(tempCache.endPoint==tempCache.size-1)
            readToTempCache(tempCache2, start, end);
        else readToTempCache(tempCache,start, end);
    }
    else{
        if(tempCache.endPoint==tempCache.size-1)
            readToTempCache(tempCache2, cur, end);
        else readToTempCache(tempCache,cur, end);
//        readToTempCache(cur, end);
    }
    writeFromCache(largeCache, start,false);
}
void Disk::writeFromMiddle(min_max_heap middleCache, int start, int end)
{
    adddiskIOCount();
    ifstream infile;
    char buf[1024];
    infile.open("quicksort.txt");
    int i=0;
    string tempStr;
    while(infile.getline(buf,sizeof(buf)))
    {
        if(i!=start)
        {
            tempStr+=charToStr(buf)+'\n';
            i++;
        }
        else{
            for(int j=start;j<end;j++)
            {
                string omg=to_string(middleCache.min());
                tempStr+=omg+'\n';
                middleCache.delete_min();
                infile.getline(buf,sizeof(buf));
            }
            string omg=to_string(middleCache.min());
            tempStr+=omg+'\n';
            middleCache.delete_min();
            i+=end-start+1;
        }
    }
    infile.close();
    ofstream outfile;
    outfile.open("quicksort.txt");
    outfile<<tempStr;
    outfile.close();
}
void Disk::quickSort(int start,int end)
{
    int cur=start;
    int end2=end;
    smallCache.diskStart=start;
    smallCache.diskEnd=start;
    largeCache.diskStart=end;
    largeCache.diskEnd=end;
//    middleCache.diskStart=(end-start+1-HEAP_SIZE)/2;
//    middleCache.diskEnd=(end-start+1+HEAP_SIZE)/2;不对,不一定在中间
    readToMiddle(cur, min(end-start+1,HEAP_SIZE));
    cur+=min(end-start+1,HEAP_SIZE);
    while(cur<=end)
    {
        readToInputCache(cur,min(end-start+1,CACHE_SIZE));
        //需要考虑如果到middle就读满了怎么办
        if(tempCache.endPoint!=-1)
        {
            end-=tempCache.endPoint+1;
            readFromInputCache(tempCache,cur);
            tempCache.endPoint=-1;
        }
        if(tempCache2.endPoint!=-1)
        {
            end-=tempCache2.endPoint+1;
            readFromInputCache(tempCache2,cur);
            tempCache2.endPoint=-1;
        }
        if(cur<=end)
            readFromInputCache(inputCache,cur);
        cur+=min(end-start,CACHE_SIZE);
    }
    
    if(smallCache.endPoint>=0)
    {
        smallCache.endPoint++;
        writeFromCache(smallCache,smallCache.diskStart,true);//把small里的写回disk
//        smallCache.endPoint=-1;
    }
    writeFromMiddle(middleCache, middleCache.diskStart, middleCache.diskEnd);
    for(int i=0;i<=middleCache.diskEnd-middleCache.diskStart;i++)
        middleCache.delete_min();
    if(largeCache.endPoint>=0)
    {
        largeCache.endPoint++;
        int start=largeCache.diskEnd-largeCache.endPoint+1;
        writeFromCache(largeCache, start,false);
//        largeCache.endPoint=-1;
    }
    
    int a=middleCache.diskStart;
    int b=middleCache.diskEnd;
    if((a-start)>1)
        quickSort(start, a-1);
    if((end2-b)>1)
        quickSort(b+1, end2);
    
    
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值