归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
- 自上而下的递归
- 自下而上的迭代(本文是非递归方式)
1. 算法原理
归并排序是一个分治算法(Divide and Conquer)的一个典型实例,归并排序有多路归并排序、两路归并排序
排序思想:
1. 分解待排序的n个元素为两个子列,各为n/2个元素
2. 若子列没有排好序,重复1步骤,每个子列继续分解为两个子列,直至被分解的子列个数为1
3. 子列元素个数为1,说明这个子列已经排好序,开始逐级合并子序列进行排序
2. 算法动图演示
3. 代码实现
/**
* @ClassName: MergeSort
* @Description:
* @Author: tp.cheng
* @Date: 2020/8/20 15:41
* @Version V1.0
**/
import java.util.Arrays;
/**
* 归并排序是一个分治算法(Divide and Conquer)的一个典型实例,归并排序有多路归并排序、两路归并排序
* 排序思想:
* 1,分解待排序的n个元素为两个子列,各为n/2个元素
* 2,若子列没有排好序,重复1步骤,每个子列继续分解为两个子列,直至被分解的子列个数为1
* 3,子列元素个数为1,说明这个子列已经排好序,开始逐级合并子序列进行排序
*/
public class MergeSort {
public static void main(String[] args) {
int[] input = {4, 7, 5, 1, 6, 3, 2, 8};
sort(input);
System.out.println(Arrays.toString(input));
}
/**
* 归并排序(非递归实现)
* 时间复杂度O(nlogn)
* 空间复杂度O(n)
*/
private static void sort(int[] arr) {
// 非递归实现,拆分的方式可以理解为:完全二叉树的分层遍历
// 从 1 开始分割,与递归不同的是,递归由数组长度一分为二最后到1,
// 而非递归则是从1开始扩大二倍直到数组长度
int start = 1;
// 排序前先创建一个和原始数组等长的临时数组,避免在拆分过程中频繁开辟空间
int[] tempArr = new int[arr.length];
// 步进从2->4->8直到数组长度,
while (start < arr.length) {
for (int i = 0; i + start < arr.length; i += start << 1) {
int left = i;
int mid = i + start - 1;
int right = i + (start << 1) - 1;
// 防止最后超过数组长度
if (right > arr.length - 1) {
right = arr.length - 1;
}
merge(arr, tempArr, left, mid, right);
}
start <<= 1;
}
}
private static void merge(int[] arr, int[] tempArr, int left, int mid, int right) {
int i = left;//左序列指针
int j = mid + 1;//右序列指针
int k = left;//临时数组指针
// 注意: 此处并没有全部放入temp中,当一边达到mid或right时就是退出循环
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
// 降序排序,如果是升序,改为arr[i++]即可
tempArr[k++] = arr[j++];
} else {
tempArr[k++] = arr[i++];
}
}
while (i <= mid) {
tempArr[k++] = arr[i++];
}
while (j <= right) {
tempArr[k++] = arr[j++];
}
while (left <= right) {
arr[left] = tempArr[left++];
}
}
}