对于排序大列表数据,一个有效的排序算法是归并排序。类似于快速排序算法,其使用的是分治法来排序。
归并排序的基本思想是:将两个或两个以上的有序子序列”归并”为一个有序序列。
在内部排序中,通常采用的是2-路归并排序。即:将两个位置相邻的有序子序列“归并”为一个有序序列。
一、基本思想
二路归并排序的基本思想是:将有n个记录的原始序列看作n个有序子序列,每个子序的长度为1,然后从第一个子序列开始,把相邻的子序列两两合关,得到n/2个长度为2或1的子序列(当子序列的个数为奇数时,最后一组合并得到的序列长度为1),我们把这一过程称为一次归并排序,对一次归并排序的n/2个子序列采用上述方法继续顺序成对归并,如此重复,当最后得到长度为n的一个子序列时,该子序列便是原始序列归并排序后的有序序列。
第一步,将列表中的11个元素看成11个有序的序列,每个子序列的长度为1,然后两两归并,得到5个长度为2和1个长度为1的有序子序列。
第二步,将6个有序子序列两两归并,得到2个长度为4和1个长度为3的有序子序列。
第三步,将2个长度为4的有序子序列归并,得到第三趟归并结果。
第四步,将长度为8有序子序列和长度为3的有序子序列归并,得到第四趟归并结果,是长度为11的一个有序子序列。
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
首先考虑下如何将将二个有序数列合并。这个非常简单,设定两个指针分别指向A和B的首地址,然后比较这两个指针所指向的数,哪一个数小就将其保存到临时数组T中,然后指针往前移动一个位置。比如指针pa指向数组A的首地址,pb指向数组B的首地址,指针pt指向临时数组的首地址。加入pa指向的数小于pb指向的数,则将pa所指向的数存放在pt所指的位置上,然后pa和pt都往前移动一格。重复上述操作,直到pa和pb有一个指向数组的末尾元素。此时将另外一个指针没有指向末尾元素的数组中的元素全部都保存到临时数组中。这样就完成了两个有序数组的归并操作。
- //将有序数组a[]和b[]合并到c[]中
- void MemeryArray(int a[], int n, int b[], int m, int c[])
- {
- int i, j, k;
- i = j = k = 0;
- while (i < n && j < m)
- {
- if (a[i] < b[j])
- c[k++] = a[i++];
- else
- c[k++] = b[j++];
- }
- while (i < n)
- c[k++] = a[i++];
- while (j < m)
- c[k++] = b[j++];
- }
可以看出合并有序数列的效率是比较高的,可以达到O(n)。
解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?
可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。
二、并归排序的实现
#include<iostream>
using namespace std;
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int k = 0;
while (i <= mid && j <= last)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= mid)
temp[k++] = a[i++];
while (j <= last)
temp[k++] = a[j++];
for (i = 0; i < k; i++)
a[first + i] = temp[i];
}
void mergesort(int Data[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(Data, first, mid, temp); //左边有序
mergesort(Data, mid + 1, last, temp); //右边有序
mergearray(Data, first, mid, last, temp); //再将二个有序数列合并
}
}
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, 0, n - 1, p);
delete[] p;
return true;
}
int main()
{
int Data[10]={54,987,654,45,87,54,41,45,5674,4};
int len=sizeof(Data)/sizeof(int);
MergeSort(Data,len);
for(int i=0;i<10;i++)
cout<<Data[i]<<" ";
return 0;
}
三、时间复杂度分析
二路归并排序的时间复杂度为O(nlog2n)。二路归并排序算法的空间复杂度为O(n)。