C++归并

1 //copyright@ 纯净的天空 && yansha
2  //5、July,updated,2010.05.28。
3 //harryshayne,update again。2011.6.30
4 #include <iostream>
5 #include <ctime>
6 #include <fstream>
7 //#include "ExternSort.h"
8 using namespace std;
9
10 //使用多路归并进行外排序的类
11 //ExternSort.h
12
13 /*
14 * 大数据量的排序
15 * 多路归并排序
16 * 以千万级整数从小到大排序为例
17 * 一个比较简单的例子,没有建立内存缓冲区
18 */
19
20 #ifndef EXTERN_SORT_H
21 #define EXTERN_SORT_H
22
23 #include <cassert>
24 //#define k 5
25 #define MIN -1//这里开始的时候出现了一个BUG,如果定义的MIN大于等于待排序的数,则会是算法出现错误
26 #define MAX 10000000//最大值,附加在归并文件结尾
27 typedef int* LoserTree;
28 typedef int* External;
29
30 class ExternSort
31 {
32 public:
33 void sort()
34 {
35 time_t start = time(NULL);
36
37 //将文件内容分块在内存中排序,并分别写入临时文件
38 k = memory_sort(); //
39
40 //归并临时文件内容到输出文件
41 //merge_sort(file_count);
42 ls=new int[k];
43 b=new int[k+1];
44 K_Merge();
45 delete []ls;
46 delete []b;
47
48 time_t end = time(NULL);
49 printf("total time:%f\n", (end - start) * 1000.0/ CLOCKS_PER_SEC);
50 }
51
52 //input_file:输入文件名
53 //out_file:输出文件名
54 //count: 每次在内存中排序的整数个数
55 ExternSort(const char *input_file, const char * out_file, int count)
56 {
57 m_count = count;
58 m_in_file = new char[strlen(input_file) + 1];
59 strcpy(m_in_file, input_file);
60 m_out_file = new char[strlen(out_file) + 1];
61 strcpy(m_out_file, out_file);
62 }
63 virtual ~ExternSort()
64 {
65 delete [] m_in_file;
66 delete [] m_out_file;
67 }
68
69 private:
70 int m_count; //数组长度
71 char *m_in_file; //输入文件的路径
72 char *m_out_file; //输出文件的路径
73 int k;//归并数,此数必须要内排序之后才能得到,所以下面的ls和b都只能定义为指针(注意和书上区别)
74 LoserTree ls;//定义成为指针,之后动态生成数组
75 External b;//定义成为指针,在成员函数中可以把它当成数组使用
76 //int External[k];
77 protected:
78 int read_data(FILE* f, int a[], int n)
79 {
80 int i = 0;
81 while(i < n && (fscanf(f, "%d", &a[i]) != EOF)) i++;
82 printf("read:%d integer\n", i);
83 return i;
84 }
85 void write_data(FILE* f, int a[], int n)
86 {
87 for(int i = 0; i < n; ++i)
88 fprintf(f, "%d ", a[i]);
89 fprintf(f,"%d",MAX);//在最后写上一个最大值
90 }
91 char* temp_filename(int index)
92 {
93 char *tempfile = new char[100];
94 sprintf(tempfile, "temp%d.txt", index);
95 return tempfile;
96 }
97 static int cmp_int(const void *a, const void *b)
98 {
99 return *(int*)a - *(int*)b;
100 }
101
102 int memory_sort()
103 {
104 FILE* fin = fopen(m_in_file, "rt");
105 int n = 0, file_count = 0;
106 int *array = new int[m_count];
107
108 //每读入m_count个整数就在内存中做一次排序,并写入临时文件
109 while(( n = read_data(fin, array, m_count)) > 0)
110 {
111 qsort(array, n, sizeof(int), cmp_int);
112 //这里,调用了库函数阿,在第四节的c实现里,不再调用qsort。
113 char *fileName = temp_filename(file_count++);
114 FILE *tempFile = fopen(fileName, "w");
115 free(fileName);
116 write_data(tempFile, array, n);
117 fclose(tempFile);
118 }
119
120 delete [] array;
121 fclose(fin);
122
123 return file_count;
124 }
125
126 void Adjust(int s)
127 {//沿从叶子节点b[s]到根节点ls[0]的路径调整败者树
128 int t=(s+k)/2;//ls[t]是b[s]的双亲节点
129 while(t>0)
130 {
131 if(b[s]>b[ls[t]])//如果失败,则失败者位置s留下,s指向新的胜利者
132 {
133 int tmp=s;
134 s=ls[t];
135 ls[t]=tmp;
136 }
137 t=t/2;
138 }
139 ls[0]=s;//ls[0]存放调整后的最大值的位置
140 }
141
142 void CreateLoserTree()
143 {
144 b[k]=MIN;//额外的存储一个最小值
145 for(int i=0;i<k;i++)ls[i]=k;//先初始化为指向最小值,这样后面的调整才是正确的
146 //这样能保证非叶子节点都是子树中的“二把手”
147 for(i=k-1;i>=0;i--)
148 Adjust(i);//依次从b[k-1],b[k-2]...b[0]出发调整败者树
149 }
150
151 void K_Merge()
152 {//利用败者数把k个输入归并段归并到输出段中
153 //b中前k个变量存放k个输入段中当前记录的元素
154 //归并临时文件
155 FILE *fout = fopen(m_out_file, "wt");
156 FILE* *farray = new FILE*[k];
157 int i;
158 for(i = 0; i < k; ++i) //打开所有k路输入文件
159 {
160 char* fileName = temp_filename(i);
161 farray[i] = fopen(fileName, "rt");
162 free(fileName);
163 }
164
165 for(i = 0; i < k; ++i) //初始读取
166 {
167 if(fscanf(farray[i], "%d", &b[i]) == EOF)//读每个文件的第一个数到data数组
168 {
169 printf("there is no %d file to merge!",k);
170 return;
171 }
172 }
173 // for(int i=0;i<k;i++)input(b[i]);
174
175 CreateLoserTree();
176 int q;
177 while(b[ls[0]]!=MAX)//
178 {
179 q=ls[0];//q用来存储b中最小值的位置,同时也对应一路文件
180 //output(q);
181 fprintf(fout,"%d ",b[q]);
182 //input(b[q],q);
183 fscanf(farray[q],"%d",&b[q]);
184 Adjust(q);
185 }
186 //output(ls[0]);
187 fprintf(fout,"%d ",b[ls[0]]);
188 //delete [] hasNext;
189 //delete [] data;
190
191 for(i = 0; i < k; ++i) //清理工作
192 {
193 fclose(farray[i]);
194 }
195 delete [] farray;
196 fclose(fout);
197 }
198 /*
199 void merge_sort(int file_count)
200 {
201 if(file_count <= 0) return;
202
203 //归并临时文件
204 FILE *fout = fopen(m_out_file, "wt");
205 FILE* *farray = new FILE*[file_count];
206 int i;
207 for(i = 0; i < file_count; ++i)
208 {
209 char* fileName = temp_filename(i);
210 farray[i] = fopen(fileName, "rt");
211 free(fileName);
212 }
213
214 int *data = new int[file_count];//存储每个文件当前的一个数字
215 bool *hasNext = new bool[file_count];//标记文件是否读完
216 memset(data, 0, sizeof(int) * file_count);
217 memset(hasNext, 1, sizeof(bool) * file_count);
218
219 for(i = 0; i < file_count; ++i) //初始读取
220 {
221 if(fscanf(farray[i], "%d", &data[i]) == EOF)//读每个文件的第一个数到data数组
222 hasNext[i] = false;
223 }
224
225 while(true) //循环读取和输出,选择最小数的方法是简单遍历选择法
226 {
227 //求data中可用的最小的数字,并记录对应文件的索引
228 int min = data[0];
229 int j = 0;
230
231 while (j < file_count && !hasNext[j]) //顺序跳过已读取完毕的文件
232 j++;
233
234 if (j >= file_count) //没有可取的数字,终止归并
235 break;
236
237
238 for(i = j +1; i < file_count; ++i) //选择最小数,这里应该是i=j吧!但结果是一样的!
239 {
240 if(hasNext[i] && min > data[i])
241 {
242 min = data[i];
243 j = i;
244 }
245 }
246
247 if(fscanf(farray[j], "%d", &data[j]) == EOF) //读取文件的下一个元素
248 hasNext[j] = false;
249 fprintf(fout, "%d ", min);
250
251 }
252
253 delete [] hasNext;
254 delete [] data;
255
256 for(i = 0; i < file_count; ++i)
257 {
258 fclose(farray[i]);
259 }
260 delete [] farray;
261 fclose(fout);
262 }
263 */
264 };
265
266 #endif
267
268
269 //测试主函数文件
270 /*
271 * 大文件排序
272 * 数据不能一次性全部装入内存
273 * 排序文件里有多个整数,整数之间用空格隔开
274 */
275
276 const unsigned int count = 10000000; // 文件里数据的行数
277 const unsigned int number_to_sort = 100000; //在内存中一次排序的数量
278 const char *unsort_file = "unsort_data.txt"; //原始未排序的文件名
279 const char *sort_file = "sort_data.txt"; //已排序的文件名
280 void init_data(unsigned int num); //随机生成数据文件
281
282 int main(int argc, char* *argv)
283 {
284 srand(time(NULL));
285 init_data(count);
286 ExternSort extSort(unsort_file, sort_file, number_to_sort);
287 extSort.sort();
288 system("pause");
289 return 0;
290 }
291
292 void init_data(unsigned int num)
293 {
294 FILE* f = fopen(unsort_file, "wt");
295 for(int i = 0; i < num; ++i)
296 fprintf(f, "%d ", rand());
297 fclose(f);
298 }
posted @ 2011-10-05 15:52  两色天  阅读(317)  评论(0编辑  收藏  举报