归并排序主要运用了"分治算法",就是将一个大的问题划分为n个规模较小而结构相似的子问题,子问题的解决办法都是相似的,分别解决这些子问题,然后归并子问题的结果,得到大问题的解。
归并排序的主旨就是分解 ,归并:
分解:将数组分为单个元素,默认单个就是排好序的。
归并:从最小的只包含一个元素的数组开始两两合并,合并后的数组也是有序的,最后得到原数组也是有序的。
如图:
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;
算法描述:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾。
比较快排:
归并排序是稳定的排序,即相等的元素的顺序不会改变。如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要。归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。
速度仅次于快速排序,一般用于对总体无序,但各子项相对有序的数组排序。
应用:求逆序对数。在合并的时候,记录逆序对。
复杂度:
- 归并排序比较占用内存,需要额外的空间来存放合并后的数组,是典型的用空间换时间的例子。
- 改进归并排序在归并时先判断前段序列的最大值与后段序列最小值的关系再确定是否进行复制比较。如果前段序列的最大值小于等于后段序列最小值,则说明序列可以直接形成一段有序序列不需要再归并,反之则需要。所以在序列本身有序的情况下时间复杂度可以降至O(n)。
- TimSort可以说是归并排序的终极优化版本,主要思想就是检测序列中的天然有序子段(若检测到严格降序子段则翻转序列为升序子段)。在最好情况下无论升序还是降序都可以使时间复杂度降至为O(n),具有很强的自适应性。
相比快排:最好O(N),最坏O(N*N),空间复杂度O(1)。
package sort;
/*
* 归并排序:利用递归来分解数组,利用分治来合并数组。
* 核心思想:将一个无序的n长数组切成n个有序子序列(只有一个数据默认为有序),
* 然后两两合并,再将合并后的n/2个子序列继续进行两两合并,循环。。
* 因为将两个有序的数组归并到另一个数组中,所以需要开辟额外的空间。workspace
*
* 算法:元素总数为n,需要分log2n层,每一层归并复制后,再与上一层继续归并,复制总数n*log2n
* 所以算法复杂度为O(n*log2n),比较次数远小于复制次数,可忽略。
*/
public class Merge {
public static int[] mergeSort(int[] nums,int l,int h) {
if(l==h) {
return new int[] {nums[l]}; //分解到一个元素默认有序。
}
//先递归分解
int mid = l + (h-l)/2;
int[] leftArr = mergeSort(nums, l, mid); //对左分组排序
int[] rightArr = mergeSort(nums, mid+1, h); //对右分组排序
int[] newNum = new int[leftArr.length + rightArr.length]; //存放归并排好序的数组
//后归并排序
int m=0,i=0,j=0;
while(i<leftArr.length && j<rightArr.length) {
newNum[m++] = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];
}
while(i<leftArr.length) {
newNum[m++] = leftArr[i++];
}
while(j<rightArr.length) {
newNum[m++] = rightArr[j++];
}
return newNum; //返回归并后的数组
}
public static void main(String[] args) {
int[] nums = new int[] {9,3,6,2,0,7,4,1};
int[] newNums = mergeSort(nums, 0, nums.length-1);
for(int x : newNums) {
System.out.print(x+" ");
}
}
}