有很多算法在结构上是递归的,为了解决一个问题,需要不断的调用自己。这些问题常采用分治的策略,即将每个大问题转化为若干个小问题来解决。
合并排序就是一个需要用分治法来解决问题的例子。
合并排序的关键在于合并排好序的两个子序列,例如,现在假设递归的一个子问题为:有大数组A[n],从n/2处分隔开,前一半为排好序的,一半为排好序的,但是整个数组并没有完全排好。这时候我们只要将已经排好序的两个稍小的数组合并起来,就能使得整个数组排好。
在此引入一个叫做merge的函数,作为合并排序的中间过程。
merge的形式为merge(A,first,mid,end)
其中A为待排序的数组,first,mid,end分别为数组的头、中、尾部。现在first到mid的一段和mid到end的一段都已经排好。
#include <iostream>
using namespace std;
void merge(int *A,int first,int mid,int end)
{
//int L[mid - first + 2];
int *L = new int[mid - first +2];//创建一个数组用来暂存first到mid之间的数
L[mid - first +1] = 255;
//int R[end - mid +1];
int *R = new int[end - mid +1];//暂存mid到end之间的数
R[end - mid] = 255;
for(int i = first,li = 0;i != mid+1; ++i)
{
L[li] = A[i];
++li;
}
for(int i = mid+1,ri = 0;i != end+1;++i)
{
R[ri] = A[i];
++ri;
}
int j = 0,i = 0;
for(int k = first;k != end +1 ;++k)//比较L、R中的数字大小,将小的重新复制给A数组
{
if(L[i] < R[j])
{
A[k] = L[i];
++i;
}
else
{
A[k] = R[j];
++j;
}
}
}
void combineSort(int *A,int first,int end)//递归过程,先两次调用combinesort函数,当first到mid以及mid到end都排好序时,调用merge函数
{
if(first < end)
{
combineSort(A,first,(first + end)/2);
combineSort(A,(first + end)/2 + 1,end);
merge(A,first,(first + end)/2,end);
}
else
return;
}
int main()
{
int A[10] = {101,54,6,7,200,9,120,5,278,11};
combineSort(A,0,9);
for(int i = 0;i != 10;++i)
{
cout<<A[i]<<" ";
}
return 0;
}
现在来说说为什么我们建立的L、R数组均比需要的数组长度大1。这是因为,我们在从L、R中取数字复制到A的时候,总有一个数组会先被清空。为了防止出错,我们就要不断检查数组是否为空,这是十分麻烦的,因此我们在这里防止一个“哨兵”,即L跟R数组的最后一个值,并将他们设置为正无穷(此处设为255),这样就不用再检查数组是否为空,因为当到了无穷大时,不管怎么比较,无穷大都是不会被复制到数组A中去的。