Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >跨站数据测试

归并排序java实现

归并排序

 编辑

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路 归并

归并过程为:比较a[i]和b[j]的大小,若a[i]≤b[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素b[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。来源于【百度百科】。

解读:归并排序可以简单理解为两个步骤

1、递归二分待排序序列,最终映射成一颗二叉树,二叉树的叶子节点为单个待排序元素。

2、合并有序序列,每一次合并,将产生一个有序序列。合并的原则是,从树的底层,兄弟节点进行合并。重点理解【归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。来源于百度百科。

举例说明:

        待排序序列  1,5,6,2,3,7,4
        递归排序的拆分与合并实例图
                         (1,5,6,2,3,7,4) 根节点                                                  第0层
                            /          \
                           /            \
                          /              \
                     (1,5,6,2)         (3,7,4)                                                       第1层
                      /    \            / \    
                      /      \          /   \
                    (1,5)   (6,2)      3    (7, 4)                                                  第2层
                      /\       /\             / \
                     /  \     /  \           /   \                
                     1    5   6    2          7    4                                                   第3层  
           那是如何进行合并的呢?从叶子节点所在层开始进行合并(兄弟节点之间的合并)
             第3层      (1,5)     (2,6)            (4,7)
             第2层   (1,2,5,6)               (3,4,7)
             第1层   (1,2,3,4,5,6,7)       
上述就是归并排序的核心所在,通常我们使用递归来实现,运用递归的核心:1、重复工作;2:递归结束条件(很重要),比如归并排序的递归结束(退出)条件为待分元素只包含一个元素(本例中的startInx == endInx),源码所在地址:https://git.oschina.net/zhcsoft/StudyDemo.git,所在类路径:persistent.prestige.console.algorithm.MergeSort。

现给出源码:

 

package persistent.prestige.console.algorithm;

/**
 * 归并排序
 * @author dingwei2
 *
 */
public class MergeSort {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		testMergeSort();
		
		
//		int[] a = new int[] {2,5,16,21,1,4,7,10,22 };
//		int[] tmp = new int[a.length];
//		merge(a,0,4,8, tmp);
//		testMerge();

	}
	
	/**
	 * 测试案例
	 */
	public static void testMergeSort() {
		int[] a = new int[] {1,8,7,16,4,9,15,12};
		testMergeSort(a);
		
		
		int[] b = new int[] {1};
		testMergeSort(b);
		
		int[] c = new int[] {1,7,9,5,6,7,9};
		testMergeSort(c);
		
		
	}
	
	private static void testMergeSort(int[] a) {
		System.out.println("待排序序列:");
		for(int i : a) {
			System.out.print(i + " ");
		}
		System.out.println("");
		
		int[] tmp = new int[a.length];
		
		mergeSort(a, 0, a.length -1, tmp);
		
		
		System.out.println("排序后的序列:");
		
		for(int i : a) {
			System.out.print(i + " ");
		}
		System.out.println("");
	}
	
	/**
	 * 归并排序,
	 */
	public static final void mergeSort(int[] source, int startInx, int endInx, int[] tmp) {
		
		if( startInx != endInx ) { // startInx == endInx,说明不可再分
			
			// mid 所在的元素放在下半区  [startInx, mid) [mid,endInx]
			int mid = ((endInx - startInx  ) >> 1) + 1 + startInx; 
			
			mergeSort(source, startInx, mid - 1, tmp); // 将左边部分继续拆分 mid 所在元素放下半区  [startInx, mid)
			mergeSort(source, mid, endInx, tmp);  // 将右边部分继续拆分 mid 所在元素放下半区 [mid,endInx]
			
			merge(source, startInx, mid, endInx, tmp);
			//为什么是放在这里呢?就是归并排序的第一阶段,就是拆分元成一个有序序列(包含单个元素)。分解后的的元素,其实可以看出如下图
			/*
			 *       待排序序列  1,5,6,2,3,7,4
			 *       递归排序的拆分与合并实例图
			 *                        (1,5,6,2,3,7,4) 根节点                                                  第0层
			 *                           /          \
			 *                          /            \
			 *                         /              \
			 *                    (1,5,6,2)         (3,7,4)                   第1层
			 *                      /    \            / \    
			 *                     /      \          /   \
			 *                   (1,5)   (6,2)      3    (7, 4)               第2层
	 		 *                     /\       /\             / \
			 *                    /  \     /  \           /   \                
			 *                   1    5   6    2          7    4                                     第3层  
			 *          那是如何进行合并的呢?从叶子节点所在层开始进行合并(合并孩子)
			 *            第3层      (1,5)     (2,6)            (4,7)
			 *            第2层   (1,2,5,6)               (3,4,7)
			 *            第1层   (1,2,3,4,5,6,7)       
			 *                      
			 *                   
			 *                   
			 * 
			 */
		} // 如果不可分,则跳出递归(递归的出口在这里)(startInx == endInx)
		
	}
	
	/**
	 * 归并排序的合并部分(合并两个有序序列)
	 * @param a
	 * @param stInx        开始合并下标
	 * @param midInx       中间位置
	 * @param endInx       结束下标
	 * @param tmp          临时空间,等于带排序长度,在一次归并排序中,共用一个临时空间。
	 * 
	 */
	public static final void merge(int[] a, int stInx, int midInx, int endInx, int[] tmp) {
		
		int alen = midInx > stInx ? midInx - stInx : 0;
		int blen = endInx - midInx + 1;
		int len = alen + blen;
		
		int aIndex = stInx;
		int bIndex = midInx;
		
		alen += stInx;  //
		blen += midInx; // 这里主要是为了  aIndex < alen && bIndex < blen 服务的
		
		int al = 0;
		
		while( al < len ) {
			if(aIndex < alen && bIndex < blen) {
				if(a[ aIndex ] >= a[bIndex]) {
					tmp[al] = a[bIndex];
					bIndex ++;
				} else {
					tmp[al] = a[aIndex];
					aIndex ++;
				}
			} else if(aIndex < alen) {
				tmp[al] = a[aIndex];
				aIndex ++;
			} else {
				tmp[al] = a[bIndex];
				bIndex ++;
			}
			
			al++;
		}
		
		System.arraycopy(tmp, 0, a, stInx, len); // 将tmp拷贝到source数组,该部门元素的排序
		
	}
	
	/**
	 * 先实现 合并算法,然后转换为 merge 方法的实现
	 * 
	 * 该方法为草稿类,主要合并两个已排序序列算法,进而演变为 merge(int[] a, int stInx, int midInx, int endInx, int[] tmp)
	 * 
	 */
	public static final void testMerge() {
		int[] a = new int[]{ 1,4,7,10,22};
		int[] b = new int[] {2,5,16,21};
		
		int len = a.length + b.length;
		int[] c = new int[len];
		
		int aIndex = 0;
		int bIndex = 0;
		
		int al = 0;
		
		while( al < len ) {
			if(aIndex < a.length && bIndex < b.length) {
				if(a[ aIndex ] >= b[bIndex]) {
					c[al] = b[bIndex];
					bIndex ++;
				} else {
					c[al] = a[aIndex];
					aIndex ++;
				}
			} else if(aIndex < a.length) {
				c[al] = a[aIndex];
				aIndex ++;
			} else {
				c[al] = b[bIndex];
				bIndex ++;
			}
			
			al++;
		}
		
		for(int i : c ) {
			System.err.print(i + " ");
		}
	}

}

 

 

归并排序算法复杂度分析 n * logn

对于一个包含n个元素的待排序序列,首先需要将排序序列分解成一颗二叉树,运算次数为 logn(2的对数)

然后合并操作,每一层进行归并运算的时候,需要比较元素的次数为n

所以算法复杂度为 n * logn

归并排序需要一个临时空间,所需要的空间为元序列的两倍。

 

欢迎加笔者微信号(dingwpmz),加群探讨,笔者优质专栏目录:

1、

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: Java从.CSV文件中读取数据和写入

下一篇: 快速排序算法java版实现

精华推荐