归并排序(算法与实现)

归并排序作为一个能够在O(nlogn)的复杂度下完成排序的算法之一,其对于比较大的数据量有着不错的排序效果。下面解析一下归并排序的算法与实现。

归并排序基于分治的思想,也就是将一个大的区间分为两个小的区间,然后只要左边实现了有序,右边实现了有序,接下去也就是并,将两个小区间合并为大区间,实现整体有序。

下面是简单的演示:

初始也就是序列4 1 5 3 2,我们要实现升序序列。

我们以l为左边界,r为有边界,m=(l+r)/2为中点,将区间划分为[l,m],(m,r]两个小区间,然后接下去对让小区间进行归并,实现小区间有序。(这实际上是递归的过程,可以在程序中感受一下,这里就不演示了,就假设经过排序之后实现了有序,先略过过程,因为后面还有比较重要的合并操作)。

递归对小区间排序之后,可以实现对小区间内的有序。

然后要进行和合并操作,这里需要一个额外的数组来暂时存储排序后的数组,然后重新赋值给原数组。

那么如何实现合并?因为左边右边实际上都是升序的,所以可以设置两个指针分别指向l和m+1也就是左边区间和右边区间的第一个,记为p和q。

 

然后比较,将两个数中的小的先放进暂存数组,比如这里1比较小,那么把1放进数组,p后移。

接下来2小,将2放进暂存数组,q后移。

 重复这样的过程...然后就可以得到这样的一个数组。

 可以看到这个区间实现了有序,然后再把暂存数组中的数字重新赋值给a的[l,r]区间就好了。需要注意的就是需要注意区间的边界问题,左边区间的所有数取完了,那么只要把右边剩下的数字放进去就好了,就不要继续将左边的指针移到右边区间去了。

参考代码

代码里似乎是把中点划分到了右区间,道理是一样的

#include<stdio.h>

int a[] = { 0,1,5,7,4,2,3,6,6,7,5,3,2,6,0,1,1,1,9,2,1,1,8,9,3,2,1 };
int n = sizeof(a) / sizeof(a[0]) - 1;

void print(){
	for (int i= 1; i<=n; i++) 
		printf("%d ", a[i]);
	printf("\n");
}

void swap(int i, int j) {
	int x = a[i];
	a[i] = a[j];
	a[j] = x;
}

//guibing_sort()
int b[30000];//额外存储空间---暂存数组
void merge(int l,int m, int r) {//合并区间
	int p = l, q = m;
	for (int i = 1; i <= r - l + 1;i++) {
		if (p<m&&(a[p]<a[q] || q>r))
			b[i] = a[p++];
		else b[i] = a[q++];
	}
	for (int i = 1; i <= r - l + 1; i++)//重新赋值
		a[l + i - 1] = b[i];
}

void guibing_sort(int l,int r) {
	if (l == r)return;//天然有序
	if (r - l == 1) {
		if (a[l] > a[r])swap(l,r);
		return;
	}
	int m = (l + r) / 2;
	guibing_sort(l, m - 1);//左边排序
	guibing_sort(m, r);//右边排序
	merge(l,m, r);//合并区间
}

int main()
{
	printf("排序前:");
	print();
	guibing_sort(1,n);
	printf("排序后:");
	print();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值