归并排序:将待排序的元素序列分成两个长度相等的子序列,为每一个子序列排序,然后再将他们合并成一个子序列。合并两个子序列的过程也就是两路归并。


  • 第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  • 第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
  • 第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 重复步骤三直到某一指针超出序列尾
  • 第四步:将另一序列剩下的所有元素直接复制到合并序列尾

原理图解:

动态图解:

归并排序详解_排序算法

代码实现:

package blog;

import java.util.Random;

/**
* 归并排序
*/
public class MergeSort {

public static void main(String[] args) {
// int[] num = {12,23,67,13,78,2};
// mergeSort(num,0,num.length-1);
// System.out.println(Arrays.toString(num));

int[] arr = new int[1000000];
Random r = new Random();
// 给数组元素赋值
for (int i = 0; i < arr.length; i++) { // 生成随机数
int num = r.nextInt();
// 如果不传递参数,随机数的范围是int范围。
arr[i] = num;
}

// 排序之前记录时间
long start = System.currentTimeMillis();
// 获取到当前操作系统的时间,以毫秒值表示 //
// 排序
mergeSort(arr, 0, arr.length - 1);
long end = System.currentTimeMillis();
System.out.println(end - start);
}

// 归并排序,递归方法
public static void mergeSort(int[] arr, int left, int right) {
// 写递归一定要先写递归出口
if (left >= right)
return;
// 将要排序的数组分成两个部分,求它们的中点
int mid = (left + right) / 2; // 这里可以写成left + (right-left)/2
// 这样写的好处是避免在排一个很大的数的时候right+left的值会超过int的取值范围,这样也就不会出现问题,小细节
// 排左边
mergeSort(arr, left, mid);
// 排右边
mergeSort(arr, mid + 1, right);
// 归并
merge(arr, left, mid, right);
}

// 为拆分的每一个小数组进行排序
public static void merge(int[] arr, int left, int mid, int right) {
// 从数组的最左边left开始排,一直排到最右边right,所以我们需要right - left + 1个空间
int[] temp = new int[right - left + 1];
// 令i为左边部分的指针
int i = left;
// 令j为右边部分的指针
int j = mid + 1;
// 令k为temp临时数组的下标
int k = 0;
//左右两个数组相比较,谁小就把谁放在新数组里面
while (i <= mid && j <= right)
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
// 如果进行了上面的排序i还是小于等于mid的话说明j已经全部排完了,剩下的i肯定是比j大的,所以直接加在temp后面即可
while (i <= mid)
temp[k++] = arr[i++];
// 与上面同理
while (j <= right)
temp[k++] = arr[j++];
// 将排好序的数组temp的值放传进来的数组arr的原处,即你从来取出来的就从哪放进去
for (int n = 0; n < temp.length; n++) {
arr[left + n] = temp[n];
}
}
}

时间复杂度分析:

归并排序比较占用内存,但却是一种效率高且稳定的算法。

改进归并排序在归并时先判断前段序列的最大值与后段序列最小值的关系再确定是否进行复制比较。如果前段序列的最大值小于等于后段序列最小值,则说明序列可以直接形成一段有序序列不需要再归并,反之则需要。所以在序列本身有序的情况下时间复杂度可以降至O(​n​)。

TimSort可以说是归并排序的终极优化版本,主要思想就是检测序列中的天然有序子段(若检测到严格降序子段则翻转序列为升序子段)。在最好情况下无论升序还是降序都可以使时间复杂度降至O(​n​),具有很强的自适应性。

最好时间复杂度

最坏时间复杂度

平均时间复杂度

空间复杂度

稳定性

传统归并排序

O(​n​log​n​)

O(​n​log​n​)

O(​n​log​n​)

T(n)

稳定

改进归并排序 [1]

O(​n​)

O(​n​log​n​)

O(​n​log​n​)

T(n)

稳定

TimSort

O(​n​)

O(​n​log​n​)

O(​n​log​n​)

T(n)

稳定

经测试,在对十万个随机数进行排序的时候归并排序所花的时间为25毫秒左右,在对一百万个随机数进行排序的时候归并排序所花的时间大约为211毫秒左右,在对一千万个数进行排序的时候归并排序所花的时间大约为2.2秒,归并排序相比快速排序来说,虽然它比较稳定,但是效率是要低一点。