微软笔试题 大型文件外部排序(二路归并和k路归并的实现和比较)

这两种排序方法都是先将一个无序的大的外部文件,分成若干块,分别读到内存中。

将每一块都先排好序,放到一个新的外部文件中。

  1. 二路归并的思路是每次将外部排序的文件两两合并,变成一个二倍大小的文件,然后对二倍大小的文件继续两两合并。直到最终合并为一个文件为止。
  2. k路归并是将外部排好序的子文件一次合并。先在各个文件中取出第一个数据,放到一个优先级队列中。然后选出最小的数据输出到外部结果文件里。并从最小数据对应的文件中读取下一个数据。这种方法的关键在于,要将每次从文件中读到的数据和对应的文件关联起来。这样才可以持续的读取。另一个重要的地方在于,当一个文件读取结束时,放置一个最大的数据MAX到优先级队列中当做标记。当某一次从优先级队列中读到的数据是MAX时,表明所有文件都已经读取完毕。结果文件输出完成。

二路归并的C++代码:

[cpp] view plaincopyprint?
  1. ////对n(10000000)个整数排序,采用二路归并的方法。每次总是将两个文件合并排序为一个。  
  2. string get_file_name(int count_file) 
  3.     stringstream s; 
  4.     s<<count_file; 
  5.     string count_file_string; 
  6.     s>>count_file_string; 
  7.     string file_name="data"
  8.     file_name+=count_file_string; 
  9.     return file_name; 
  10. ////用二路归并的方法将n个整数分为每个大小为per的部分。然后逐级递增的合并  
  11. //void push_random_data_to_file(const string& filename ,unsigned long number)  
  12. //{  
  13. //      if (number<100000)  
  14. //      {  
  15. //          vector<int> a;  
  16. //          push_rand(a,number,0,number);  
  17. //          write_data_to_file(a,filename.c_str());   
  18. //      }   
  19. //      else  
  20. //      {  
  21. //          vector<int> a;  
  22. //          const int per=100000,n=number/per;  
  23. //          push_rand(a,number%per,0,number);  
  24. //          write_data_to_file(a,filename.c_str());   
  25. //          for (int i=0;i<n;i++)  
  26. //          {  
  27. //              a.clear();  
  28. //              push_rand(a,100000,0,100000);  
  29. //              write_data_append_file(a,filename.c_str());   
  30. //          }  
  31. //      }  
  32. //}  
  33. //void split_data(const string& datafrom,deque<string>& file_name_array,unsigned long per,int& count_file)  
  34. //{  
  35. //  unsigned long position=0;  
  36. //  while (true)      
  37. //  {  
  38. //      vector<int> a;  
  39. //      a.clear();  
  40. //      //读文件中的一段数据到数组中   
  41. //      if (read_data_to_array(datafrom,a,position,per)==true)  
  42. //      {  
  43. //          break;  
  44. //      }  
  45. //      position+=per;  
  46. //      //将数组中的数据在内存中排序  
  47. //      sort(a.begin(),a.end());  
  48. //      ofstream fout;  
  49. //      string filename=get_file_name(count_file++);  
  50. //      file_name_array.push_back(filename);  
  51. //      fout.open(filename.c_str(),ios::in | ios::binary);  
  52. //      //将排好序的数组输出到外部文件中  
  53. //      write_data_to_file(a,filename.c_str());  
  54. //      print_file(filename);  
  55. //      fout.close();  
  56. //  }  
  57. //}  
  58. //void sort_big_file_with_binary_merge(unsigned long n,unsigned long per)  
  59. //{  
  60. //  unsigned  long traverse=n/per;  
  61. //  vector<int> a;  
  62. //  //制造大量数据放入文件中  
  63. //  cout<<"对"<<n<<"个整数进行二路归并排序,每一路的大小为"<<per<<endl  
  64. //      <<"全部数据被分割放在"<<traverse<<"个文件中"<<endl;  
  65. //    
  66. //  SingletonTimer::Instance();  
  67. //  //将待排序文件分成小文件,在内存中排序后放到磁盘文件中  
  68. //  string datafrom="data.txt";  
  69. //  deque<string> file_name_array;  
  70. //  int count_file=0;  
  71. //  split_data(datafrom,file_name_array,per,count_file);  
  72. //  
  73. //  SingletonTimer::Instance()->print("将待排序文件分成小文件,在内存中排序后放到磁盘文件中");  
  74. //  //合并排序,二路归并的方法。  
  75. //  while (file_name_array.size()>=2)  
  76. //  {  
  77. //      //获取两个有序文件中的内容,将其合并为一个有序的文件,直到最后合并为一个有序文件  
  78. //      string file1=file_name_array.front();  
  79. //      file_name_array.pop_front();  
  80. //      string file2=file_name_array.front();  
  81. //      file_name_array.pop_front();  
  82. //      string fileout=get_file_name(count_file++);  
  83. //      file_name_array.push_back(fileout);  
  84. //      merge_file(file1,file2,fileout);  
  85. //      print_file(fileout);  
  86. //  }  
  87. //  SingletonTimer::Instance()->print("获取两个有序文件中的内容,将其合并为一个有序的文件,直到最后合并为一个有序文件");  
  88. //  cout<<"最终的文件中存放所有排好序的数据,其中前一百个为:"<<endl;  
  89. //  print_file(file_name_array.back(),100);  
  90. //  
  91. //} 
////对n(10000000)个整数排序,采用二路归并的方法。每次总是将两个文件合并排序为一个。
string get_file_name(int count_file)
{
	stringstream s;
	s<<count_file;
	string count_file_string;
	s>>count_file_string;
	string file_name="data";
	file_name+=count_file_string;
	return file_name;
}
////用二路归并的方法将n个整数分为每个大小为per的部分。然后逐级递增的合并
//void push_random_data_to_file(const string& filename ,unsigned long number)
//{
//		if (number<100000)
//		{
//			vector<int> a;
//			push_rand(a,number,0,number);
//			write_data_to_file(a,filename.c_str()); 
//		} 
//		else
//		{
//			vector<int> a;
//			const int per=100000,n=number/per;
//			push_rand(a,number%per,0,number);
//			write_data_to_file(a,filename.c_str()); 
//			for (int i=0;i<n;i++)
//			{
//				a.clear();
//				push_rand(a,100000,0,100000);
//				write_data_append_file(a,filename.c_str()); 
//			}
//		}
//}
//void split_data(const string& datafrom,deque<string>& file_name_array,unsigned long per,int& count_file)
//{
//	unsigned long position=0;
//	while (true)	
//	{
//		vector<int> a;
//		a.clear();
//		//读文件中的一段数据到数组中 
//		if (read_data_to_array(datafrom,a,position,per)==true)
//		{
//			break;
//		}
//		position+=per;
//		//将数组中的数据在内存中排序
//		sort(a.begin(),a.end());
//		ofstream fout;
//		string filename=get_file_name(count_file++);
//		file_name_array.push_back(filename);
//		fout.open(filename.c_str(),ios::in | ios::binary);
//		//将排好序的数组输出到外部文件中
//		write_data_to_file(a,filename.c_str());
//		print_file(filename);
//		fout.close();
//	}
//}
//void sort_big_file_with_binary_merge(unsigned long n,unsigned long per)
//{
//	unsigned  long traverse=n/per;
//	vector<int> a;
//	//制造大量数据放入文件中
//	cout<<"对"<<n<<"个整数进行二路归并排序,每一路的大小为"<<per<<endl
//		<<"全部数据被分割放在"<<traverse<<"个文件中"<<endl;
//	
//	SingletonTimer::Instance();
//	//将待排序文件分成小文件,在内存中排序后放到磁盘文件中
//	string datafrom="data.txt";
//	deque<string> file_name_array;
//	int count_file=0;
//	split_data(datafrom,file_name_array,per,count_file);
//
//	SingletonTimer::Instance()->print("将待排序文件分成小文件,在内存中排序后放到磁盘文件中");
//	//合并排序,二路归并的方法。
//	while (file_name_array.size()>=2)
//	{
//		//获取两个有序文件中的内容,将其合并为一个有序的文件,直到最后合并为一个有序文件
//		string file1=file_name_array.front();
//		file_name_array.pop_front();
//		string file2=file_name_array.front();
//		file_name_array.pop_front();
//		string fileout=get_file_name(count_file++);
//		file_name_array.push_back(fileout);
//		merge_file(file1,file2,fileout);
//		print_file(fileout);
//	}
//	SingletonTimer::Instance()->print("获取两个有序文件中的内容,将其合并为一个有序的文件,直到最后合并为一个有序文件");
//	cout<<"最终的文件中存放所有排好序的数据,其中前一百个为:"<<endl;
//	print_file(file_name_array.back(),100);
//
//}

k路归并的C++代码:

[cpp] view plaincopyprint?
  1. ////k路归并排序大文件1000*10000  
  2. //  
  3. //void write_random_data_to_file(unsigned long number)  
  4. //{  
  5. //  cout<<"writing "<<number<<" to file data ..."<<endl;  
  6. //  unsigned  long traverse=number/100000;  
  7. //  cout<<traverse<<"s times have to write."<<endl;  
  8. //  ////制造大量数据放入文件中  
  9. //  vector<int> a;  
  10. //  if (number<100000)  
  11. //  {  
  12. //      push_rand(a,number,0,number);  
  13. //      write_data_to_file(a,"data");   
  14. //  }  
  15. //  else  
  16. //  {  
  17. //      push_rand(a,100000,0,1000000);  
  18. //      write_data_to_file(a,"data");   
  19. //      cout<<"the "<<0<<" times finished."<<endl;  
  20. //      for (unsigned long i=1;i<traverse;i++)  
  21. //      {  
  22. //          a.clear();  
  23. //          push_rand(a,100000,0,100000);  
  24. //          write_data_append_file(a,"data");   
  25. //          cout<<"the "<<i<<" times finished."<<endl  
  26. //              <<(traverse-1-i)<<" times left."<<endl;  
  27. //      }  
  28. //  }  
  29. //  cout<<number<<" integers wrote to file data finished."<<endl;  
  30. //  ///////////////////TEST/////////////////  
  31. //  //print_file("data",100);  
  32. //  //sort(a.begin(),a.end());  
  33. //  //print(a.begin(),a.end());  
  34. //}  
  35. //list<string> divide_big_file_into_small_sorted_file(long number)  
  36. //{  
  37. //  vector<int> a;  
  38. //  a.clear();  
  39. //  long position=0;  
  40. //  int count_file=0;  
  41. //  list<string> file_name_array;  
  42. //  //get part files and file names  
  43. //  while (true)  
  44. //  {  
  45. //      a.clear();  
  46. //      if (read_data_to_array("data.txt",a,position,number)==true)  
  47. //      {  
  48. //          break;  
  49. //      }  
  50. //      position+=number;  
  51. //      sort(a.begin(),a.end());  
  52. //      string filename=get_file_name(count_file++);  
  53. //      file_name_array.push_back(filename);  
  54. //      write_data_to_file(a,filename.c_str());  
  55. //      cout<<"sorted file"<<(count_file-1)<<" builded."<<endl;  
  56. //  }  
  57. //  
  58. //  return file_name_array;  
  59. //}  
  60. //void k_way_merge_sort(const list<string>& file_name_array)  
  61. //{  
  62. //  
  63. //  //get ifstreams and put them to list<ifstream> readfiles  
  64. //  vector<ifstream> readfiles;  
  65. //  for (list<string>::const_iterator i=file_name_array.begin();  
  66. //      i!=file_name_array.end();i++)  
  67. //  {  
  68. //      readfiles.push_back(ifstream());  
  69. //      readfiles.back().open(i->c_str(),ios::binary | ios::in );  
  70. //  }  
  71. //  //init priority queue by read one data from each file  
  72. //  //初始化优先队列:从每个文件中读取第一个数据  
  73. //  priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int> > > prioritydata;  
  74. //  for (vector<ifstream>::size_type i=0;  
  75. //      i<readfiles.size();i++)  
  76. //  {  
  77. //      int temp;  
  78. //      readfiles[i].read(reinterpret_cast<char*>(&temp),sizeof(int));  
  79. //      prioritydata.push(make_pair(temp,i));  
  80. //  }  
  81. //  //merge sort file  
  82. //  ofstream fout;  
  83. //  fout.open("result",ios::binary);  
  84. //  while (true)  
  85. //  {  
  86. //      int onedata=prioritydata.top().first;  
  87. //      if (onedata==numeric_limits<int>().max())  
  88. //      {  
  89. //          break;  
  90. //      }  
  91. //      else  
  92. //      {  
  93. //  
  94. //          fout.write(reinterpret_cast<const char*>(&onedata),sizeof(int));  
  95. //          //从第i个文件中读取一个整数  
  96. //          int i=prioritydata.top().second;  
  97. //          prioritydata.pop();  
  98. //          int temp;  
  99. //          readfiles[i].read(reinterpret_cast<char*>(&temp),sizeof(int));  
  100. //          if (readfiles[i].eof())  
  101. //          {  
  102. //              //当此文件读到最后结束时,放入标记到优先级队列中  
  103. //              prioritydata.push(make_pair(numeric_limits<int>().max(),i));  
  104. //          }  
  105. //          else  
  106. //          {  
  107. //              //否则将读取到的数据直接放到优先级队列中  
  108. //              prioritydata.push(make_pair(temp,i));  
  109. //          }  
  110. //      }  
  111. //  }  
  112. //  //关闭所有打开的文件  
  113. //  fout.close();  
  114. //  for (vector<ifstream>::size_type i=0;  
  115. //      i<readfiles.size();i++)  
  116. //  {  
  117. //      readfiles[i].close();  
  118. //  }  
  119. //}  
  120. //void sort_big_file_with_k_way_merge(unsigned long n,unsigned long partitionfilesize)  
  121. //{  
  122. //    
  123. //  //write_random_data_to_file(n);  
  124. //  timer t;  
  125. //  k_way_merge_sort(divide_big_file_into_small_sorted_file(partitionfilesize));  
  126. //  //将待排序文件分成小文件,在内存中排序后放到磁盘文件中  
  127. //  //假设内存只有1MB,26W个整数  
  128. //  cout<<n/partitionfilesize<<"路归并排序大文件 "<<n<<" ,内存一次排序 "<<partitionfilesize<<endl;  
  129. //  print(t.elapsed());  
  130. //  print("秒");  
  131. //  print_file("result",1000);  
  132. //} 
////k路归并排序大文件1000*10000
//
//void write_random_data_to_file(unsigned long number)
//{
//	cout<<"writing "<<number<<" to file data ..."<<endl;
//	unsigned  long traverse=number/100000;
//	cout<<traverse<<"s times have to write."<<endl;
//	////制造大量数据放入文件中
//	vector<int> a;
//	if (number<100000)
//	{
//		push_rand(a,number,0,number);
//		write_data_to_file(a,"data"); 
//	}
//	else
//	{
//		push_rand(a,100000,0,1000000);
//		write_data_to_file(a,"data"); 
//		cout<<"the "<<0<<" times finished."<<endl;
//		for (unsigned long i=1;i<traverse;i++)
//		{
//			a.clear();
//			push_rand(a,100000,0,100000);
//			write_data_append_file(a,"data"); 
//			cout<<"the "<<i<<" times finished."<<endl
//				<<(traverse-1-i)<<" times left."<<endl;
//		}
//	}
//	cout<<number<<" integers wrote to file data finished."<<endl;
//	///////////////////TEST/////////////////
//	//print_file("data",100);
//	//sort(a.begin(),a.end());
//	//print(a.begin(),a.end());
//}
//list<string> divide_big_file_into_small_sorted_file(long number)
//{
//	vector<int> a;
//	a.clear();
//	long position=0;
//	int count_file=0;
//	list<string> file_name_array;
//	//get part files and file names
//	while (true)
//	{
//		a.clear();
//		if (read_data_to_array("data.txt",a,position,number)==true)
//		{
//			break;
//		}
//		position+=number;
//		sort(a.begin(),a.end());
//		string filename=get_file_name(count_file++);
//		file_name_array.push_back(filename);
//		write_data_to_file(a,filename.c_str());
//		cout<<"sorted file"<<(count_file-1)<<" builded."<<endl;
//	}
//
//	return file_name_array;
//}
//void k_way_merge_sort(const list<string>& file_name_array)
//{
//
//	//get ifstreams and put them to list<ifstream> readfiles
//	vector<ifstream> readfiles;
//	for (list<string>::const_iterator i=file_name_array.begin();
//		i!=file_name_array.end();i++)
//	{
//		readfiles.push_back(ifstream());
//		readfiles.back().open(i->c_str(),ios::binary | ios::in );
//	}
//	//init priority queue by read one data from each file
//	//初始化优先队列:从每个文件中读取第一个数据
//	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int> > > prioritydata;
//	for (vector<ifstream>::size_type i=0;
//		i<readfiles.size();i++)
//	{
//		int temp;
//		readfiles[i].read(reinterpret_cast<char*>(&temp),sizeof(int));
//		prioritydata.push(make_pair(temp,i));
//	}
//	//merge sort file
//	ofstream fout;
//	fout.open("result",ios::binary);
//	while (true)
//	{
//		int onedata=prioritydata.top().first;
//		if (onedata==numeric_limits<int>().max())
//		{
//			break;
//		}
//		else
//		{
//
//			fout.write(reinterpret_cast<const char*>(&onedata),sizeof(int));
//			//从第i个文件中读取一个整数
//			int i=prioritydata.top().second;
//			prioritydata.pop();
//			int temp;
//			readfiles[i].read(reinterpret_cast<char*>(&temp),sizeof(int));
//			if (readfiles[i].eof())
//			{
//				//当此文件读到最后结束时,放入标记到优先级队列中
//				prioritydata.push(make_pair(numeric_limits<int>().max(),i));
//			}
//			else
//			{
//				//否则将读取到的数据直接放到优先级队列中
//				prioritydata.push(make_pair(temp,i));
//			}
//		}
//	}
//	//关闭所有打开的文件
//	fout.close();
//	for (vector<ifstream>::size_type i=0;
//		i<readfiles.size();i++)
//	{
//		readfiles[i].close();
//	}
//}
//void sort_big_file_with_k_way_merge(unsigned long n,unsigned long partitionfilesize)
//{
//	
//	//write_random_data_to_file(n);
//	timer t;
//	k_way_merge_sort(divide_big_file_into_small_sorted_file(partitionfilesize));
//	//将待排序文件分成小文件,在内存中排序后放到磁盘文件中
//	//假设内存只有1MB,26W个整数
//	cout<<n/partitionfilesize<<"路归并排序大文件 "<<n<<" ,内存一次排序 "<<partitionfilesize<<endl;
//	print(t.elapsed());
//	print("秒");
//	print_file("result",1000);
//}

 

posted @ 2012-11-10 17:36  wenllsz  阅读(503)  评论(0编辑  收藏  举报