归并排序

狂风中的少年 提交于 2020-01-23 02:59:18

归并排序是一个典型的基于分治的递归算法。它不断地将原数组分成大小相等的两个子数组(可能相差1),最终当划分的子数组大小为1时(即left小于right不成立时) ,将划分的有序子数组合并成一个更大的有序数组。

归并排序思路分析:

归并排序算法有两个基本的操作,一个是分,也就是把原数组划分成两个子数组的过程。另一个是治,它将两个有序数组合并成一个更大的有序数组。

它将数组平均分成两部分: center = (left + right)/2,当数组分得足够小时---数组中只有一个元素时,只有一个元素的数组自然而然地就可以视为是有序的,此时就可以进行合并操作了。因此,上面讲的合并两个有序的子数组,是从 只有一个元素 的两个子数组开始合并的。

合并后的元素个数:从 1-->2-->4-->8......

比如初始数组:[19,15,37,12,25]

0

1

2

3

4

19

15

37

12

25

第一步:分解

首先将数组拆分成两部分,即19 15 37为一组,12 25为一组,这是第一层,如图所示:

19

15

37

 

12

25

第二步:分解

将第一步得到的数组继续分解,19 15为一组,37为一组,12为一组,25为一组,这四组是第二层,如图所示:

19

15

 

37

 

12

 

25

第三步:分解

将第二步分解得到的数组再次分解,此时只剩下19 15这一组可以分解,所以继续分解,19为一组,15为一组,这两组为第三层,如下图:

19

 

15

第四步:归并

由于所有组都已经分解成只有1个元素,开始进行归并,从“高层”开始归并,即先归并“第三层”,比较“第三层”两组元素,19 < 15,因此将15排在19前面,由于已经没有元素,结束此次归并,如下图:

15

 

19

第五步:归并

继续归并,此次归并“第二层”,这一层有4个组,进行两两比较。首先,比较15、19和37:15 < 37,所以15放第一个位置,接着比较19和37,19 < 37,所以19放第二个位置,此时第一组15、19已经没有元素,于是将37填入15和19之后。接着比较:12和25:12 < 25,所以12放第一个位置,由于第一组12已经没有元素,于是将25填入12之后。归并的结果如下:

15

19

37

 

12

25

第六步:归并

继续归并,此次归并“第一层”,这一组有2个组,第一组:15、19、37,第二组:12、25。同样的,取两组的第1个数比较:15 > 12,所以12放第1个位置;接着取第二组的第2个数比较:15 < 25,所以15放第2个位置;接着取第一组的第2个数比较:19 < 25,所以19放第3个位置;接着取第一组的第3个数比较:37 > 25,所以25放第4个位置;由于第二组已经没有元素,所以37自然归入第5个位置。此时,归并结束,最终数组如下:

12

15

19

25

37

总结:归并排序先进行从外到内的分解,然后进行从内到外的合并。分解可以通过递归实现,通过改变数组左右两边的索引,完成分解的过程,当分解到最内层(只剩下一个元素,即i==j时),再调用归并函数。

代码实现如下:

package com.atguigu.sort;

import java.util.Arrays;

public class MergeSort {

	public static void main(String[] args) {
		int[] arr= {8,4,5,7,1,3,6};
		int[] temp=new int[arr.length];
		mergeSort(arr, 0, arr.length-1, temp);
		System.out.println("归并排序后="+Arrays.toString(arr));
	}
	
	//分+合
	public static void mergeSort(int[] arr,int left,int right,int[] temp) {
		//分
		if(left<right) {
			int mid=(left+right)/2;//中间索引
			//先向左递归进行分解
			mergeSort(arr, left, mid, temp);
			//向右递归进行分解
			mergeSort(arr, mid+1, right, temp);
			
			//合并
			merge(arr, left, mid, right, temp);
		}
	}
	
	/**
	 * @param arr 待排序数组
	 * @param left 左边有序序列的初始索引
	 * @param mid  中间索引
	 * @param right 右边索引
	 * @param temp  做中转的数组
	 */
	//合并的方法
	public static void merge(int[] arr,int left,int mid,int right,int[] temp) {
		int i=left;//初始化i,i表示左边有序序列的初始索引
		int j=mid+1;//初始化j,表示右边有序序列的初始索引
		int t=0;//指向temp数组的当前索引
		//1.先把左右两边有序的数据按照规则填充到temp数组
		//直到左右两边的有序序列,有一边处理完毕为止
		while(i<=mid&&j<=right) {
			if(arr[i]<=arr[j]) {//左边有序序列的元素小于右边有序序列的元素
				temp[t++]=arr[i++];
			}
			else {
				temp[t++]=arr[j++];
			}
		}
		//2.把有剩余数据的一方依次全部填充到temp
		while(i<=mid) {//左边有序序列还有数据
			temp[t++]=arr[i++];
		}
		while(j<=right) {//右边有序序列还有数据
			temp[t++]=arr[j++];
		}
		//3.将temp数组的元素拷贝到arr
		for(int k=0;k<t;++k) {//并不是每次都拷贝所有数据,
			//所以要拷贝到的数组arr的起始位置并不一定从0开始
			arr[left+k]=temp[k];
		}
	}

}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!