归并排序
归并排序(MERGE-SORT) 是利用归并的思想实现的排序方法, 该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解, 而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起, 即分而治之)。
1) 整体就是一个简单递归, 左边排好序、 右边排好序、 合并左边和右边让其整体有序
2) 让其整体有序的过程里用了外排序方法
思路
1.主方法merSort将原来的部分分成左边部分和右边部分,对这两部分进行递归,递归完成后进行merge合并操作
2.merge操作将两个部分的的元素排序好写入辅助数组,完成后再将辅助数组中的值拷贝到原数组的对应位置
代码
public void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) return;
mergeSort(arr, 0, arr.length - 1);
}
public void mergeSort(int[] arr, int L, int R) {
if (L == R) return;
int mid = L + ((R - L) >> 1);
mergeSort(arr, L, mid);
mergeSort(arr, mid + 1, R);
merge(arr, L, R, mid);
}
public void merge(int[] arr, int L, int R, int M) {
int[] temp = new int[R - L + 1]; //辅助数组
int i = 0; //变量i给辅助数组使用
int p1 = L;
int p2 = M + 1;
while (p1 <= M && p2 <= R) {
temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= M) {
temp[i++] = arr[p1++];
}
while (p2 <= R) {
temp[i++] = arr[p2++];
}
//将结果拷贝到原数组中
for (int j = L; j <= R; j++) {
arr[j] = temp[j - L];
}
}
时间复杂度
O (N * logN)
Master公式
对递归的时间复杂度计算涉及到Master公式(Master公式是用来解决递归问题时间复杂度的公式)
记录主方法的表现形式:
T [n] = aT[n/b] +O(N^d)
①当d<logb a时,时间复杂度为O(n^(logb a))
②当d=logb a时,时间复杂度为O((n^d)*logn)
③当d>logb a时,时间复杂度为O(n^d)
其表示的意义是n表示问题的规模,a表示递归的次数也就是生成的子问题数,b表示每次递归是原来的1/b之一个规模,f(n)表示分解和合并所要花费的时间之和。
然后我们来分析一下归并排序的时间复杂度:
a=2 每次生成的子问题数为2
b= 2 每次递归是原来1/2的规模
d=1 递归过程中除了递归部分其他部分使用的时间为O(N)
所以此时d= logb a 时间复杂度为O((n^1) * logn)=O (N * logN)
为什么时间复杂度能够达到O(N *logN)
简单排序为什么是O(N^2),因为浪费了大量的比较行为,遍历1次只搞定了一个数,每一轮的比较都是独立,前面的比较信息被丢弃了但是归并排序没有浪费比较行为,比较行为信息被留下来了变成了整体有序的部分,比较信息实际通过已经排序好的部分向上传递给将要排序的部分。
额外空间复杂度
递归过程中merge方法使用到辅助数组,使用完之后回收,数组的最大长度等于原数组的长度,所以额外空间复杂度为O(N)