1.取中间数的几种方法
- mid=(L+R)/2 当数组长度过大左右相加溢出时会出错
- mid=L+(R-L)/2
- mid=L+((R-L)>>1)
2.获取最大值
public class Day03_GetMax {
public static int getMax(int[] arr) {
return process(arr, 0, arr.length - 1);
}
// arr[L...R]范围上求最大值
public static int process(int[] arr, int L, int R) {
if (L == R) { //arr[L...R]范围上只有一个数,直接返回,base,case
return arr[L];
}
int mid = L + ((R - L) >> 1);//中点
int leftMax = process(arr, L, mid);
int rightMax = process(arr, mid + 1, R);
return Math.max(leftMax, rightMax);
}
}
3.master函数
母问题的数据量是N级别的,即母问题有N个数据
子问题的规模都是N/b规模
母问题为N规模,子问题每一次调用第一所N/b规模
子问题被调用多少次
除了子过程,剩下的过程的时间复杂度是多少
4.归并排序
public class Day03_MergeSort {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
private static void process(int[] arr, int L, int R) {
if (L == R) {
return;
}
int mid = L + ((L + R) >> 1);
process(arr, 0, mid);
process(arr, mid + 1, R);
merge(arr, L, mid, R);
}
private 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;
/** L p1 M p2 R
* | 1238 | 2367 |
*/
//如果p1、p2都没越界,谁小谁被加入新的数组,加入之后指针向后移一位
while (p1 <= M && p2 <= R) {
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
/**
* 下面两个while只会有一个执行
*/
//如果最后是p1没越界,p2越界了,则p1剩下的所有数据按顺序依次继续添加到新数组
while (p1 <= M) {
help[i++] = arr[p1++];
}
//如果最后是p2没越界,p1越界了,则p2剩下的所有数据按顺序依次继续添加到新数组
while (p2 <= R) {
arr[L + i] = help[i];
}
//最后数据重新拷贝 排序后就可以直接通过下标计算个数了
for (i = 0; i < help.length; i++) {
arr[L + i] = help[i];
}
}
}
5.小和问题
public class Day04_SmallSum {
public static int smallSun(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
return process(arr, 0, arr.length - 1);
}
// arr[L...R]既要排好序,也要求小和
public static int process(int[] arr, int l, int r) {
if (l == r) {
return 0;
}
int mid = l + ((r - 1) >> 1);
return process(arr, l, mid) //左侧排序小和的数量
+ process(arr, mid + 1, r) //右侧排序小和的数量
+ merge(arr, l, mid, r); //排序后合并小和的数量
}
private static int merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1];
int i = 0; // i是辅助数组的下标
int p1 = l;
int p2 = m + 1;
int res = 0;
while (p1 <= m && p2 <= r) {
// res+= 右边比p2位置的数大的数的数量 * p1位置的数
// 如果左组的数不比右组的数小,小和增加的数量就是0
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++];
}
// 把数组重新赋值为有序数组
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
return res;
}
}
6.荷兰国旗问题
public class Day04_QuickSort {
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
}
// arr[1...r]排好序
public static void quickSort(int[] arr, int L, int R) {
if (L < R) {
//等概率随机选一个位置,把它和最右侧的数据交换
swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
// 在 L ---- R 范围上,以及选出的最后的那个数,做partition
int[] p = partition(arr, L, R);
quickSort(arr, L, p[0] - 1);// <区
quickSort(arr, p[1] + 1, R);// >区
}
}
/**
* 这是一个处理arr[1...r]的函数
* 默认以arr[r]做划分,arr[r] -> p <p ==p >p
* 返回等于区域(左边界,有边界),所以返回一个长度为2的数组res,res[0] res[1]
*/
public static int[] partition(int[] arr, int L, int R) {
int less = L - 1;// <区右边界
int more = R; // >区左边界
while (L < more) { //L 表示当前数的位置 arr[R] -> 划分值
if (arr[L] < arr[R]) {
//当前数 < 划分值
swap(arr, ++less, L++);
} else if (arr[L] > arr[R]) {
// 当前数 > 划分值
swap(arr, --more, L);
} else {
L++;
}
}
swap(arr, more, R);
return new int[]{less + 1, more};
}
public static void swap(int[] arr, int m, int n) {
int temp = arr[n];
arr[n] = arr[m];
arr[m] = temp;
}
public static void main(String[] args) {
int[] arr = new int[]{1, 5, 7, 2, 4};
swap(arr, 2, 3);
Arrays.stream(arr).forEach(System.out::print);
}
}