对于归并排序,我们收到的任务很明确:
就是对一组无序(当然也有可能有序啦)的不明是否有序的数据进行排序,使这组序列实现从小到大的排列顺序,这里给出一组样例输入:
8 5 9 2 6 3 7 1 10 4
我们不妨将序列先放入数组中
#define MAX 500
int n;
int S[MAX];
cin>>n;//先输入预处理序列的大小
for(int i=0;i<n;i++)//初始化序列(数组)
{
cin>>S[i];
}
好,预处理完毕!
回到归并排序上,归并的思想就是分而治之,用递归的方法将对整个数组排序转化为对数组的每个部分进行排序,然后再将他们组合起来
首先,我们将数组先一分为二,凭借小学出色的奇偶数知识,我们不难这么挑选奇偶数:偶数就总长度/2,奇数就(长度+1)/2,
if (n%2==0)
{
mid = n/2;
}
else
{
mid = (n+1)/2;
}
那么我们只需要在奇数情况下丢掉中间数即可,这样每次划分(一分为二)之后,得到的都是长度相等的子问题
但是这样做,在合并的时候就会很困难了emmm
所以我们必须转换一下思路
通过牺牲等长的代价,通过(left+right)/2的方法选分界点,
分为[left,mid)和[mid,right)两部分,这样不管怎么样,每次都能缩小问题的规模,知道最后mergesort只用处理一个数(PS:left=right),停止分割,
void mergesort(int A[], int left, int right)
{
if (left +1< right)//规模至少是3,这样才能进行下一次分割
{
int mid = (left + right) / 2;//分界点
mergesort(A, left, mid);
mergesort(A, mid, right);
merge(A, left, mid, right);
}
}
下面就来考虑合并的问题
void merge(int A[], int left, int mid, int right)
{
int size_L = mid - left;
int size_R = right - mid;
int L[MAX];
int R[MAX];
for (int i = 0; i < size_L; i++)//分别将分好的两块放到两个新的数组中去
{
L[i] = A[left + i];
}
//此处先跳过不看
R[size_L] = 100;
//分界线
for (int j = 0; j < size_R; j++)
{
R[j] = A[mid + j];
}
L[size_R] = 100;
int i = 0;
int j = 0;
for (int cnt = left; cnt < right; cnt++)
//对照着拿出来的两块分组
//左右比较,一个个放入
{
if (L[i] < R[j])
{
A[cnt] = L[i];
i++;
}
else
{
A[cnt] = R[j];
j++;
//这时候我们发现会出现两边拿出的数组越界的情况/
//为了避免这种事情
//我们在原来数组的末尾加上一个极大的数100
//(这时候回到上面的"跳过的部分"))
}
}
}
ok
这样我就可以交差了!
那么,我们为什么要用归并排序呢?
那是因为,这样每次一分为二,规模为n的排序问题
只需要经过log(以2为底)n次就能分割完。
由于每一次的合并(merge)总共需要比较n次
所以总的时间复杂度(所有情况下)均为O(nlgn)
优于快速排序的最坏情况O(n^2)