外排序(大數據文件排序)

內排序方法的共同特點是在排序的過程中所有數據都在內存中。但是當待排序的記錄數目特別多時,在內存中不能一次處理。必須把他們以文件的形式存放於外存,排序時再把他們一部分一部分地調入內存進行處理。這樣,在排序過程中必須不斷地在內存與外存之間傳送數據。這種基於外部儲存設備(或文件)的排序技術就是外排序。

操作系統讀寫磁盤所需的時間遠遠超過內存運算時間,基於磁盤(文件)進行的排序多使用歸併排序方法。排序分爲兩個階段:

  1. 第一階段建立爲外排序所用的內存緩衝區。根據他們的大小將輸入文件劃分爲若干段,用某種有效的內排序方法(例如本博客中用快速排序),對各段進行排序。這些排序後的排序段,寫到外存中。
  2. 利用二路歸併思想,把第一階段生成的初始段加以歸併,一趟趟地擴大歸併段和減少歸併段的個數,直到最後歸併成一個大歸併段(有序文件)爲止。
現在以一個示例加以說明(包含提供完整代碼),對2百萬個數字進行排序(可以自己編寫程序隨機生成2百萬個數字)。

利用簡單的兩路歸併函數merge對兩個歸併段進行歸併時,僅需把這兩個歸併段中的記錄逐塊讀入內存,所以這種方法能夠對很大的歸併段進行排序。

void merge(int numberOfSegments, int segmentSize,char* f1, char* f2, char* f3)
{
  if (numberOfSegments > 1)
  {
    mergeOneStep(numberOfSegments, segmentSize, f1, f2, f3);
    merge((numberOfSegments + 1) / 2, segmentSize * 2, f3, f1, f2);
  }
  else
  { // rename f1 as the final sorted file
    copyFile(f1, "sortedlargedata.dat");
    cout << "\nSorted into the file sortedlargedata.dat" << endl;
  }
}
其中mergeOneStep函數,以及copyFile函數如下:

void mergeOneStep(int numberOfSegments, int segmentSize, char* f1,char* f2, char* f3)
{
  fstream f1Input;
  f1Input.open(f1, ios::in | ios::binary);

  fstream f2Output;
  f2Output.open(f2, ios::out | ios::binary);

  // Copy half number of segments from f1.dat to f2.dat
  copyHalfToF2(numberOfSegments, segmentSize, f1Input, f2Output);
  f2Output.close();

  // Merge remaining segments in f1 with segments in f2 into f3
  fstream f2Input;
  f2Input.open(f2, ios::in | ios::binary);
  fstream f3Output;
  f3Output.open(f3, ios::out | ios::binary);

  mergeSegments(numberOfSegments / 2, segmentSize, f1Input, f2Input, f3Output);

  f1Input.close();
  f2Input.close();
  f3Output.close();
}
void copyHalfToF2(int numberOfSegments, int segmentSize, fstream &f1,
  fstream &f2)
{
  for (int i = 0; i < (numberOfSegments / 2) * segmentSize; i++)
  {
    int value;
    f1.read(reinterpret_cast<char *> (& value), sizeof(value));
    f2.write(reinterpret_cast<char *> (& value), sizeof(value));
  }
}
void mergeSegments(int numberOfSegments, int segmentSize, fstream &f1,fstream &f2, fstream &f3)
{
  for (int i = 0; i < numberOfSegments; i++)
  {
    mergeTwoSegments(segmentSize, f1, f2, f3);
  }

  // f1 may have one extra segment, copy it to f3
  while (!f1.eof())
  {
    int value;
    f1.read(reinterpret_cast<char *> (& value), sizeof(value));
    if (f1.eof()) break;
    f3.write(reinterpret_cast<char *> (& value), sizeof(value));
  }
}
void mergeTwoSegments(int segmentSize, fstream &f1, fstream &f2,fstream &f3)
{
  int intFromF1;
  f1.read(reinterpret_cast<char *> (& intFromF1), sizeof(intFromF1));
  int intFromF2;
  f2.read(reinterpret_cast<char *> (& intFromF2), sizeof(intFromF2));
  int f1Count = 1;
  int f2Count = 1;

  while (true)
  {
    if (intFromF1 < intFromF2)
    {
      f3.write(reinterpret_cast<char *>(&intFromF1), sizeof(intFromF1));
      if (f1.eof() || f1Count++ >= segmentSize)
      {
        if (f1.eof()) break;
        f3.write(reinterpret_cast<char *>(&intFromF2), sizeof(intFromF2));
        break;
      }
      else
      {
        f1.read(reinterpret_cast<char *> (& intFromF1), sizeof(intFromF1));
      }
    }
    else
    {
      f3.write(reinterpret_cast<char *>(&intFromF2), sizeof(intFromF2));
      if (f2.eof() || f2Count++ >= segmentSize)
      {
        if (f2.eof()) break;
        f3.write(reinterpret_cast<char *>(&intFromF1), sizeof(intFromF1));
        break;
      }
      else {
        f2.read(reinterpret_cast<char *> (& intFromF2), sizeof(intFromF2));
      }
    }
  }

  while (!f1.eof() && f1Count++ < segmentSize) {
    int value;
    f1.read(reinterpret_cast<char *> (& value), sizeof(value));
    if (f1.eof()) break;
    f3.write(reinterpret_cast<char *> (& value), sizeof(value));
  }

  while (!f2.eof() && f2Count++ < segmentSize) {
    int value;
    f2.read(reinterpret_cast<char *> (& value), sizeof(value));
    if (f2.eof()) break;
    f3.write(reinterpret_cast<char *> (& value), sizeof(value));
  }
}

void copyFile(char * f1, char * target)
{
  fstream input;
  input.open(f1, ios::in | ios::binary);

  fstream output;
  output.open(target, ios::out | ios::binary);
  int i = 0;
  while (!input.eof()) // Continue if not end of file
  {
    int value;
    input.read(reinterpret_cast<char *> (& value), sizeof(value));
    if (input.eof()) break;
    output.write(reinterpret_cast<char *> (& value), sizeof(value));
  }

  input.close();
  output.close();
}
第一階段代碼(其中mergeOneStep函數也屬於第一階段):
int initializeSegments(int segmentSize, char* originalFile, char* f1)
{
  int *list = new int[segmentSize];

  fstream input;
  input.open(originalFile, ios::in | ios::binary);
  fstream output;
  output.open(f1, ios::out | ios::binary);

  int numberOfSegments = 0;
  while (!input.eof()) {
    int i = 0;
    for ( ; !input.eof() && i < segmentSize; i++) {
      input.read(reinterpret_cast<char *> (& list[i]), sizeof(list[i]));
    }

    if (input.eof()) i--;
    if (i <= 0)
      break;
    else
      numberOfSegments++;

    // Sort an array list[0..i-1]
    quickSort(list, i);

    // Write the array to f1.dat
    for (int j = 0; j < i; j++) {
      output.write(reinterpret_cast<char *> (& list[j]), sizeof(list[j]));
    }
  }

  input.close();
  output.close();
  delete [] list;
  return numberOfSegments;
}
主函數:
int main()
{
  const int MAX_ARRAY_SIZE = 100;

  // Implement Phase 1: Create initial segments
  int numberOfSegments =
    initializeSegments(MAX_ARRAY_SIZE, "largedata.dat", "f1.dat");

  // Implement Phase 2: Merge segments recursively
  merge(numberOfSegments, MAX_ARRAY_SIZE, "f1.dat", "f2.dat", "f3.dat");

  fstream input;
  int value;
  input.open("sortedlargedata.dat",ios::in|ios::binary);
	for(int i=0;i<100;i++)
	{
		input.read(reinterpret_cast<char *>(&value),sizeof(value));
		cout<<value<<" ";
	}

	input.close();
	return 0;
}
以及快速排序QuickSort.h:

void quickSort(int list[],int arraySize);
void quickSort(int list[],int first,int last);
int partition(int list[],int first,int last);

void quickSort(int list[],int arraySize)
{
	quickSort(list,0,arraySize-1);
}

void quickSort(int list[],int first,int last)
{
	if(last>first)
	{
		int pivotIndex=partition(list,first,last);
		quickSort(list,first,pivotIndex-1);
		quickSort(list,pivotIndex+1,last);
	}
}

int partition(int list[],int first,int last)
{
	int pivot=list[first];
	int low=first+1;
	int high=last;

	while(high>low)
	{
		while(low<=high&&list[low]<=pivot)
			low++;

		while(low<=high&&list[high]>pivot)
			high--;

		if(high>low)
		{
			int temp=list[high];
			list[high]=list[low];
			list[low]=temp;
		}
	}

	while(high>first&&list[high]>=pivot)
		high--;

	if(pivot>list[high])
	{
		list[first]=list[high];
		list[high]=pivot;
		return high;
	}
	else
	{
		return first;
	}
}

可以在http://wenku.baidu.com/view/a6bfc3859ec3d5bbfd0a74f8下載完整代碼

發佈了47 篇原創文章 · 獲贊 95 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章