外部排序C++版

     排序是编程中很常用的操作。当要排序的数据量很大,无法一次装入内存时,就要使用外部排序。
     外部排序通常的做法是先把数据分成多个可以一次装入内存的小段,对这些段分别使用内部排序,将排好序的段依次写入磁盘,再进行多路归并。
     多路归并通常用败者树来加速,但既然stl里有现成的priority_queue,我们可以偷个懒,不去重复发明轮子。
     废话少说,我们直接上程序。
 
     首先是数据生成器,不做其他什么操作,只是像data.dat中写入一些随机数,注意我们这里生成的全是正整数,运行这个程序一段时间以后我们按下ctrl+C。
 
 1 #include <fstream>
 2 #include <ctime>
 3 #include <cstdlib>
 4 using namespace std;
 5 
 6 int main()
 7 {
 8      ofstream os("data.dat");
 9      srand(time(NULL));
10      while(true)
11           os << rand() << endl;
12 }

 

 
接下来是我们的主要程序:
 
  1 #include <iostream>
  2 #include <fstream>
  3 #include <sstream>
  4 #include <cmath>
  5 #include <algorithm>
  6 #include <cstdio>
  7 #include <map>
  8 #include <vector>
  9 #include <queue>
 10 
 11 class ExternalSort
 12 {
 13      //分段排序并写入文件
 14      struct FilePart
 15      {
 16           int index;
 17           std::string file;
 18           std::ifstream in;
 19           int next;
 20           FilePart(int i) : index(i), file(""), in(), next(-2)
 21           {
 22                std::ostringstream os;
 23                os << ".temp." << index << ".dat";
 24                file = os.str();
 25           }
 26           void writeFile(int* array, int sz)
 27           {
 28                std::cout  << file << " created" << std::endl;
 29                std::ofstream of(file.c_str());
 30                for(int i = 0; i < sz; i++)
 31                     of << array[i] << std::endl;     
 32                in.open(file.c_str());
 33           }
 34 
 35           int peek()
 36           {
 37                if(next == -2)
 38                     getNext();
 39                return next;
 40           }          
 41 
 42           int getNext()
 43           {
 44                in >> next;
 45                if(!in)
 46                {
 47                     next = -1;
 48                     return next;
 49                }
 50                return next;
 51           }
 52           ~FilePart()
 53           {
 54                if(remove(file.c_str())== 0)//删除临时文件
 55                     std::cout << file << " deleted" << std::endl;
 56           }
 57      };
 58 
 59 //比较器,注意这里用的是大于,这样使得优先队列每次top都访问到拥有最小的next的FilePart
 60      struct Comparator 
 61      {
 62           bool operator()(FilePart* fp1, FilePart* fp2)
 63           {
 64                return fp1->peek() > fp2->peek();
 65           }
 66      };
 67      const static int READ_NUM = 10000000;//每次读取一千万个数
 68      std::ifstream input;
 69      int k;
 70      std::vector<FilePart*> forDelete;
 71 public:
 72      ExternalSort(const char* fn) : input(fn), forDelete() {}
 73 
 74      void sort()
 75      {
 76           split();
 77           merge();
 78      }
 79      //快速排序
 80      void quickSort(int* array, int a, int b)
 81      {
 82           if(a < b)
 83           {
 84                int mid = partition(array, a, b);
 85                quickSort(array, a, mid - 1);
 86                quickSort(array, mid + 1, b);
 87           }
 88      }
 89 
 90      ~ExternalSort()
 91      {
 92           for(std::vector<FilePart*>::iterator it = forDelete.begin(); it != forDelete.end(); ++it)
 93                if(*it)
 94                     delete *it;
 95      }
 96      
 97 private:
 98      int partition(int* array, int a, int b)
 99      {
100           int key = array[b];
101           int index = a;
102           for(int i = a; i < b; i++)
103                if(array[i] < key)
104                     std::swap(array[i], array[index++]);
105           std::swap(array[b], array[index]);
106           return index;
107      }
108 
109      void merge()
110      {
111           std::priority_queue<FilePart*, std::vector<FilePart*>, Comparator> pq(forDelete.begin(), forDelete.end());
112           std::ofstream os("output.dat");
113           while(pq.size())
114           {
115                FilePart* fp = pq.top();//取得拥有最小的next的FilePart
116                pq.pop();//从队列中删除它
117                int num = fp->peek();
118                os << num << std::endl;//写入输出文件
119                if(fp->getNext() != -1)//取得下一个值,由于我们的数据都是正整数,得到-1意味着已到达文件末尾
120                     pq.push(fp);//如果有下一个,就将它再次加入队列
121           }     
122      }
123      
124      void split()
125      {
126           k = 0;
127           while(input)
128           {
129                int* buf = new int[READ_NUM];
130                int i = 0;
131                for(; i < READ_NUM && input; i++)
132                     input >> buf[i];
133                if(!input)
134                     i--;
135                if(i == 0)
136                     continue;
137                quickSort(buf, 0, i - 1);//排序
138                FilePart* fp = new FilePart(k);
139                forDelete.push_back(fp);
140                fp->writeFile(buf, i);//写入临时文件
141                k++;
142                delete[] buf;
143           }     
144      }
145 };

 


 
main函数:
int main(int argc, char** argv)
{
     if(argc != 2)
     {
          cout << "Usage sort <file>" << endl;
          exit(1);
     }
     ExternalSort es(argv[1]);
     clock_t c1 = clock();
     es.sort();
     clock_t c2 = clock();
     float t = (c2 - c1) / CLOCKS_PER_SEC;
     cout << "Sort done in " << t << " seconds." << endl;
}

 

 
测试电脑比较烂,2.6G的测试数据花了了1378秒才跑完
posted on 2012-07-18 18:55  心不在焉  阅读(1609)  评论(0)    收藏  举报