归并排序复杂度:
T(N) = 2*T(N/2) + O(N^1) 根据master可知时间复杂度为O(N*logN), merge过程需要辅助数组,所以额外空间复杂度为O(N), 归并排序的实质是把比较行为变成了有序信息并传递,比O(N^2)的排序快
// 递归方法实现 public static void mergeSort1(int[] arr) { if (arr == null || arr.length < 2) { return; } process(arr, 0, arr.length - 1); } // 请把arr[L..R]排有序 // l...r N // T(N) = 2 * T(N / 2) + O(N) // 复杂度O(N * logN) // 使L位置到R位置有序 public static void process(int[] arr, int L, int R) { if (L == R) { // base case return; } //中间位置:L+(R-L)/2 int mid = L + ((R - L) >> 1); //使L到mid位置有序 process(arr, L, mid); //使mid+1位置到R位置有序 process(arr, mid + 1, R); //使L位置到R位置有序 merge(arr, L, mid, R); } public static void merge(int[] arr, int L, int M, int R) { //建立一个辅助数组,长度和原数组一样 int[] help = new int[R - L + 1]; int i = 0; //两个指针p1,p2,分别指向左半部分的第一个位置,和右半部分的第一个位置 int p1 = L; int p2 = M + 1; //p1,p2都未出界 while (p1 <= M && p2 <= R) { help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]; } // 要么p1越界了,要么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]; } }
1)整体是递归,左边排好序+右边排好序+merge让整体有序
2)让其整体有序的过程里用了排外序方法
3)利用master公式来求解时间复杂度
4)当然可以用非递归实现
// 非递归方法实现 public static void mergeSort2(int[] arr) { if (arr == null || arr.length < 2) { return; } int N = arr.length; // 步长 int mergeSize = 1; while (mergeSize < N) { // log N // 当前左组的,第一个位置 int L = 0; while (L < N) { if (mergeSize >= N - L) { break; } //中间位置 int M = L + mergeSize - 1; //右半部分可能长度比左半部分要短 int R = M + Math.min(mergeSize, N - M - 1); merge(arr, L, M, R); L = R + 1; } // 防止溢出 if (mergeSize > N / 2) { break; } mergeSize <<= 1; } }
在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和,求数组小和
public static int smallSum(int[] arr) { if (arr == null || arr.length < 2) { return 0; } return process(arr, 0, arr.length - 1); } public static int process(int[] arr, int l, int r) { if (l == r) { return 0; } // l < r int mid = l + ((r - l) >> 1); //左边排序产生的小和总量+右边排序产生的小和总量+加上自己merge过程中产生的小和总量 return process(arr, l, mid) + process(arr, mid + 1, r) + merge(arr, l, mid, r); } 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; //左组和右组相等时,先copy右组 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]; } //去掉三处,就是归并排序 return res; }
在一个数组中, 任何一个前面的数a,和任何一个后面的数b, 如果(a,b)是降序的,就称为逆序对,返回数组中所有的逆序对
public static int reverPairNumber(int[] arr) { if (arr == null || arr.length < 2) { return 0; } return process(arr, 0, arr.length - 1); } public static int process(int[] arr, int l, int r) { if (l == r) { return 0; } // l < r int mid = l + ((r - l) >> 1); return process(arr, l, mid) + process(arr, mid + 1, r) + merge(arr, l, mid, r); } public static int merge(int[] arr, int L, int m, int r) { int[] help = new int[r - L + 1]; int i = help.length - 1; int p1 = m; int p2 = r; int res = 0; while (p1 >= L && p2 > m) { res += arr[p1] > arr[p2] ? (p2 - m) : 0; help[i--] = arr[p1] > arr[p2] ? arr[p1--] : arr[p2--]; } while (p1 >= L) { help[i--] = arr[p1--]; } while (p2 > m) { help[i--] = arr[p2--]; } for (i = 0; i < help.length; i++) { arr[L + i] = help[i]; } return res; }
在一个数组中, 对于每个数num,求有多少个后面的数 * 2 依然<num,求总个数
public static int biggerTwice(int[] arr) { if (arr == null || arr.length < 2) { return 0; } return process(arr, 0, arr.length - 1); } public static int process(int[] arr, int l, int r) { if (l == r) { return 0; } // l < r int mid = l + ((r - l) >> 1); return process(arr, l, mid) + process(arr, mid + 1, r) + merge(arr, l, mid, r); } public static int merge(int[] arr, int L, int m, int r) { //先算有多少个 // [L....M] [M+1....R] int ans = 0; // 目前囊括进来的数,是从[M+1, windowR) int windowR = m + 1; for (int i = L; i <= m; i++) { while (windowR <= r && arr[i] > (arr[windowR] * 2)) { windowR++; } ans += windowR - m - 1; } //再merge 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]; } return ans; }
给定一个数组arr,两个整数lower和upper, 返回arr中有多少个子数组的累加和在[lower,upper]范围上
public static int countRangeSum(int[] nums, int lower, int upper) { if (nums == null || nums.length == 0) { return 0; } long[] sum = new long[nums.length]; sum[0] = nums[0]; for (int i = 1; i < nums.length; i++) { sum[i] = sum[i - 1] + nums[i]; } return process(sum, 0, sum.length - 1, lower, upper); } public static int process(long[] sum, int L, int R, int lower, int upper) { if (L == R) { return sum[L] >= lower && sum[L] <= upper ? 1 : 0; } int M = L + ((R - L) >> 1); return process(sum, L, M, lower, upper) + process(sum, M + 1, R, lower, upper) + merge(sum, L, M, R, lower, upper); } public static int merge(long[] arr, int L, int M, int R, int lower, int upper) { int ans = 0; int windowL = L; int windowR = L; // [windowL, windowR) for (int i = M + 1; i <= R; i++) { long min = arr[i] - upper; long max = arr[i] - lower; while (windowR <= M && arr[windowR] <= max) { windowR++; } while (windowL <= M && arr[windowL] < min) { windowL++; } ans += windowR - windowL; } long[] help = new long[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]; } return ans; }