归并排序
- 整体是递归,左边排好序+右边排好序+merge让整体有序
- 让其整体有序的过程里用了排外序方法
- 利用master公式来求解时间复杂度
- 当然可以用非递归实现
递归算法每次让左右两边有序,之后进行merge,两边进行比较,左边的第一位和右边的第一位进行比较,谁小将谁拷贝进新的数组中,如果相同优先拷贝左边的,如果某一边offset越界,直接将另一边拷贝进新的数组,最后将新的数组刷回原数组
非递归算法图解
/**
* 递归算法实现
* @param arr 进行排序的数组
*/
public static void mergeSort1(int[] arr){
if (arr == null || arr.length < 2){
return;
}
}
/**
* 使数组在L-R上有序
* @param arr 进行排序的数组
* @param l 开始下标
* @param r 结束下标
*/
public static void process(int[] arr, int l, int r){
if (l == r){
return;
}
int mid = l + ((r - l) >> 1);
process(arr,l,mid);
process(arr,mid + 1,r);
merge(arr,l,mid,r);
}
/**
* 将两边有序的数组进行合并
* @param arr 要合并的数组
* @param l 开始的下标
* @param m 中间的小标
* @param r 结束的下标
*/
public static void merge(int[] arr,int l,int m,int r){
int[] help = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = m + 1;
while (p1 <= m && p2 <= r){
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m){
help[i++] = arr[p1++];
}
while (p2 <= r){
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
}
非递归算法图解
/**
* 非递归算法实现
* @param arr 要排序的数组
*/
public static void mergeSort2(int[] arr){
if (arr == null || arr.length < 2){
return;
}
int n = arr.length;
int mergeSize = 1;
while (mergeSize < n){
int l = 0;
while (l < n){
int m = l + mergeSize - 1;
if (m >= n){
break;
}
int r = Math.min(m + mergeSize,n - 1);
merge(arr,l,m,r);
l = r + 1;
}
if (mergeSize > n / 2){
break;
}
mergeSize <<= 1;
}
}
归并排序的时间复杂度:使用master公式进行计算得出:T(N) = 2T(N/2) + O(N^1) 为O(NlogN) merge的过程需要申请辅助数组,所以空间复杂度为O(N) 归并排序的实质是把比较行为变成了有序信息并传递,比O(N^2)的排序快 O(N^2)的排序算法浪费了过多的比较行为
常见的面试题
在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和。求数组小和。 例子: [1,3,4,2,5] 1左边比1小的数:没有 3左边比3小的数:1 4左边比4小的数:1、3 2左边比2小的数:1 5左边比5小的数:1、3、4、 2 所以数组的小和为1+1+3+1+1+3+4+2=16
思路:一个数左边比他小的数的总和等价于一个数右边有几个数比他大,小和就是几个本身,如果使用暴力方法,时间复杂度为O(N^2),使用归并排序,时间复杂度为O(NlogN) *
/**
* 计算小和
* @param arr 需要计算的数组
* @return 小和
*/
public static int smallSum(int[] arr){
if (arr == null || arr.length < 2){
return 0;
}
return process(arr,0,arr.length - 1);
}
/**
* 归并排序
* @param arr 数组
* @param l 开始下标
* @param r 结束下标
* @return 小和
*/
public static int process(int[] arr,int l,int r){
if (l == r){
return 0;
}
int m = l + ((r - l) >> 1);
return process(arr,l,m) + process(arr,m + 1,r) + merge(arr,l,m,r);
}
/**
* 合并排序
* @param arr 数组
* @param l 开始下标
* @param m 中间下标
* @param r 结束下标
* @return 小和
*/
public static int merge(int[] arr,int l,int m,int r){
int[] help = new int[r - l +1];
int i = 0;
int p1 = l;
int p2 = m + 1;
int res = 0;
while (p1 <= m && p2 <= r){
res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m){
help[i++] = arr[p1++];
}
while (p2 <= r){
help[i++] = arr[p2++];
}
return res;
}
图解