归并排序算法思想是分而治之。下面将对分而治之算法排序做一个简单描述。
//归并排序之分而治之
void sort(E, n)
{
//对E中的n个元素排序
if(n >= k)
{
i = n / k;
j = n - I;
令A由E的前i个元素组成;
令B由E的后j个元素组成;
sort(A, i);
sort(B, j);
merge(A, B, E, i, j);//把A和B合并到E
}
else
对E插入排序
}
当K=2的时候,分而治之的排序算法称之为归并排序,严格的来说是二路归并排序,它的空间复杂度是O(n),时间复杂度为O(nlogn)。
归并排序函数用一个数组a来存储元素序列E,并用a饭后排序后的序列。当序列E被划为2个子序列时,不必把他们分别复制到A和B中,只需要简单的记录他们在序列E中的左右边界。然后将拍熏后的子序列归并到一个新的数组b中去,最后再将它们赋值回a中。简明的算法如下:、
void mergeSort(T *a, int left, int right)
{
//对数组a[left,right-1]排序
if (left < right)
{
//至少有两个元素
int middle = (left + right) / 2;
mergeSort(a, left, middle);
mergeSort(middle + 1, right);
merge(a, b, left, middle, right); //从a到b归并
copy(b, a, left, right);//将排序结果复制到a
}
}
仔细观察这个归并程序会发现递归就是不断的将序列进行划分,直到序列的长度变为1,这时在进行归并,长度为1的子序列被归并成长度为2的子序列,长度为2的子序列被归并成长度为4的子序列,如此反复,直到只剩下一个有序序列,例子如下;
初始段 [8][4][5][1][3][6][2][7]
归并到 b [4 8] [1 5] [3 6] [2 7]
复制到 a [4 8] [1 5] [3 6] [2 7]
归并到 b [1 4 5 8] [2 3 6 7]
复制到 b [1 4 5 8] [2 3 6 7]
归并到 b [1 2 3 4 5 6 7 8]
轮流的从a归并到b,从b归并到a,实际上消除了从b复制到a的过程。
用归并排序对数组a[0,n-1]进行排序的最终算法如下:
template<class T>
void mergeSort(T a[], int n)
{
T *b = new T[n];
int segmentSize = 1; //记录子序列的长度
while (segmentSize < n)
{
mergePass(a, b, n, segmentSize); //从a到b的归并
segmentSize += segmentSize;
mergePass(a, b, n, segmentSize); //从b到a的归并
segmentSize += segmentSize;
}
delete[]b;
}
//mergePass函数的作用是确定归并子序列的左右边界,真实的归并是在merge中完成
template <class T>
void mergePass(T x[], T y[], int n, int segmentSize)
{
int i = 0;
while (i <= n - 2 * segmentSize)//i+ 2 * segmentSize<n
{
//从x到y归并相邻的数据段
merge(x, y, i, i + segmentSize - 1, i + 2 * segmentSize - 1);
i += 2 * segmentSize;
}
//少于两个慢数据段时
if (i + segmentSize < n)
//剩余两个数据段
merge(x, y, i, i + segmentSize - 1, n - 1);
else
//剩余一个数据段
for (int j = i; j < n; j++)
{
y[j] = x[i];
}
}
template <class T>
void merge(T c[], T d[], int startOfFirst, int endOfFirst, int endOfSencond)
{
//把相邻的数据段从c归并到d
int first = startOfFirst;//第一个数据段的索引
int sencond = endOfFirst + 1;//第二个数据段的索引
result = startOfFirst;//归并数据段d的索引
while (first <= endOfFirst&&sencond <= endOfSencond)
{
if (c[first] <= c[sencond])
d[result++] = c[first++];
else
d[resulst++] = c[sencond++];
}
//归并剩余元素
if (first>endOfFirst)
for (int q = sencond; q <= endOfSencond; q++)
d[result++] = c[q]; //当归并序列的左边全不大于右边
else
for (int q = first; q < endOfFirst; q++)
{
d[result++] = c[q]; //当归并序列的左边全不小于右边
}
}
//法二
template<class T>
void merge(T data[], T result[],int start, int mid, int end)
{
int i, j, k;
i = start;
j = mid + 1; //避免重复比较data[mid]
k = 0;
while (i <= mid && j <= end) //数组data[start,mid]与数组(mid,end]均没有全部归入数组result中去
{
if (data[i] <= data[j]) //如果data[i]小于等于data[j]
result[k++] = data[i++]; //则将data[i]的值赋给result[k],之后i,k各加一,表示后移一位
else
result[k++] = data[j++]; //否则,将data[j]的值赋给result[k],j,k各加一
}
while (i <= mid) //表示数组data(mid,end]已经全部归入result数组中去了,而数组data[start,mid]还有剩余
result[k++] = data[i++]; //将数组data[start,mid]剩下的值,逐一归入数组result
while (j <= end) //表示数组data[start,mid]已经全部归入到result数组中去了,而数组(mid,high]还有剩余
result[k++] = data[j++]; //将数组a[mid,high]剩下的值,逐一归入数组result
for (i = 0; i < k; i++) //将归并后的数组的值逐一赋给数组data[start,end]
data[start + i] = result[i]; //注意,应从data[start+i]开始赋值
}
template<class T>
void merge_sort(T data[], T result[], int start, int end)
{
if (start < end)
{
int mid = (start + end) / 2;
merge_sort(data, result, start, mid); //对左边进行排序
merge_sort(data, result,mid + 1, end); //对右边进行排序
merge(data, result,start, mid, end); //把排序好的数据合并
}
}