经典排序算法之——归并排序

1、思想:多次将两个或两个以上的有序表合并成一个新的有序表。
image       2、算法时间复杂度
          最好的情况下
:一趟归并需要n次,总共需要logN次,因此为O(N*logN)
          最坏的情况下,接近于平均情况下,为O(N*logN)
          说明:对长度为n的文件,需进行logN 趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)。

      3、稳定性
         归并排序最大的特色就是它是一种稳定的排序算法。归并过程中是不会改变元素的相对位置的。
      4、缺点是,它需要O(n)的额外空间。但是很适合于多链表排序。


例如:13579 2468这两个序列,各自都是排好序的,然后我们进行合并,成为123456789这样一个

排好序的序列。貌似这个跟排序关系不大,因为排序给的是一个乱的序列,而合并是合并的两个已经排序
好的序列。且慢,我们可以把需要排序的数据分解成N个子序列,当分解的子序列所包含数据个数为1的时
候,那么这个序列不久是有序了吗?然后再合并。这个就是有名的”分治“了。例如321分成3,2,1三个序

列,1这个序列是有序的啦。。同理2,3都是有序的。然后我们逐一的合并他们。3,2合并为23,然后在

23与1合并为123。哈哈,排序成功。合并排序主要思路就是这样了。但是,问题又出来了,怎么合并两

个有序列呢?我相信我应该理解了数组的存储方式,所以直接用数组说事啦。。我们先把下标定位到各有

序子序列的开始,也把合并之后数组的下标定位到最初。那么下标对应的位置就是他们当前的最小值了。

然后拿他们来比较,把更小的那个放到合并之后数组的下标位置。这样,合并后数组的第一个元素就是他

们的最小值了。接着,控制合并后数组的下标后移一个,把比较时小数字所在序列对应的下标后移一个。

这样。下次比较的时候,他得到就是他的第二小,(第一下已经合并了)就是当前最小值了,在于另一

个序列的当前最小值比较,用小的一个放到合并后数组的相应位置。依次类推。接着当数据都合并玩了结

束,合并完成。
 来做例子:
(1回合)    1357 2468 (合并后数据空)
(2)  357 2468 100000(0表示空) 因为1 < 2所以把1放到合并后位置中了(这里1并不是丢掉了,而是下
标变为指向3了,1是没有写而已。呵呵。理解为数组的下标指向了3)
(3) 357 468 120000  因为3 > 2,所以把而放进去
(4) 57 468  123000  同理3 < 4
(5) 57 68   1234000 同理5 > 4
(6) 7 68    1234500 同理5 > 6
(7) 7 8        1234560 同理7 > 6
(8) 0(空了) 8 12345670 同理7 < 8
(9) 0 0 12345678  弄最后一个

当然,这些只是思路。并不是一定一成不变的这样。合并OK,那么我们就可以用合并排序了哦!哈哈。。
不过注意,那个321全部弄成一个单个数字,然后一个一个合并这样来合并似乎不是很好,貌似还有更好
的解决方案。哈哈,对了,就是我先分一半来合并。如果这一半是排好序的,那么合并不久简单了吗?但
是我怎么让一般排好序呢。呵呵简单,我一半在分一半合并排序,在分一半合并排序,直到分到两个都是
1个了,就合并,ok!
例如,81726354:
(1)分成9172 6354
(2)把8172 分成 81 和72 把6354分成63和54
(3)81分成8和1,哦能合并了哦。合并为18, 同理72,63,54,也可以分解成单个合并为27,36,45
(4) 现在变为了 18, 27, 36, 45了,这个时侯,18 和27能合并了,合并为1278 同理36,合并为45 3456
(5) 好了最好吧,1278和3456合并为12345678.ok排序成功。哈哈。
这样把一个问题分解为两个或多个小问题,然后在分解,最后解决小小问题,已达到解决大问题的目的。
思路主要就是这样了哦:
程序实现上也有点技巧。这个就不说了,直接奉上源代码:


#define INFINITE 1000  
//对两个序列进行合并,数组从mid分开  
//对a[start...mid]和a[mid+1...end]进行合并  
void merge(int *a,int start,int mid,int end)  
{  
	int i,j,k;  
	//申请辅助数组  
	int *array1=(int *)malloc(sizeof(int)*(mid-start+2));  
	int *array2=(int *)malloc(sizeof(int)*(end-mid+1));  
	//把a从mid分开分别赋值给数组  
	for(i=0;i<mid-start+1;i++)  
		*(array1+i)=a[start+i];  
	*(array1+i)=INFINITE;//作为哨兵  
	for(i=0;i<end-mid;i++)  
		*(array2+i)=a[mid+1+i];  
	*(array2+i)=INFINITE;  
	//有序的归并到数组a中  
	i=j=0;  
	for(k=start;k<=end;k++)
	{  
		if(*(array1+i) > *(array2+j))   //如果不设置哨兵,那么因为start到end的区间远大于array的大小,所以,当array的数组下表分配完时,会产生数组越界!
		{  
                     
			a[k]=*(array2+j);  
			j++;  
		}  
		else
		{  
			a[k]=*(array1+i);  
			i++;  
		}  
	}  
	free(array1);  
	free(array2);  
}  
//归并排序  
void mergeSort(int *a,int start,int end)  
{  
	int mid=(start+end)/2;  
	if(start<end){  
		//分解  
		mergeSort(a,start,mid);  //1
		mergeSort(a,mid+1,end);  //2
		//合并  
		merge(a,start,mid,end);  //30
	}  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值