归并排序
2015-11-04 11:02
447 查看
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
再来看归并排序
描述:
基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?
可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。
图示:
算法:
上面的代码都配有详细的注释,不懂的可以评论给我。
下面的代码,我打印出了整个分解和合并的过程,如下图
上面的数字,是计算机执行归并过程的调用执行顺序,对于理解递归很有帮助
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要lgN(计算机里面表示以2为底N的对数)步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)
为什么是lgN
??
设需要X步,最后才能分解成一个元素,那么(N/2的x次方) = 1, 解得X = lgN;
首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
再来看归并排序
描述:
基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?
可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。
图示:
算法:
package com.zq.algorithm.sort; /** * Created by zhengshouzi on 2015/11/2. */ public class MergeSort { public static void main(String[] args) { int[] a = {10, 4, 6, 3, 8, 2, 5, 7}; mergeSort(a, 0, a.length - 1); //输出最后结果 for (int i = 0; i < a.length; i++) { System.out.print(" " + a[i]); } } /** * 递归方法,用来分解问题, * @param a 源数组 * @param left 每次分解的序列的左边界 * @param right 每次分解的序列的的右边界 */ public static void mergeSort(int[] a, int left, int right) { //定义中间值 int middle; //定义递归结束条件,,当左边界< 右边界的时候才递归,左边界等于右边界 时候表示这一次只归并一个元素,一个元素当然是有序的,不用做处理直接返回上一层 if (left < right) { //求出中间值 middle = (left + right) / 2; //递归调用此方法,使原来的大问题,分解成最小的一个元素有序的小问题, mergeSort(a, left, middle); mergeSort(a, middle + 1, right); //当左边和右边都有序了,那么就合并左右两边的序列,使序列整体有序 mergeArray(a, left, middle, right); } } /** * 归并两个连续的有序的序列 * @param a 源数组 * @param first 序列的开始下标 * @param middle 中间下标 * @param last 结束下标 */ public static void mergeArray(int[] a, int first, int middle, int last) { //必须要一个temp数组,长度为每次需要归并的序列的长度,也就是动态变化的,最大为整个a 数组的长度(最后一个合并) int[] temp = new int[last - first + 1]; //定义下标变量 int index1 = first; int index2 = middle + 1; int k = 0; //将两个有序的序列,合并成一个有序的序列,放入temp数组中,循环退出条件是:有一个序列比较完了,那么这时候将另外一个有序序列中所有元素依次放入temp中 while (index1 <= middle && index2 <= last) { //依次比较两个序列中元素谁小,谁就放入temp中, if (a[index1] <= a[index2]) { temp[k++] = a[index1++]; } else { temp[k++] = a[index2++]; } } //将上面没有比较晚的,有序序列中所有元素依次放入temp中 while (index1 <= middle) temp[k++] = a[index1++]; while (index2 <= last) temp[k++] = a[index2++]; //temp必是有序的,将temp中的元素,写到原来的a数组中的对应位置(从first 开始的temp.length 个长度) for (int i = 0; i < temp.length; i++) { a[first + i] = temp[i]; //System.out.print(" " + temp[i]); } //System.out.println(); } }
上面的代码都配有详细的注释,不懂的可以评论给我。
下面的代码,我打印出了整个分解和合并的过程,如下图
package com.zq.algorithm.sort; /** * Created by zhengshouzi on 2015/11/2. */ public class MergeSort { public static void main(String[] args) { int[] a = {10, 4, 6, 3, 8, 2, 5, 7}; mergeSort1(a, 0, a.length - 1); //输出最后结果 for (int i = 0; i < a.length; i++) { System.out.print(" " + a[i]); } } public static void mergeSort1(int[] a, int left, int right) { //定义中间值 int middle; System.out.print("分解:"); for (int i = left; i <= right; i++) { System.out.print( " "+ a[i]); } System.out.println(); //定义递归结束条件,,当左边界< 右边界的时候才递归,左边界等于右边界 时候表示这一次只归并一个元素,一个元素当然是有序的,不用做处理直接返回上一层 if (left < right) { //求出中间值 middle = (left + right) / 2; //递归调用此方法,使原来的大问题,分解成最小的一个元素有序的小问题, mergeSort1(a, left, middle); mergeSort1(a, middle + 1, right); //当左边和右边都有序了,那么就合并左右两边的序列,使序列整体有序 mergeArray1(a, left, middle, right); }else { //System.out.println("叶子节点:" +a[left]); } } public static void mergeArray1(int[] a, int first, int middle, int last) { //必须要一个temp数组,长度为每次需要归并的序列的长度,也就是动态变化的,最大为整个a 数组的长度(最后一个合并) int[] temp = new int[last - first + 1]; //定义下标变量 int index1 = first; int index2 = middle + 1; int k = 0; //将两个有序的序列,合并成一个有序的序列,放入temp数组中,循环退出条件是:有一个序列比较完了,那么这时候将另外一个有序序列中所有元素依次放入temp中 while (index1 <= middle && index2 <= last) { //依次比较两个序列中元素谁小,谁就放入temp中, if (a[index1] <= a[index2]) { temp[k++] = a[index1++]; } else { temp[k++] = a[index2++]; } } //将上面没有比较晚的,有序序列中所有元素依次放入temp中 while (index1 <= middle) temp[k++] = a[index1++]; while (index2 <= last) temp[k++] = a[index2++]; //temp必是有序的,将temp中的元素,写到原来的a数组中的对应位置(从first 开始的temp.length 个长度) System.out.print("归并:"); for (int i = 0; i < temp.length; i++) { a[first + i] = temp[i]; System.out.print(" " + temp[i]); } System.out.println(); System.out.println("--------------------------------------------"); } }
上面的数字,是计算机执行归并过程的调用执行顺序,对于理解递归很有帮助
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要lgN(计算机里面表示以2为底N的对数)步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)
为什么是lgN
??
设需要X步,最后才能分解成一个元素,那么(N/2的x次方) = 1, 解得X = lgN;
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统