【超级通俗易懂的算法实现讲解】(一):归并排序



        对于归并排序,我们收到的任务很明确:
 就是对一组无序(当然也有可能有序啦)的不明是否有序的数据进行排序,使这组序列实现从小到大的排列顺序,这里给出一组样例输入:
            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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值