java版基础排序归并排

归并排序详解
本文详细介绍了归并排序的原理及实现过程,通过实例演示了如何递归地将数组拆分和合并,最终达到排序的目的。文章还提供了完整的Java代码示例。

归并排和快排的时间复杂度都是o(nlogn).属于比较高效的一种排序算法。归并排序使用的分治思想。所谓的分治:分---就是将原数组划分派别,直到划到不可分为止;

治:将各个派别两两’治理‘,合成一个有序派别。需要强调的是这里的‘治’是指原先两个本就有序的派别重新比较治理。这里涉及的两个动作第一就是递归地划分,比较容易实现;另一个就是对两个有序的子数组进行比较合并,比较时我们可能需要一个临时存储区---一个先进先出的队列。当然你也可以申请一个与原始数组等长的临时数组。比较过程如下:从两个子数组开始处比较,谁小就把谁放入临时存储区,并取下一个值继续比较。如果一方比较完毕,则把另一方全部放入临时存储区中。最后再把临时存储区里的数组(已经全部有序)赋值回原始数组。临时比较图示说明如下:原始数组

012345678910
246814027410

注:第一行为下标,第二行为元素。目标从小到大排序。这里为了说明比较过程,我选取一个中间比较过程比如当我们把这些元素递归成单一派别,再合并时可能会出现下标为0-2的元素有序,下标为3-5的元素有序即是下面这种情况。

012345678910
246148027410
下面的合并就是将二者合并成整体有序。先将元素2和元素1比较,将较小元素1优先存入临时队列,同时将元素1的下标后移,开始下一次比较元素2和4比较;依次类推。如果哪一个元素移动到头。就把另一子数组中全部元素移入临时队列。合并后的临时队列为。注意:我这里强调凡是排好序的都是放在了临时队列中,原数组没做变动。这也是为什么最后我们要把临时数组再按序放回原数组中的原因。

012345
124468

放回原始数组中,也就是一次合并后原始数组可能出现的情况:

012345678910
124468027410

好了,基本逻辑就是这些.下面看一下代码:

 public static void main(String[] args){
    	   int a[]={2,4,6,8,1,4,0,2,7,4,10};
    	   merge_sort(a,0,a.length-1);
    	   ArrayUtils.printArray(a);//这里将排好后的结果输出,这个函数是写在另一个类中的静态输出函数。读者若嫌麻烦,不妨弄个for循环输出。
    	   }
       public static void merge_sort(int a[],int first,int end){
    	   if(first<end){    //这里划分一定要分成单个元素成派。理由很简单因为合并的基础是有序子数组。只有单个元素可以认为有序,多个则不然。
    		   int mid=(first+end)/2;  //采用中间划分
    		   //System.out.println(mid);//这里是我调试所用,不需要看
    		   merge_sort(a,first,mid);//这里是两条递归语句,就是划分派别。注意这里划分其实是划分的下标
    		   merge_sort(a,mid+1,end);
    		   merge(a,first,mid,end);//合并
    		 //  ArrayUtils.printArray(a);//调试所用,不用看
    		   
    	   }
       }
       public static void merge(int a[],int first,int mid,int end){
    	  Queue<Integer> q=new LinkedList<Integer>();//声明一个临时队列先进先出
    	  int indexA=first;//考虑一下为什么不直接使用first还需要再申请一个新的临时变量?答案在最后一句我们其实是在每合并一次就赋值回原数组一次。
    	  int indexB=mid+1;
    	  while(indexA<=mid&&indexB<=end){
    		  if(a[indexA]<=a[indexB]){  //谁小把谁优先加入临时队列
    			  q.offer(a[indexA]);
    			  indexA++;
    		  }
    		  else{
    			  q.offer(a[indexB]);
    			  indexB++;
    		  }	  
    	  }
    	  while(indexA<=mid){          //这个队列还没比较完,依次加入队列
    		  q.offer(a[indexA]);
    		  indexA++;
    	  }
    	  while(indexB<=end){
    		  q.offer(a[indexB]);
    		  indexB++;
    	  }
    	  int index=0;
    	  while(q.size()>0){     //赋值回原始数组
    		  a[first+index]=q.poll();
    		  index++;
    	  }
       }
看到这里大家应该已经会了基数排序,但是我其实一直都对那个递归划分有些不是很清楚。所以为了弄清楚究竟如何递归的,我加了上面的两个输出语句。最后得出如下结论:


上面二叉树中的结点数字代表下标范围,分叉中间的数字就是mid值。这个二叉树有点丑,如果大家有这方面的画图工具不妨推荐一下。如果取消我的那两条调试语句注释,并对main函数中的排序后输出注释掉。可以看到输出结果如下:

5
2
1
0
{2, 4, 6, 8, 1, 4, 0, 2, 7, 4, 10}
{2, 4, 6, 8, 1, 4, 0, 2, 7, 4, 10}
4
3
{2, 4, 6, 1, 8, 4, 0, 2, 7, 4, 10}
{2, 4, 6, 1, 4, 8, 0, 2, 7, 4, 10}
{1, 2, 4, 4, 6, 8, 0, 2, 7, 4, 10}
8
7
6
{1, 2, 4, 4, 6, 8, 0, 2, 7, 4, 10}
{1, 2, 4, 4, 6, 8, 0, 2, 7, 4, 10}
9
{1, 2, 4, 4, 6, 8, 0, 2, 7, 4, 10}
{1, 2, 4, 4, 6, 8, 0, 2, 4, 7, 10}
{0, 1, 2, 2, 4, 4, 4, 6, 7, 8, 10}
从上面可以看出Java顺序执行,1.先执行二叉树的左分支一直到左侧不能划分为止,然后向上比较合并。2.边合并边赋值回原数组。所以结果才会出现乱中有序的输出啊。好了
以上就是我的见解。如有不当之处,敬请留言指点。互相学习,共同提高。最后贴出两篇其他牛人写的,也是我参考过的连接。http://mp.weixin.qq.com/s?src=3&timestamp=1463015893&ver=1&signature=ApxbrqJNHSesYNQ2UF1VYl3WwA0qgIacTPzCkimq1PGIb15WBWOxlrrkuiEiyzohBZcBbG8YWUPCHxPrmWu2M2urOMp-Rvpn70xOs7MSuIx5SXFo*jrFKEjT0jjr0tIxevtlBB8XQi8mqFHCiXUpvsDkEKTGahWOfSK1q5cyu3o=;

还有一篇博客http://www.cnblogs.com/jillzhang/archive/2007/09/16/894936.html。再次声明,文章仅为学习交流之用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值