分治算法 / 归并排序

归并排序

归并排序是采用的分治法的一个非常典型的应用,将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

既然是排序,通俗来讲就是把数字序列变为有序状态,但是传统的排序方式,像冒泡排序啊,插入排序啊什么的,耗时太高,即使是快速排序,最坏的情况时间复杂度也到了O(n^2),而归并排序,则比快排最好的情况O(n*log n)要好。

比如我有两个有序数列,我怎么把他们合并起来并且排序好呢,直接合并再排序太麻烦了,我们可以这样;

例如:
arr11 3 5 7 9
arr22 4 6 8 10
两个有序数列,我们可以新建一个数组(qrr),然后我们设两个指针分别指向两个数组的开头,通过比较大小把小的放到qrr里面,(当然如果想从大到小也行) ,上面两个数组 其中1 < 2 ,把1放到qrr里面之后我们可以让arr1的指针后移,指向3 然后再比较3 和 2 的大小 ,这时2 < 3, arr2的指针后移,不断进行下去,然后你就会发现qrr 1 2 3 4 5 6 7 8 9 arr1 已经走完了 , arr2还有剩余,这时我们只需要把arr2剩余的添加的qrr里面就行了。
这样我们就实现了合并 + 排序, 时间复杂的为O(n)

但是为什么说用到了分治的思想呢,我们知道,分治就是把一件大的事情,不断分解成许多小问题来解决,(其实类似递归对吧) ,如果合并这两个序列是无序的呢? 事情就复杂了。这是分治就排上用场了,算法算法自然可以有很多变化,前面我们说了合并两个有序数列,这是一种应用,从归并排序分离出来的一些而已,如果我们要对一个数列排序的话,运用归并的思想应该怎么做呢?

不妨这样想,我们可以把数列分成两个数列,但又有问题了,分成的两个数列仍然不是有序的怎么办,是不是想到了什么… 递归对吧,虽然分成的两个数列都不是有序的,但我们可以继续分下去,一直分到最后每个区间只剩一个元素,这样我们就可以认为他是有序的,(一个元素肯定是有序的)

例如:

2 1 3 5 0 7 6 9

对应下标

0 1 2 3 4 5 6 7

这样一个无序数列,我们可以把他分成两半,

左边: 2 1 3 5  右边: 0 7 6 9

然后不是有序的没关系 我们递归下去,继续分它,
一直分到每个区间都是一个元素的时候

2	1	3	5	0	7	6	9    

然后我们用刚开始的办法合并,
2 1 合并成一个区间
3 5 合并成一个区间
0 7合并成一个区间
6 9合并成一个区间

然后4个区间合并成两个

两个最后合并成一个

这样我们就完成了排序


int arr[100];
int qrr[100];

void memeryarray(int first, int mid, int end,)//分别对应开始,中间,末尾
{
	int i=first, j=mid+1, k=0; //对应下标
	while(i <= mid && j <= end) //分成的两个区间
	{
		if(arr[i] > arr[j]) //如果左边的区间下标为i的值 > 右边区间下标为j的值
		{
			qrr[k++]=arr[j++]; //放到新的数组里面
		}
		else //如果左边的区间下标为i的值 < 右边区间下标为j的值
			qrr[k++]=arr[i++];
	}
	while(i <= mid) qrr[k++]=arr[i++]; 
	while(j <= end) qrr[k++]=arr[j++]; //这两句分别代表某一个区间的值用完了,剩下的直接排在后面
	for(i = 0; i < k; ++i)
		arr[first+i] = qrr[i]; //最后区间合并完再放回去
}
void mergesort(int first, int end) //数组的开头和最后
{
	if(first < end) //区分到最后区间内只剩一个值的话 就停止递归
	{
		
		int mid=(first + end)/2; //分成两个区间
		mergesort(first,mid); //左边
		mergesort(mid+1,end); //右边
		memeryarray(first,mid,end); //区间合并
	}
}

至此我们的归并排序就完成了。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值