归并排序

过程

将一个大的无序数组有序,我们可以把大的数组分成两个,然后对这两个数组分别进行排序,之后在把这两个数组合并成一个有序的数组。由于两个小的数组都是有序的,所以在合并的时候是很快的。

通过递归的方式将大的数组一直分割,直到数组的大小为 1,此时只有一个元素,那么该数组就是有序的了,之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 ….. 直到全部小的数组合并起来。

动图展示

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 归并排序
*/

public class MergeSort1 {

public static int[] mergeSort(int[] arr, int left, int right) {
//如果 left == right,表示数组只有一个元素,则不用递归排序
if (left < right) {
//把大的数组分隔成两个数组
int mid = (left + right) / 2;
//对左半部分进行排序
arr = mergeSort(arr, left, mid);
//对右半部分进行排序
arr = mergeSort(arr, mid + 1, right);
//进行合并
merge(arr, left, mid, right);
}
return arr;
}

//合并函数,把两个有序的数组合并起来
// arr[left..mif]表示一个数组,arr[mid+1 .. right]表示一个数组
public static void merge(int[] arr, int left, int mid, int right) {
//先用一个临时数组把他们合并汇总起来
int[] a = new int[right - left + 1];
int i = left;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
a[k++] = arr[i++];
} else {
a[k++] = arr[j++];
}
}
while (i <= mid) {
a[k++] = arr[i++];
}
while (j <= right) {
a[k++] = arr[j++];
}
//把临时数组复制到原数组
for (i = 0; i < k; i++) {
arr[left++] = a[i];
}
}

public static void main(String[] args) {
int[] arr = {2, 5, 3, 1, 4, 6};
mergeSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}

}

性质:

  1. 时间复杂度:O(nlogn)
  2. 空间复杂度:O(n)
  3. 稳定排序
  4. 非原地排序

非递归式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* 归并排序非递归式
*/

public class MergeSort2 {

public static int[] mergeSort(int[] arr) {
int n = arr.length;
//子数组的大小分别为1,2,4,8...
//刚开始合并的数组大小是1,接着是2,接着4....
for (int i = 1; i < n; i += i) {
//进行数组进行划分
int left = 0;
int mid = left + i - 1;
int right = mid + i;
//进行合并,对数组大小为 i 的数组进行两两合并
while (right < n) {
// 合并函数和递归式的合并函数一样
merge(arr, left, mid, right);
left = right + 1;
mid = left + i - 1;
right = mid + i;
}
//还有一些被遗漏的数组没合并,千万别忘了
//因为不可能每个字数组的大小都刚好为 i
if (left < n && mid < n) {
merge(arr, left, mid, n - 1);
}
}
return arr;
}

//合并函数,把两个有序的数组合并起来
// arr[left..mif]表示一个数组,arr[mid+1 .. right]表示一个数组
public static void merge(int[] arr, int left, int mid, int right) {
//先用一个临时数组把他们合并汇总起来
int[] a = new int[right - left + 1];
int i = left;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
a[k++] = arr[i++];
} else {
a[k++] = arr[j++];
}
}
while (i <= mid) {
a[k++] = arr[i++];
}
while (j <= right) {
a[k++] = arr[j++];
}
//把临时数组复制到原数组
for (i = 0; i < k; i++) {
arr[left++] = a[i];
}
}

}

参考