commit 5dce9076cd26555c5f3713a1b5db53e7571dfae6 Author: algorithmzuo Date: Wed Jul 27 12:47:27 2022 +0800 first commit diff --git a/体系学习班/class01/Code01_SelectionSort.java b/体系学习班/class01/Code01_SelectionSort.java new file mode 100644 index 0000000..c9b5738 --- /dev/null +++ b/体系学习班/class01/Code01_SelectionSort.java @@ -0,0 +1,115 @@ +package class01; + +import java.util.Arrays; + +public class Code01_SelectionSort { + + public static void selectionSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + // 0 ~ N-1 找到最小值,在哪,放到0位置上 + // 1 ~ n-1 找到最小值,在哪,放到1 位置上 + // 2 ~ n-1 找到最小值,在哪,放到2 位置上 + for (int i = 0; i < arr.length - 1; i++) { + int minIndex = i; + for (int j = i + 1; j < arr.length; j++) { // i ~ N-1 上找最小值的下标 + minIndex = arr[j] < arr[minIndex] ? j : minIndex; + } + swap(arr, i, minIndex); + } + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + // Math.random() [0,1) + // Math.random() * N [0,N) + // (int)(Math.random() * N) [0, N-1] + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + // [-? , +?] + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + selectionSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + succeed = false; + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + selectionSort(arr); + printArray(arr); + } + +} diff --git a/体系学习班/class01/Code02_BubbleSort.java b/体系学习班/class01/Code02_BubbleSort.java new file mode 100644 index 0000000..14b6e47 --- /dev/null +++ b/体系学习班/class01/Code02_BubbleSort.java @@ -0,0 +1,110 @@ +package class01; + +import java.util.Arrays; + +public class Code02_BubbleSort { + + public static void bubbleSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + // 0 ~ N-1 + // 0 ~ N-2 + // 0 ~ N-3 + for (int e = arr.length - 1; e > 0; e--) { // 0 ~ e + for (int i = 0; i < e; i++) { + if (arr[i] > arr[i + 1]) { + swap(arr, i, i + 1); + } + } + } + } + + // 交换arr的i和j位置上的值 + public static void swap(int[] arr, int i, int j) { + arr[i] = arr[i] ^ arr[j]; + arr[j] = arr[i] ^ arr[j]; + arr[i] = arr[i] ^ arr[j]; + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + bubbleSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + bubbleSort(arr); + printArray(arr); + } + +} diff --git a/体系学习班/class01/Code03_InsertionSort.java b/体系学习班/class01/Code03_InsertionSort.java new file mode 100644 index 0000000..7283369 --- /dev/null +++ b/体系学习班/class01/Code03_InsertionSort.java @@ -0,0 +1,116 @@ +package class01; + +import java.util.Arrays; + +public class Code03_InsertionSort { + + public static void insertionSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + // 不只1个数 + for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序 + for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) { + swap(arr, j, j + 1); + } + } + } + + // i和j是一个位置的话,会出错 + public static void swap(int[] arr, int i, int j) { + arr[i] = arr[i] ^ arr[j]; + arr[j] = arr[i] ^ arr[j]; + arr[i] = arr[i] ^ arr[j]; + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + // Math.random() -> [0,1) 所有的小数,等概率返回一个 + // Math.random() * N -> [0,N) 所有小数,等概率返回一个 + // (int)(Math.random() * N) -> [0,N-1] 所有的整数,等概率返回一个 + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; // 长度随机 + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; // 随机数组的长度0~100 + int maxValue = 100;// 值:-100~100 + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + insertionSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + // 打印arr1 + // 打印arr2 + succeed = false; + for (int j = 0; j < arr.length; j++) { + System.out.print(arr[j] + " "); + } + System.out.println(); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + insertionSort(arr); + printArray(arr); + } + +} diff --git a/体系学习班/class01/Code04_BSExist.java b/体系学习班/class01/Code04_BSExist.java new file mode 100644 index 0000000..d89be00 --- /dev/null +++ b/体系学习班/class01/Code04_BSExist.java @@ -0,0 +1,65 @@ +package class01; + +import java.util.Arrays; + +public class Code04_BSExist { + + public static boolean exist(int[] sortedArr, int num) { + if (sortedArr == null || sortedArr.length == 0) { + return false; + } + int L = 0; + int R = sortedArr.length - 1; + int mid = 0; + // L..R + while (L < R) { // L..R 至少两个数的时候 + mid = L + ((R - L) >> 1); + if (sortedArr[mid] == num) { + return true; + } else if (sortedArr[mid] > num) { + R = mid - 1; + } else { + L = mid + 1; + } + } + return sortedArr[L] == num; + } + + // for test + public static boolean test(int[] sortedArr, int num) { + for(int cur : sortedArr) { + if(cur == num) { + return true; + } + } + return false; + } + + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 10; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + Arrays.sort(arr); + int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + if (test(arr, value) != exist(arr, value)) { + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/体系学习班/class01/Code05_BSNearLeft.java b/体系学习班/class01/Code05_BSNearLeft.java new file mode 100644 index 0000000..eaf5804 --- /dev/null +++ b/体系学习班/class01/Code05_BSNearLeft.java @@ -0,0 +1,75 @@ +package class01; + +import java.util.Arrays; + +public class Code05_BSNearLeft { + + // 在arr上,找满足>=value的最左位置 + public static int nearestIndex(int[] arr, int value) { + int L = 0; + int R = arr.length - 1; + int index = -1; // 记录最左的对号 + while (L <= R) { // 至少一个数的时候 + int mid = L + ((R - L) >> 1); + if (arr[mid] >= value) { + index = mid; + R = mid - 1; + } else { + L = mid + 1; + } + } + return index; + } + + // for test + public static int test(int[] arr, int value) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] >= value) { + return i; + } + } + return -1; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 10; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + Arrays.sort(arr); + int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + if (test(arr, value) != nearestIndex(arr, value)) { + printArray(arr); + System.out.println(value); + System.out.println(test(arr, value)); + System.out.println(nearestIndex(arr, value)); + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/体系学习班/class01/Code05_BSNearRight.java b/体系学习班/class01/Code05_BSNearRight.java new file mode 100644 index 0000000..f3b6f25 --- /dev/null +++ b/体系学习班/class01/Code05_BSNearRight.java @@ -0,0 +1,75 @@ +package class01; + +import java.util.Arrays; + +public class Code05_BSNearRight { + + // 在arr上,找满足<=value的最右位置 + public static int nearestIndex(int[] arr, int value) { + int L = 0; + int R = arr.length - 1; + int index = -1; // 记录最右的对号 + while (L <= R) { + int mid = L + ((R - L) >> 1); + if (arr[mid] <= value) { + index = mid; + L = mid + 1; + } else { + R = mid - 1; + } + } + return index; + } + + // for test + public static int test(int[] arr, int value) { + for (int i = arr.length - 1; i >= 0; i--) { + if (arr[i] <= value) { + return i; + } + } + return -1; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 10; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + Arrays.sort(arr); + int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + if (test(arr, value) != nearestIndex(arr, value)) { + printArray(arr); + System.out.println(value); + System.out.println(test(arr, value)); + System.out.println(nearestIndex(arr, value)); + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/体系学习班/class01/Code06_BSAwesome.java b/体系学习班/class01/Code06_BSAwesome.java new file mode 100644 index 0000000..c167629 --- /dev/null +++ b/体系学习班/class01/Code06_BSAwesome.java @@ -0,0 +1,76 @@ +package class01; + +public class Code06_BSAwesome { + + // 课上的代码 + public static int getLessIndex(int[] arr) { + if (arr == null || arr.length == 0) { + return -1; + } + if (arr.length == 1 || arr[0] < arr[1]) { + return 0; + } + if (arr[arr.length - 1] < arr[arr.length - 2]) { + return arr.length - 1; + } + int left = 1; + int right = arr.length - 2; + int mid = 0; + while (left < right) { + mid = (left + right) / 2; + if (arr[mid] > arr[mid - 1]) { + right = mid - 1; + } else if (arr[mid] > arr[mid + 1]) { + left = mid + 1; + } else { + return mid; + } + } + return left; + } + + // 验证得到的结果,是不是局部最小 + public static boolean isRight(int[] arr, int index) { + if (arr.length <= 1) { + return true; + } + if (index == 0) { + return arr[index] < arr[index + 1]; + } + if (index == arr.length - 1) { + return arr[index] < arr[index - 1]; + } + return arr[index] < arr[index - 1] && arr[index] < arr[index + 1]; + } + + // 为了测试 + // 生成相邻不相等的数组 + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) (Math.random() * maxSize) + 1]; + arr[0] = (int) (Math.random() * maxValue) - (int) (Math.random() * maxValue); + for (int i = 1; i < arr.length; i++) { + do { + arr[i] = (int) (Math.random() * maxValue) - (int) (Math.random() * maxValue); + } while (arr[i] == arr[i - 1]); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 30; + int maxValue = 100; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int ans = getLessIndex(arr); + if (!isRight(arr, ans)) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class02/Code01_Swap.java b/体系学习班/class02/Code01_Swap.java new file mode 100644 index 0000000..c625614 --- /dev/null +++ b/体系学习班/class02/Code01_Swap.java @@ -0,0 +1,71 @@ +package class02; + +public class Code01_Swap { + + public static void main(String[] args) { + + + + + + + int a = 16; + int b = 603; + + System.out.println(a); + System.out.println(b); + + + a = a ^ b; + b = a ^ b; + a = a ^ b; + + + System.out.println(a); + System.out.println(b); + + + + + int[] arr = {3,1,100}; + + int i = 0; + int j = 0; + + arr[i] = arr[i] ^ arr[j]; + arr[j] = arr[i] ^ arr[j]; + arr[i] = arr[i] ^ arr[j]; + + System.out.println(arr[i] + " , " + arr[j]); + + + + + + + + + + System.out.println(arr[0]); + System.out.println(arr[2]); + + swap(arr, 0, 0); + + System.out.println(arr[0]); + System.out.println(arr[2]); + + + + } + + + public static void swap (int[] arr, int i, int j) { + // arr[0] = arr[0] ^ arr[0]; + arr[i] = arr[i] ^ arr[j]; + arr[j] = arr[i] ^ arr[j]; + arr[i] = arr[i] ^ arr[j]; + } + + + +} diff --git a/体系学习班/class02/Code02_EvenTimesOddTimes.java b/体系学习班/class02/Code02_EvenTimesOddTimes.java new file mode 100644 index 0000000..13ac877 --- /dev/null +++ b/体系学习班/class02/Code02_EvenTimesOddTimes.java @@ -0,0 +1,83 @@ +package class02; + +public class Code02_EvenTimesOddTimes { + + // arr中,只有一种数,出现奇数次 + public static void printOddTimesNum1(int[] arr) { + int eor = 0; + for (int i = 0; i < arr.length; i++) { + eor ^= arr[i]; + } + System.out.println(eor); + } + + // arr中,有两种数,出现奇数次 + public static void printOddTimesNum2(int[] arr) { + int eor = 0; + for (int i = 0; i < arr.length; i++) { + eor ^= arr[i]; + } + // a 和 b是两种数 + // eor != 0 + // eor最右侧的1,提取出来 + // eor : 00110010110111000 + // rightOne :00000000000001000 + int rightOne = eor & (-eor); // 提取出最右的1 + + + int onlyOne = 0; // eor' + for (int i = 0 ; i < arr.length;i++) { + // arr[1] = 111100011110000 + // rightOne= 000000000010000 + if ((arr[i] & rightOne) != 0) { + onlyOne ^= arr[i]; + } + } + System.out.println(onlyOne + " " + (eor ^ onlyOne)); + } + + + public static int bit1counts(int N) { + int count = 0; + + // 011011010000 + // 000000010000 1 + + // 011011000000 + // + + + + while(N != 0) { + int rightOne = N & ((~N) + 1); + count++; + N ^= rightOne; + // N -= rightOne + } + + + return count; + + } + + + public static void main(String[] args) { + int a = 5; + int b = 7; + + a = a ^ b; + b = a ^ b; + a = a ^ b; + + System.out.println(a); + System.out.println(b); + + int[] arr1 = { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 }; + printOddTimesNum1(arr1); + + int[] arr2 = { 4, 3, 4, 2, 2, 2, 4, 1, 1, 1, 3, 3, 1, 1, 1, 4, 2, 2 }; + printOddTimesNum2(arr2); + + } + +} diff --git a/体系学习班/class02/Code03_KM.java b/体系学习班/class02/Code03_KM.java new file mode 100644 index 0000000..a530445 --- /dev/null +++ b/体系学习班/class02/Code03_KM.java @@ -0,0 +1,159 @@ +package class02; + +import java.util.HashMap; +import java.util.HashSet; + +// 输入一定能够保证,数组中所有的数都出现了M次,只有一种数出现了K次 +// 1 <= K < M +// 返回这种数 +public class Code03_KM { + + public static int test(int[] arr, int k, int m) { + HashMap map = new HashMap<>(); + for (int num : arr) { + if (map.containsKey(num)) { + map.put(num, map.get(num) + 1); + } else { + map.put(num, 1); + } + } + int ans = 0; + for (int num : map.keySet()) { + if (map.get(num) == k) { + ans = num; + break; + } + } + return ans; + } + + public static HashMap map = new HashMap<>(); + + // 请保证arr中,只有一种数出现了K次,其他数都出现了M次 + public static int onlyKTimes(int[] arr, int k, int m) { + if (map.size() == 0) { + mapCreater(map); + } + int[] t = new int[32]; + // t[0] 0位置的1出现了几个 + // t[i] i位置的1出现了几个 + for (int num : arr) { + while (num != 0) { + int rightOne = num & (-num); + t[map.get(rightOne)]++; + num ^= rightOne; + } + } + int ans = 0; + // 如果这个出现了K次的数,就是0 + // 那么下面代码中的 : ans |= (1 << i); + // 就不会发生 + // 那么ans就会一直维持0,最后返回0,也是对的! + for (int i = 0; i < 32; i++) { + if (t[i] % m != 0) { + ans |= (1 << i); + } + } + return ans; + } + + public static void mapCreater(HashMap map) { + int value = 1; + for (int i = 0; i < 32; i++) { + map.put(value, i); + value <<= 1; + } + } + + // 更简洁的写法 + public static int km(int[] arr, int k, int m) { + int[] help = new int[32]; + for (int num : arr) { + for (int i = 0; i < 32; i++) { + help[i] += (num >> i) & 1; + } + } + int ans = 0; + for (int i = 0; i < 32; i++) { + help[i] %= m; + if (help[i] != 0) { + ans |= 1 << i; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int maxKinds, int range, int k, int m) { + int ktimeNum = randomNumber(range); + // 真命天子出现的次数 + int times = k; + // 2 + int numKinds = (int) (Math.random() * maxKinds) + 2; + // k * 1 + (numKinds - 1) * m + int[] arr = new int[times + (numKinds - 1) * m]; + int index = 0; + for (; index < times; index++) { + arr[index] = ktimeNum; + } + numKinds--; + HashSet set = new HashSet<>(); + set.add(ktimeNum); + while (numKinds != 0) { + int curNum = 0; + do { + curNum = randomNumber(range); + } while (set.contains(curNum)); + set.add(curNum); + numKinds--; + for (int i = 0; i < m; i++) { + arr[index++] = curNum; + } + } + // arr 填好了 + for (int i = 0; i < arr.length; i++) { + // i 位置的数,我想随机和j位置的数做交换 + int j = (int) (Math.random() * arr.length);// 0 ~ N-1 + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + return arr; + } + + // 为了测试 + // [-range, +range] + public static int randomNumber(int range) { + return (int) (Math.random() * (range + 1)) - (int) (Math.random() * (range + 1)); + } + + // 为了测试 + public static void main(String[] args) { + int kinds = 5; + int range = 30; + int testTime = 100000; + int max = 9; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int a = (int) (Math.random() * max) + 1; // a 1 ~ 9 + int b = (int) (Math.random() * max) + 1; // b 1 ~ 9 + int k = Math.min(a, b); + int m = Math.max(a, b); + // k < m + if (k == m) { + m++; + } + int[] arr = randomArray(kinds, range, k, m); + int ans1 = test(arr, k, m); + int ans2 = onlyKTimes(arr, k, m); + int ans3 = km(arr, k, m); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println(ans1); + System.out.println(ans3); + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class03/Code01_ReverseList.java b/体系学习班/class03/Code01_ReverseList.java new file mode 100644 index 0000000..4b0318a --- /dev/null +++ b/体系学习班/class03/Code01_ReverseList.java @@ -0,0 +1,221 @@ +package class03; + +import java.util.ArrayList; +import java.util.List; + +public class Code01_ReverseList { + + public static class Node { + public int value; + public Node next; + + public Node(int data) { + value = data; + } + } + + public static class DoubleNode { + public int value; + public DoubleNode last; + public DoubleNode next; + + public DoubleNode(int data) { + value = data; + } + } + + // head + // a -> b -> c -> null + // c -> b -> a -> null + public static Node reverseLinkedList(Node head) { + Node pre = null; + Node next = null; + while (head != null) { + next = head.next; + head.next = pre; + pre = head; + head = next; + } + return pre; + } + + public static DoubleNode reverseDoubleList(DoubleNode head) { + DoubleNode pre = null; + DoubleNode next = null; + while (head != null) { + next = head.next; + head.next = pre; + head.last = next; + pre = head; + head = next; + } + return pre; + } + + public static Node testReverseLinkedList(Node head) { + if (head == null) { + return null; + } + ArrayList list = new ArrayList<>(); + while (head != null) { + list.add(head); + head = head.next; + } + list.get(0).next = null; + int N = list.size(); + for (int i = 1; i < N; i++) { + list.get(i).next = list.get(i - 1); + } + return list.get(N - 1); + } + + public static DoubleNode testReverseDoubleList(DoubleNode head) { + if (head == null) { + return null; + } + ArrayList list = new ArrayList<>(); + while (head != null) { + list.add(head); + head = head.next; + } + list.get(0).next = null; + DoubleNode pre = list.get(0); + int N = list.size(); + for (int i = 1; i < N; i++) { + DoubleNode cur = list.get(i); + cur.last = null; + cur.next = pre; + pre.last = cur; + pre = cur; + } + return list.get(N - 1); + } + + // for test + public static Node generateRandomLinkedList(int len, int value) { + int size = (int) (Math.random() * (len + 1)); + if (size == 0) { + return null; + } + size--; + Node head = new Node((int) (Math.random() * (value + 1))); + Node pre = head; + while (size != 0) { + Node cur = new Node((int) (Math.random() * (value + 1))); + pre.next = cur; + pre = cur; + size--; + } + return head; + } + + // for test + public static DoubleNode generateRandomDoubleList(int len, int value) { + int size = (int) (Math.random() * (len + 1)); + if (size == 0) { + return null; + } + size--; + DoubleNode head = new DoubleNode((int) (Math.random() * (value + 1))); + DoubleNode pre = head; + while (size != 0) { + DoubleNode cur = new DoubleNode((int) (Math.random() * (value + 1))); + pre.next = cur; + cur.last = pre; + pre = cur; + size--; + } + return head; + } + + // for test + public static List getLinkedListOriginOrder(Node head) { + List ans = new ArrayList<>(); + while (head != null) { + ans.add(head.value); + head = head.next; + } + return ans; + } + + // for test + public static boolean checkLinkedListReverse(List origin, Node head) { + for (int i = origin.size() - 1; i >= 0; i--) { + if (!origin.get(i).equals(head.value)) { + return false; + } + head = head.next; + } + return true; + } + + // for test + public static List getDoubleListOriginOrder(DoubleNode head) { + List ans = new ArrayList<>(); + while (head != null) { + ans.add(head.value); + head = head.next; + } + return ans; + } + + // for test + public static boolean checkDoubleListReverse(List origin, DoubleNode head) { + DoubleNode end = null; + for (int i = origin.size() - 1; i >= 0; i--) { + if (!origin.get(i).equals(head.value)) { + return false; + } + end = head; + head = head.next; + } + for (int i = 0; i < origin.size(); i++) { + if (!origin.get(i).equals(end.value)) { + return false; + } + end = end.last; + } + return true; + } + + // for test + public static void main(String[] args) { + int len = 50; + int value = 100; + int testTime = 100000; + System.out.println("test begin!"); + for (int i = 0; i < testTime; i++) { + Node node1 = generateRandomLinkedList(len, value); + List list1 = getLinkedListOriginOrder(node1); + node1 = reverseLinkedList(node1); + if (!checkLinkedListReverse(list1, node1)) { + System.out.println("Oops1!"); + } + + Node node2 = generateRandomLinkedList(len, value); + List list2 = getLinkedListOriginOrder(node2); + node2 = testReverseLinkedList(node2); + if (!checkLinkedListReverse(list2, node2)) { + System.out.println("Oops2!"); + } + + DoubleNode node3 = generateRandomDoubleList(len, value); + List list3 = getDoubleListOriginOrder(node3); + node3 = reverseDoubleList(node3); + if (!checkDoubleListReverse(list3, node3)) { + System.out.println("Oops3!"); + } + + DoubleNode node4 = generateRandomDoubleList(len, value); + List list4 = getDoubleListOriginOrder(node4); + node4 = reverseDoubleList(node4); + if (!checkDoubleListReverse(list4, node4)) { + System.out.println("Oops4!"); + } + + } + System.out.println("test finish!"); + + } + +} \ No newline at end of file diff --git a/体系学习班/class03/Code02_DeleteGivenValue.java b/体系学习班/class03/Code02_DeleteGivenValue.java new file mode 100644 index 0000000..647047a --- /dev/null +++ b/体系学习班/class03/Code02_DeleteGivenValue.java @@ -0,0 +1,38 @@ +package class03; + +public class Code02_DeleteGivenValue { + + public static class Node { + public int value; + public Node next; + + public Node(int data) { + this.value = data; + } + } + + // head = removeValue(head, 2); + public static Node removeValue(Node head, int num) { + // head来到第一个不需要删的位置 + while (head != null) { + if (head.value != num) { + break; + } + head = head.next; + } + // 1 ) head == null + // 2 ) head != null + Node pre = head; + Node cur = head; + while (cur != null) { + if (cur.value == num) { + pre.next = cur.next; + } else { + pre = cur; + } + cur = cur.next; + } + return head; + } + +} diff --git a/体系学习班/class03/Code03_DoubleEndsQueueToStackAndQueue.java b/体系学习班/class03/Code03_DoubleEndsQueueToStackAndQueue.java new file mode 100644 index 0000000..e9b30a9 --- /dev/null +++ b/体系学习班/class03/Code03_DoubleEndsQueueToStackAndQueue.java @@ -0,0 +1,183 @@ +package class03; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Stack; + +public class Code03_DoubleEndsQueueToStackAndQueue { + + public static class Node { + public T value; + public Node last; + public Node next; + + public Node(T data) { + value = data; + } + } + + public static class DoubleEndsQueue { + public Node head; + public Node tail; + + public void addFromHead(T value) { + Node cur = new Node(value); + if (head == null) { + head = cur; + tail = cur; + } else { + cur.next = head; + head.last = cur; + head = cur; + } + } + + public void addFromBottom(T value) { + Node cur = new Node(value); + if (head == null) { + head = cur; + tail = cur; + } else { + cur.last = tail; + tail.next = cur; + tail = cur; + } + } + + public T popFromHead() { + if (head == null) { + return null; + } + Node cur = head; + if (head == tail) { + head = null; + tail = null; + } else { + head = head.next; + cur.next = null; + head.last = null; + } + return cur.value; + } + + public T popFromBottom() { + if (head == null) { + return null; + } + Node cur = tail; + if (head == tail) { + head = null; + tail = null; + } else { + tail = tail.last; + tail.next = null; + cur.last = null; + } + return cur.value; + } + + public boolean isEmpty() { + return head == null; + } + + } + + public static class MyStack { + private DoubleEndsQueue queue; + + public MyStack() { + queue = new DoubleEndsQueue(); + } + + public void push(T value) { + queue.addFromHead(value); + } + + public T pop() { + return queue.popFromHead(); + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + } + + public static class MyQueue { + private DoubleEndsQueue queue; + + public MyQueue() { + queue = new DoubleEndsQueue(); + } + + public void push(T value) { + queue.addFromHead(value); + } + + public T poll() { + return queue.popFromBottom(); + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + } + + public static boolean isEqual(Integer o1, Integer o2) { + if (o1 == null && o2 != null) { + return false; + } + if (o1 != null && o2 == null) { + return false; + } + if (o1 == null && o2 == null) { + return true; + } + return o1.equals(o2); + } + + public static void main(String[] args) { + int oneTestDataNum = 100; + int value = 10000; + int testTimes = 100000; + for (int i = 0; i < testTimes; i++) { + MyStack myStack = new MyStack<>(); + MyQueue myQueue = new MyQueue<>(); + Stack stack = new Stack<>(); + Queue queue = new LinkedList<>(); + for (int j = 0; j < oneTestDataNum; j++) { + int nums = (int) (Math.random() * value); + if (stack.isEmpty()) { + myStack.push(nums); + stack.push(nums); + } else { + if (Math.random() < 0.5) { + myStack.push(nums); + stack.push(nums); + } else { + if (!isEqual(myStack.pop(), stack.pop())) { + System.out.println("oops!"); + } + } + } + int numq = (int) (Math.random() * value); + if (queue.isEmpty()) { + myQueue.push(numq); + queue.offer(numq); + } else { + if (Math.random() < 0.5) { + myQueue.push(numq); + queue.offer(numq); + } else { + if (!isEqual(myQueue.poll(), queue.poll())) { + System.out.println("oops!"); + } + } + } + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class03/Code04_RingArray.java b/体系学习班/class03/Code04_RingArray.java new file mode 100644 index 0000000..cad73b4 --- /dev/null +++ b/体系学习班/class03/Code04_RingArray.java @@ -0,0 +1,50 @@ +package class03; + +public class Code04_RingArray { + + public static class MyQueue { + private int[] arr; + private int pushi;// end + private int polli;// begin + private int size; + private final int limit; + + public MyQueue(int limit) { + arr = new int[limit]; + pushi = 0; + polli = 0; + size = 0; + this.limit = limit; + } + + public void push(int value) { + if (size == limit) { + throw new RuntimeException("队列满了,不能再加了"); + } + size++; + arr[pushi] = value; + pushi = nextIndex(pushi); + } + + public int pop() { + if (size == 0) { + throw new RuntimeException("队列空了,不能再拿了"); + } + size--; + int ans = arr[polli]; + polli = nextIndex(polli); + return ans; + } + + public boolean isEmpty() { + return size == 0; + } + + // 如果现在的下标是i,返回下一个位置 + private int nextIndex(int i) { + return i < limit - 1 ? i + 1 : 0; + } + + } + +} diff --git a/体系学习班/class03/Code05_GetMinStack.java b/体系学习班/class03/Code05_GetMinStack.java new file mode 100644 index 0000000..91b20ae --- /dev/null +++ b/体系学习班/class03/Code05_GetMinStack.java @@ -0,0 +1,105 @@ +package class03; + +import java.util.Stack; + +public class Code05_GetMinStack { + + public static class MyStack1 { + private Stack stackData; + private Stack stackMin; + + public MyStack1() { + this.stackData = new Stack(); + this.stackMin = new Stack(); + } + + public void push(int newNum) { + if (this.stackMin.isEmpty()) { + this.stackMin.push(newNum); + } else if (newNum <= this.getmin()) { + this.stackMin.push(newNum); + } + this.stackData.push(newNum); + } + + public int pop() { + if (this.stackData.isEmpty()) { + throw new RuntimeException("Your stack is empty."); + } + int value = this.stackData.pop(); + if (value == this.getmin()) { + this.stackMin.pop(); + } + return value; + } + + public int getmin() { + if (this.stackMin.isEmpty()) { + throw new RuntimeException("Your stack is empty."); + } + return this.stackMin.peek(); + } + } + + public static class MyStack2 { + private Stack stackData; + private Stack stackMin; + + public MyStack2() { + this.stackData = new Stack(); + this.stackMin = new Stack(); + } + + public void push(int newNum) { + if (this.stackMin.isEmpty()) { + this.stackMin.push(newNum); + } else if (newNum < this.getmin()) { + this.stackMin.push(newNum); + } else { + int newMin = this.stackMin.peek(); + this.stackMin.push(newMin); + } + this.stackData.push(newNum); + } + + public int pop() { + if (this.stackData.isEmpty()) { + throw new RuntimeException("Your stack is empty."); + } + this.stackMin.pop(); + return this.stackData.pop(); + } + + public int getmin() { + if (this.stackMin.isEmpty()) { + throw new RuntimeException("Your stack is empty."); + } + return this.stackMin.peek(); + } + } + + public static void main(String[] args) { + MyStack1 stack1 = new MyStack1(); + stack1.push(3); + System.out.println(stack1.getmin()); + stack1.push(4); + System.out.println(stack1.getmin()); + stack1.push(1); + System.out.println(stack1.getmin()); + System.out.println(stack1.pop()); + System.out.println(stack1.getmin()); + + System.out.println("============="); + + MyStack1 stack2 = new MyStack1(); + stack2.push(3); + System.out.println(stack2.getmin()); + stack2.push(4); + System.out.println(stack2.getmin()); + stack2.push(1); + System.out.println(stack2.getmin()); + System.out.println(stack2.pop()); + System.out.println(stack2.getmin()); + } + +} diff --git a/体系学习班/class03/Code06_TwoStacksImplementQueue.java b/体系学习班/class03/Code06_TwoStacksImplementQueue.java new file mode 100644 index 0000000..483d7a7 --- /dev/null +++ b/体系学习班/class03/Code06_TwoStacksImplementQueue.java @@ -0,0 +1,60 @@ +package class03; + +import java.util.Stack; + +public class Code06_TwoStacksImplementQueue { + + public static class TwoStacksQueue { + public Stack stackPush; + public Stack stackPop; + + public TwoStacksQueue() { + stackPush = new Stack(); + stackPop = new Stack(); + } + + // push栈向pop栈倒入数据 + private void pushToPop() { + if (stackPop.empty()) { + while (!stackPush.empty()) { + stackPop.push(stackPush.pop()); + } + } + } + + public void add(int pushInt) { + stackPush.push(pushInt); + pushToPop(); + } + + public int poll() { + if (stackPop.empty() && stackPush.empty()) { + throw new RuntimeException("Queue is empty!"); + } + pushToPop(); + return stackPop.pop(); + } + + public int peek() { + if (stackPop.empty() && stackPush.empty()) { + throw new RuntimeException("Queue is empty!"); + } + pushToPop(); + return stackPop.peek(); + } + } + + public static void main(String[] args) { + TwoStacksQueue test = new TwoStacksQueue(); + test.add(1); + test.add(2); + test.add(3); + System.out.println(test.peek()); + System.out.println(test.poll()); + System.out.println(test.peek()); + System.out.println(test.poll()); + System.out.println(test.peek()); + System.out.println(test.poll()); + } + +} diff --git a/体系学习班/class03/Code07_TwoQueueImplementStack.java b/体系学习班/class03/Code07_TwoQueueImplementStack.java new file mode 100644 index 0000000..a2a3c54 --- /dev/null +++ b/体系学习班/class03/Code07_TwoQueueImplementStack.java @@ -0,0 +1,90 @@ +package class03; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Stack; + +public class Code07_TwoQueueImplementStack { + + public static class TwoQueueStack { + public Queue queue; + public Queue help; + + public TwoQueueStack() { + queue = new LinkedList<>(); + help = new LinkedList<>(); + } + + public void push(T value) { + queue.offer(value); + } + + public T poll() { + while (queue.size() > 1) { + help.offer(queue.poll()); + } + T ans = queue.poll(); + Queue tmp = queue; + queue = help; + help = tmp; + return ans; + } + + public T peek() { + while (queue.size() > 1) { + help.offer(queue.poll()); + } + T ans = queue.poll(); + help.offer(ans); + Queue tmp = queue; + queue = help; + help = tmp; + return ans; + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + } + + public static void main(String[] args) { + System.out.println("test begin"); + TwoQueueStack myStack = new TwoQueueStack<>(); + Stack test = new Stack<>(); + int testTime = 1000000; + int max = 1000000; + for (int i = 0; i < testTime; i++) { + if (myStack.isEmpty()) { + if (!test.isEmpty()) { + System.out.println("Oops"); + } + int num = (int) (Math.random() * max); + myStack.push(num); + test.push(num); + } else { + if (Math.random() < 0.25) { + int num = (int) (Math.random() * max); + myStack.push(num); + test.push(num); + } else if (Math.random() < 0.5) { + if (!myStack.peek().equals(test.peek())) { + System.out.println("Oops"); + } + } else if (Math.random() < 0.75) { + if (!myStack.poll().equals(test.pop())) { + System.out.println("Oops"); + } + } else { + if (myStack.isEmpty() != test.isEmpty()) { + System.out.println("Oops"); + } + } + } + } + + System.out.println("test finish!"); + + } + +} diff --git a/体系学习班/class03/Code08_GetMax.java b/体系学习班/class03/Code08_GetMax.java new file mode 100644 index 0000000..81661d8 --- /dev/null +++ b/体系学习班/class03/Code08_GetMax.java @@ -0,0 +1,24 @@ +package class03; + +public class Code08_GetMax { + + // 求arr中的最大值 + public static int getMax(int[] arr) { + return process(arr, 0, arr.length - 1); + } + + // arr[L..R]范围上求最大值 L ... R N + public static int process(int[] arr, int L, int R) { + // arr[L..R]范围上只有一个数,直接返回,base case + if (L == R) { + return arr[L]; + } + // L...R 不只一个数 + // mid = (L + R) / 2 + int mid = L + ((R - L) >> 1); // 中点 1 + int leftMax = process(arr, L, mid); + int rightMax = process(arr, mid + 1, R); + return Math.max(leftMax, rightMax); + } + +} diff --git a/体系学习班/class03/HashMapAndSortedMap.java b/体系学习班/class03/HashMapAndSortedMap.java new file mode 100644 index 0000000..bd54a06 --- /dev/null +++ b/体系学习班/class03/HashMapAndSortedMap.java @@ -0,0 +1,128 @@ +package class03; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.TreeMap; + +public class HashMapAndSortedMap { + + public static class Node { + public int value; + + public Node(int v) { + value = v; + } + } + + public static class Zuo { + public int value; + + public Zuo(int v) { + value = v; + } + } + + public static void main(String[] args) { + + HashMap test = new HashMap<>(); + Integer a = 19000000; + Integer b = 19000000; + System.out.println(a == b); + + test.put(a, "我是3"); + System.out.println(test.containsKey(b)); + + Zuo z1 = new Zuo(1); + Zuo z2 = new Zuo(1); + HashMap test2 = new HashMap<>(); + test2.put(z1, "我是z1"); + System.out.println(test2.containsKey(z2)); + + // UnSortedMap + HashMap map = new HashMap<>(); + map.put(1000000, "我是1000000"); + map.put(2, "我是2"); + map.put(3, "我是3"); + map.put(4, "我是4"); + map.put(5, "我是5"); + map.put(6, "我是6"); + map.put(1000000, "我是1000001"); + + System.out.println(map.containsKey(1)); + System.out.println(map.containsKey(10)); + + System.out.println(map.get(4)); + System.out.println(map.get(10)); + + map.put(4, "他是4"); + System.out.println(map.get(4)); + + map.remove(4); + System.out.println(map.get(4)); + + // key + HashSet set = new HashSet<>(); + set.add("abc"); + set.contains("abc"); + set.remove("abc"); + + // 哈希表,增、删、改、查,在使用时,O(1) + + System.out.println("====================="); + + Integer c = 100000; + Integer d = 100000; + System.out.println(c.equals(d)); + + Integer e = 127; // - 128 ~ 127 + Integer f = 127; + System.out.println(e == f); + + HashMap map2 = new HashMap<>(); + Node node1 = new Node(1); + Node node2 = node1; + map2.put(node1, "我是node1"); + map2.put(node2, "我是node1"); + System.out.println(map2.size()); + + System.out.println("======================"); + + // TreeMap 有序表:接口名 + // 红黑树、avl、sb树、跳表 + // O(logN) + System.out.println("有序表测试开始"); + TreeMap treeMap = new TreeMap<>(); + + treeMap.put(3, "我是3"); + treeMap.put(4, "我是4"); + treeMap.put(8, "我是8"); + treeMap.put(5, "我是5"); + treeMap.put(7, "我是7"); + treeMap.put(1, "我是1"); + treeMap.put(2, "我是2"); + + System.out.println(treeMap.containsKey(1)); + System.out.println(treeMap.containsKey(10)); + + System.out.println(treeMap.get(4)); + System.out.println(treeMap.get(10)); + + treeMap.put(4, "他是4"); + System.out.println(treeMap.get(4)); + + // treeMap.remove(4); + System.out.println(treeMap.get(4)); + + System.out.println("新鲜:"); + + System.out.println(treeMap.firstKey()); + System.out.println(treeMap.lastKey()); + // <= 4 + System.out.println(treeMap.floorKey(4)); + // >= 4 + System.out.println(treeMap.ceilingKey(4)); + // O(logN) + + } + +} diff --git a/体系学习班/class04/Code01_MergeSort.java b/体系学习班/class04/Code01_MergeSort.java new file mode 100644 index 0000000..c0ddb6b --- /dev/null +++ b/体系学习班/class04/Code01_MergeSort.java @@ -0,0 +1,147 @@ +package class04; + +public class Code01_MergeSort { + + // 递归方法实现 + 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) + public static void process(int[] arr, int L, int R) { + if (L == R) { // base case + return; + } + int mid = L + ((R - L) >> 1); + process(arr, L, mid); + process(arr, mid + 1, 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; + int p1 = L; + int p2 = M + 1; + 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]; + } + } + + // 非递归方法实现 + 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; + } + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + mergeSort1(arr1); + mergeSort2(arr2); + if (!isEqual(arr1, arr2)) { + System.out.println("出错了!"); + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class04/Code02_SmallSum.java b/体系学习班/class04/Code02_SmallSum.java new file mode 100644 index 0000000..1167a64 --- /dev/null +++ b/体系学习班/class04/Code02_SmallSum.java @@ -0,0 +1,137 @@ +package class04; + +public class Code02_SmallSum { + + public static int smallSum(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + return process(arr, 0, arr.length - 1); + } + + // arr[L..R]既要排好序,也要求小和返回 + // 所有merge时,产生的小和,累加 + // 左 排序 merge + // 右 排序 merge + // merge + 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 = 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++]; + } + for (i = 0; i < help.length; i++) { + arr[L + i] = help[i]; + } + return res; + } + + // for test + public static int comparator(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int res = 0; + for (int i = 1; i < arr.length; i++) { + for (int j = 0; j < i; j++) { + res += arr[j] < arr[i] ? arr[j] : 0; + } + } + return res; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + if (smallSum(arr1) != comparator(arr2)) { + succeed = false; + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/体系学习班/class04/Code03_ReversePair.java b/体系学习班/class04/Code03_ReversePair.java new file mode 100644 index 0000000..0e6e531 --- /dev/null +++ b/体系学习班/class04/Code03_ReversePair.java @@ -0,0 +1,130 @@ +package class04; + +public class Code03_ReversePair { + + public static int reverPairNumber(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + return process(arr, 0, arr.length - 1); + } + + // arr[L..R]既要排好序,也要求逆序对数量返回 + // 所有merge时,产生的逆序对数量,累加,返回 + // 左 排序 merge并产生逆序对数量 + // 右 排序 merge并产生逆序对数量 + 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; + } + + // for test + public static int comparator(int[] arr) { + int ans = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + if (arr[i] > arr[j]) { + ans++; + } + } + } + return ans; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + if (reverPairNumber(arr1) != comparator(arr2)) { + System.out.println("Oops!"); + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class04/Code04_BiggerThanRightTwice.java b/体系学习班/class04/Code04_BiggerThanRightTwice.java new file mode 100644 index 0000000..7eb4f7e --- /dev/null +++ b/体系学习班/class04/Code04_BiggerThanRightTwice.java @@ -0,0 +1,135 @@ +package class04; + +// 本题测试链接 : https://leetcode.com/problems/reverse-pairs/ +public class Code04_BiggerThanRightTwice { + + public static int reversePairs(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 && (long) arr[i] > (long) arr[windowR] * 2) { + windowR++; + } + ans += windowR - m - 1; + } + 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; + } + + // for test + public static int comparator(int[] arr) { + int ans = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + if (arr[i] > (arr[j] << 1)) { + ans++; + } + } + } + return ans; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) ((maxValue + 1) * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + if (reversePairs(arr1) != comparator(arr2)) { + System.out.println("Oops!"); + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class05/Code01_CountOfRangeSum.java b/体系学习班/class05/Code01_CountOfRangeSum.java new file mode 100644 index 0000000..559a7d8 --- /dev/null +++ b/体系学习班/class05/Code01_CountOfRangeSum.java @@ -0,0 +1,63 @@ +package class05; + +// 这道题直接在leetcode测评: +// https://leetcode.com/problems/count-of-range-sum/ +public class Code01_CountOfRangeSum { + + 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; + } + +} diff --git a/体系学习班/class05/Code02_PartitionAndQuickSort.java b/体系学习班/class05/Code02_PartitionAndQuickSort.java new file mode 100644 index 0000000..e8a9d7a --- /dev/null +++ b/体系学习班/class05/Code02_PartitionAndQuickSort.java @@ -0,0 +1,197 @@ +package class05; + +public class Code02_PartitionAndQuickSort { + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // arr[L..R]上,以arr[R]位置的数做划分值 + // <= X > X + // <= X X + public static int partition(int[] arr, int L, int R) { + if (L > R) { + return -1; + } + if (L == R) { + return L; + } + int lessEqual = L - 1; + int index = L; + while (index < R) { + if (arr[index] <= arr[R]) { + swap(arr, index, ++lessEqual); + } + index++; + } + swap(arr, ++lessEqual, R); + return lessEqual; + } + + // arr[L...R] 玩荷兰国旗问题的划分,以arr[R]做划分值 + // arr[R] + public static int[] netherlandsFlag(int[] arr, int L, int R) { + if (L > R) { // L...R L>R + return new int[] { -1, -1 }; + } + if (L == R) { + return new int[] { L, R }; + } + int less = L - 1; // < 区 右边界 + int more = R; // > 区 左边界 + int index = L; + while (index < more) { // 当前位置,不能和 >区的左边界撞上 + if (arr[index] == arr[R]) { + index++; + } else if (arr[index] < arr[R]) { +// swap(arr, less + 1, index); +// less++; +// index++; + swap(arr, index++, ++less); + } else { // > + swap(arr, index, --more); + } + } + swap(arr, more, R); // <[R] =[R] >[R] + return new int[] { less + 1, more }; + } + + public static void quickSort1(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + process1(arr, 0, arr.length - 1); + } + + public static void process1(int[] arr, int L, int R) { + if (L >= R) { + return; + } + // L..R partition arr[R] [ <=arr[R] arr[R] >arr[R] ] + int M = partition(arr, L, R); + process1(arr, L, M - 1); + process1(arr, M + 1, R); + } + + + + + + + public static void quickSort2(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + process2(arr, 0, arr.length - 1); + } + + // arr[L...R] 排有序,快排2.0方式 + public static void process2(int[] arr, int L, int R) { + if (L >= R) { + return; + } + // [ equalArea[0] , equalArea[0]] + int[] equalArea = netherlandsFlag(arr, L, R); + process2(arr, L, equalArea[0] - 1); + process2(arr, equalArea[1] + 1, R); + } + + + + + + + + public static void quickSort3(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + process3(arr, 0, arr.length - 1); + } + + public static void process3(int[] arr, int L, int R) { + if (L >= R) { + return; + } + swap(arr, L + (int) (Math.random() * (R - L + 1)), R); + int[] equalArea = netherlandsFlag(arr, L, R); + process3(arr, L, equalArea[0] - 1); + process3(arr, equalArea[1] + 1, R); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + int[] arr3 = copyArray(arr1); + quickSort1(arr1); + quickSort2(arr2); + quickSort3(arr3); + if (!isEqual(arr1, arr2) || !isEqual(arr2, arr3)) { + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Oops!"); + + } + +} diff --git a/体系学习班/class05/Code03_QuickSortRecursiveAndUnrecursive.java b/体系学习班/class05/Code03_QuickSortRecursiveAndUnrecursive.java new file mode 100644 index 0000000..57a7657 --- /dev/null +++ b/体系学习班/class05/Code03_QuickSortRecursiveAndUnrecursive.java @@ -0,0 +1,195 @@ +package class05; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Stack; + +public class Code03_QuickSortRecursiveAndUnrecursive { + + // 荷兰国旗问题 + public static int[] netherlandsFlag(int[] arr, int L, int R) { + if (L > R) { + return new int[] { -1, -1 }; + } + if (L == R) { + return new int[] { L, R }; + } + int less = L - 1; + int more = R; + int index = L; + while (index < more) { + if (arr[index] == arr[R]) { + index++; + } else if (arr[index] < arr[R]) { + swap(arr, index++, ++less); + } else { + swap(arr, index, --more); + } + } + swap(arr, more, R); + return new int[] { less + 1, more }; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 快排递归版本 + public static void quickSort1(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + process(arr, 0, arr.length - 1); + } + + public static void process(int[] arr, int L, int R) { + if (L >= R) { + return; + } + swap(arr, L + (int) (Math.random() * (R - L + 1)), R); + int[] equalArea = netherlandsFlag(arr, L, R); + process(arr, L, equalArea[0] - 1); + process(arr, equalArea[1] + 1, R); + } + + // 快排非递归版本需要的辅助类 + // 要处理的是什么范围上的排序 + public static class Op { + public int l; + public int r; + + public Op(int left, int right) { + l = left; + r = right; + } + } + + // 快排3.0 非递归版本 用栈来执行 + public static void quickSort2(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int N = arr.length; + swap(arr, (int) (Math.random() * N), N - 1); + int[] equalArea = netherlandsFlag(arr, 0, N - 1); + int el = equalArea[0]; + int er = equalArea[1]; + Stack stack = new Stack<>(); + stack.push(new Op(0, el - 1)); + stack.push(new Op(er + 1, N - 1)); + while (!stack.isEmpty()) { + Op op = stack.pop(); // op.l ... op.r + if (op.l < op.r) { + swap(arr, op.l + (int) (Math.random() * (op.r - op.l + 1)), op.r); + equalArea = netherlandsFlag(arr, op.l, op.r); + el = equalArea[0]; + er = equalArea[1]; + stack.push(new Op(op.l, el - 1)); + stack.push(new Op(er + 1, op.r)); + } + } + } + + // 快排3.0 非递归版本 用队列来执行 + public static void quickSort3(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int N = arr.length; + swap(arr, (int) (Math.random() * N), N - 1); + int[] equalArea = netherlandsFlag(arr, 0, N - 1); + int el = equalArea[0]; + int er = equalArea[1]; + Queue queue = new LinkedList<>(); + queue.offer(new Op(0, el - 1)); + queue.offer(new Op(er + 1, N - 1)); + while (!queue.isEmpty()) { + Op op = queue.poll(); + if (op.l < op.r) { + swap(arr, op.l + (int) (Math.random() * (op.r - op.l + 1)), op.r); + equalArea = netherlandsFlag(arr, op.l, op.r); + el = equalArea[0]; + er = equalArea[1]; + queue.offer(new Op(op.l, el - 1)); + queue.offer(new Op(er + 1, op.r)); + } + } + } + + // 生成随机数组(用于测试) + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // 拷贝数组(用于测试) + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // 对比两个数组(用于测试) + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // 打印数组(用于测试) + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 跑大样本随机测试(对数器) + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + int[] arr3 = copyArray(arr1); + quickSort1(arr1); + quickSort2(arr2); + quickSort3(arr3); + if (!isEqual(arr1, arr2) || !isEqual(arr1, arr3)) { + succeed = false; + break; + } + } + System.out.println("test end"); + System.out.println("测试" + testTime + "组是否全部通过:" + (succeed ? "是" : "否")); + } + +} diff --git a/体系学习班/class05/Code04_DoubleLinkedListQuickSort.java b/体系学习班/class05/Code04_DoubleLinkedListQuickSort.java new file mode 100644 index 0000000..a3014dc --- /dev/null +++ b/体系学习班/class05/Code04_DoubleLinkedListQuickSort.java @@ -0,0 +1,300 @@ +package class05; + +import java.util.ArrayList; +import java.util.Comparator; + +// 双向链表的随机快速排序 +// 课上没有讲,因为这是群里同学问的问题 +// 作为补充放在这,有需要的同学可以看看 +// 和课上讲的数组的经典快速排序在算法上没有区别 +// 但是coding需要更小心 +public class Code04_DoubleLinkedListQuickSort { + + public static class Node { + public int value; + public Node last; + public Node next; + + public Node(int v) { + value = v; + } + } + + public static Node quickSort(Node h) { + if (h == null) { + return null; + } + int N = 0; + Node c = h; + Node e = null; + while (c != null) { + N++; + e = c; + c = c.next; + } + return process(h, e, N).h; + } + + public static class HeadTail { + public Node h; + public Node t; + + public HeadTail(Node head, Node tail) { + h = head; + t = tail; + } + } + + // L...R是一个双向链表的头和尾, + // L的last指针指向null,R的next指针指向null + // 也就是说L的左边没有,R的右边也没节点 + // 就是一个正常的双向链表,一共有N个节点 + // 将这一段用随机快排的方式排好序 + // 返回排好序之后的双向链表的头和尾(HeadTail) + public static HeadTail process(Node L, Node R, int N) { + if (L == null) { + return null; + } + if (L == R) { + return new HeadTail(L, R); + } + // L..R上不只一个节点 + // 随机得到一个随机下标 + int randomIndex = (int) (Math.random() * N); + // 根据随机下标得到随机节点 + Node randomNode = L; + while (randomIndex-- != 0) { + randomNode = randomNode.next; + } + // 把随机节点从原来的环境里分离出来 + // 比如 a(L) -> b -> c -> d(R), 如果randomNode = c,那么调整之后 + // a(L) -> b -> d(R), c会被挖出来,randomNode = c + if (randomNode == L || randomNode == R) { + if (randomNode == L) { + L = randomNode.next; + L.last = null; + } else { + randomNode.last.next = null; + } + } else { // randomNode一定是中间的节点 + randomNode.last.next = randomNode.next; + randomNode.next.last = randomNode.last; + } + randomNode.last = null; + randomNode.next = null; + Info info = partition(L, randomNode); + // randomNode的部分去排序 + HeadTail rht = process(info.rh, info.rt, info.rs); + // 左部分排好序、右部分排好序 + // 把它们串在一起 + if (lht != null) { + lht.t.next = info.eh; + info.eh.last = lht.t; + } + if (rht != null) { + info.et.next = rht.h; + rht.h.last = info.et; + } + // 返回排好序之后总的头和总的尾 + Node h = lht != null ? lht.h : info.eh; + Node t = rht != null ? rht.t : info.et; + return new HeadTail(h, t); + } + + public static class Info { + public Node lh; + public Node lt; + public int ls; + public Node rh; + public Node rt; + public int rs; + public Node eh; + public Node et; + + public Info(Node lH, Node lT, int lS, Node rH, Node rT, int rS, Node eH, Node eT) { + lh = lH; + lt = lT; + ls = lS; + rh = rH; + rt = rT; + rs = rS; + eh = eH; + et = eT; + } + } + + // (L....一直到空),是一个双向链表 + // pivot是一个不在(L....一直到空)的独立节点,它作为划分值 + // 根据荷兰国旗问题的划分方式,把(L....一直到空)划分成: + // pivot 三个部分,然后把pivot融进=pivot的部分 + // 比如 4(L)->6->7->1->5->0->9->null pivot=5(这个5和链表中的5,是不同的节点) + // 调整完成后: + // 4->1->0 小于的部分 + // 5->5 等于的部分 + // 6->7->9 大于的部分 + // 三个部分是断开的 + // 然后返回Info: + // 小于部分的头、尾、节点个数 : lh,lt,ls + // 大于部分的头、尾、节点个数 : rh,rt,rs + // 等于部分的头、尾 : eh,et + public static Info partition(Node L, Node pivot) { + Node lh = null; + Node lt = null; + int ls = 0; + Node rh = null; + Node rt = null; + int rs = 0; + Node eh = pivot; + Node et = pivot; + Node tmp = null; + while (L != null) { + tmp = L.next; + L.next = null; + L.last = null; + if (L.value < pivot.value) { + ls++; + if (lh == null) { + lh = L; + lt = L; + } else { + lt.next = L; + L.last = lt; + lt = L; + } + } else if (L.value > pivot.value) { + rs++; + if (rh == null) { + rh = L; + rt = L; + } else { + rt.next = L; + L.last = rt; + rt = L; + } + } else { + et.next = L; + L.last = et; + et = L; + } + L = tmp; + } + return new Info(lh, lt, ls, rh, rt, rs, eh, et); + } + + // 为了测试 + public static class NodeComp implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.value - o2.value; + } + + } + + // 为了测试 + public static Node sort(Node head) { + if (head == null) { + return null; + } + ArrayList arr = new ArrayList<>(); + while (head != null) { + arr.add(head); + head = head.next; + } + arr.sort(new NodeComp()); + Node h = arr.get(0); + h.last = null; + Node p = h; + for (int i = 1; i < arr.size(); i++) { + Node c = arr.get(i); + p.next = c; + c.last = p; + c.next = null; + p = c; + } + return h; + } + + // 为了测试 + public static Node generateRandomDoubleLinkedList(int n, int v) { + if (n == 0) { + return null; + } + Node[] arr = new Node[n]; + for (int i = 0; i < n; i++) { + arr[i] = new Node((int) (Math.random() * v)); + } + Node head = arr[0]; + Node pre = head; + for (int i = 1; i < n; i++) { + pre.next = arr[i]; + arr[i].last = pre; + pre = arr[i]; + } + return head; + } + + // 为了测试 + public static Node cloneDoubleLinkedList(Node head) { + if (head == null) { + return null; + } + Node h = new Node(head.value); + Node p = h; + head = head.next; + while (head != null) { + Node c = new Node(head.value); + p.next = c; + c.last = p; + p = c; + head = head.next; + } + return h; + } + + // 为了测试 + public static boolean equal(Node h1, Node h2) { + return doubleLinkedListToString(h1).equals(doubleLinkedListToString(h2)); + } + + // 为了测试 + public static String doubleLinkedListToString(Node head) { + Node cur = head; + Node end = null; + StringBuilder builder = new StringBuilder(); + while (cur != null) { + builder.append(cur.value + " "); + end = cur; + cur = cur.next; + } + builder.append("| "); + while (end != null) { + builder.append(end.value + " "); + end = end.last; + } + return builder.toString(); + } + + // 为了测试 + public static void main(String[] args) { + int N = 500; + int V = 500; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * N); + Node head1 = generateRandomDoubleLinkedList(size, V); + Node head2 = cloneDoubleLinkedList(head1); + Node sort1 = quickSort(head1); + Node sort2 = sort(head2); + if (!equal(sort1, sort2)) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class06/Code01_Comparator.java b/体系学习班/class06/Code01_Comparator.java new file mode 100644 index 0000000..af23b64 --- /dev/null +++ b/体系学习班/class06/Code01_Comparator.java @@ -0,0 +1,168 @@ +package class06; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.TreeMap; + +public class Code01_Comparator { + + public static class Student { + public String name; + public int id; + public int age; + + public Student(String name, int id, int age) { + this.name = name; + this.id = id; + this.age = age; + } + } + + // 任何比较器: + // compare方法里,遵循一个统一的规范: + // 返回负数的时候,认为第一个参数应该排在前面 + // 返回正数的时候,认为第二个参数应该排在前面 + // 返回0的时候,认为无所谓谁放前面 + public static class IdShengAgeJiangOrder implements Comparator { + + // 根据id从小到大,但是如果id一样,按照年龄从大到小 + @Override + public int compare(Student o1, Student o2) { + return o1.id != o2.id ? (o1.id - o2.id) : (o2.age - o1.age); + } + + } + + public static class IdAscendingComparator implements Comparator { + + // 返回负数的时候,第一个参数排在前面 + // 返回正数的时候,第二个参数排在前面 + // 返回0的时候,谁在前面无所谓 + @Override + public int compare(Student o1, Student o2) { + return o1.id - o2.id; + } + + } + + public static class IdDescendingComparator implements Comparator { + + @Override + public int compare(Student o1, Student o2) { + return o2.id - o1.id; + } + + } + + // 先按照id排序,id小的,放前面; + // id一样,age大的,前面; + public static class IdInAgeDe implements Comparator { + + @Override + public int compare(Student o1, Student o2) { + return o1.id != o2.id ? o1.id - o2.id : (o2.age - o1.age); + } + + } + + public static void printStudents(Student[] students) { + for (Student student : students) { + System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age); + } + } + + public static void printArray(Integer[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static class MyComp implements Comparator { + + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + + } + + public static class AComp implements Comparator { + + // 如果返回负数,认为第一个参数应该拍在前面 + // 如果返回正数,认为第二个参数应该拍在前面 + // 如果返回0,认为谁放前面都行 + @Override + public int compare(Integer arg0, Integer arg1) { + + return arg1 - arg0; + +// return 0; + } + + } + + public static void main(String[] args) { + + Integer[] arr = { 5, 4, 3, 2, 7, 9, 1, 0 }; + + Arrays.sort(arr, new AComp()); + + for (int i = 0; i < arr.length; i++) { + System.out.println(arr[i]); + } + + System.out.println("==========================="); + + Student student1 = new Student("A", 4, 40); + Student student2 = new Student("B", 4, 21); + Student student3 = new Student("C", 3, 12); + Student student4 = new Student("D", 3, 62); + Student student5 = new Student("E", 3, 42); + // D E C A B + + Student[] students = new Student[] { student1, student2, student3, student4, student5 }; + System.out.println("第一条打印"); + + Arrays.sort(students, new IdShengAgeJiangOrder()); + for (int i = 0; i < students.length; i++) { + Student s = students[i]; + System.out.println(s.name + "," + s.id + "," + s.age); + } + + System.out.println("第二条打印"); + ArrayList studentList = new ArrayList<>(); + studentList.add(student1); + studentList.add(student2); + studentList.add(student3); + studentList.add(student4); + studentList.add(student5); + studentList.sort(new IdShengAgeJiangOrder()); + for (int i = 0; i < studentList.size(); i++) { + Student s = studentList.get(i); + System.out.println(s.name + "," + s.id + "," + s.age); + } + // N * logN + System.out.println("第三条打印"); + student1 = new Student("A", 4, 40); + student2 = new Student("B", 4, 21); + student3 = new Student("C", 4, 12); + student4 = new Student("D", 4, 62); + student5 = new Student("E", 4, 42); + TreeMap treeMap = new TreeMap<>((a, b) -> (a.id - b.id)); + treeMap.put(student1, "我是学生1,我的名字叫A"); + treeMap.put(student2, "我是学生2,我的名字叫B"); + treeMap.put(student3, "我是学生3,我的名字叫C"); + treeMap.put(student4, "我是学生4,我的名字叫D"); + treeMap.put(student5, "我是学生5,我的名字叫E"); + for (Student s : treeMap.keySet()) { + System.out.println(s.name + "," + s.id + "," + s.age); + } + + } + +} diff --git a/体系学习班/class06/Code02_Heap.java b/体系学习班/class06/Code02_Heap.java new file mode 100644 index 0000000..28140a1 --- /dev/null +++ b/体系学习班/class06/Code02_Heap.java @@ -0,0 +1,192 @@ +package class06; + +import java.util.Comparator; +import java.util.PriorityQueue; + +public class Code02_Heap { + + public static class MyMaxHeap { + private int[] heap; + private final int limit; + private int heapSize; + + public MyMaxHeap(int limit) { + heap = new int[limit]; + this.limit = limit; + heapSize = 0; + } + + public boolean isEmpty() { + return heapSize == 0; + } + + public boolean isFull() { + return heapSize == limit; + } + + public void push(int value) { + if (heapSize == limit) { + throw new RuntimeException("heap is full"); + } + heap[heapSize] = value; + // value heapSize + heapInsert(heap, heapSize++); + } + + // 用户此时,让你返回最大值,并且在大根堆中,把最大值删掉 + // 剩下的数,依然保持大根堆组织 + public int pop() { + int ans = heap[0]; + swap(heap, 0, --heapSize); + heapify(heap, 0, heapSize); + return ans; + } + + // 新加进来的数,现在停在了index位置,请依次往上移动, + // 移动到0位置,或者干不掉自己的父亲了,停! + private void heapInsert(int[] arr, int index) { + // [index] [index-1]/2 + // index == 0 + while (arr[index] > arr[(index - 1) / 2]) { + swap(arr, index, (index - 1) / 2); + index = (index - 1) / 2; + } + } + + // 从index位置,往下看,不断的下沉 + // 停:较大的孩子都不再比index位置的数大;已经没孩子了 + private void heapify(int[] arr, int index, int heapSize) { + int left = index * 2 + 1; + while (left < heapSize) { // 如果有左孩子,有没有右孩子,可能有可能没有! + // 把较大孩子的下标,给largest + int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left; + largest = arr[largest] > arr[index] ? largest : index; + if (largest == index) { + break; + } + // index和较大孩子,要互换 + swap(arr, largest, index); + index = largest; + left = index * 2 + 1; + } + } + + private void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + } + + public static class RightMaxHeap { + private int[] arr; + private final int limit; + private int size; + + public RightMaxHeap(int limit) { + arr = new int[limit]; + this.limit = limit; + size = 0; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull() { + return size == limit; + } + + public void push(int value) { + if (size == limit) { + throw new RuntimeException("heap is full"); + } + arr[size++] = value; + } + + public int pop() { + int maxIndex = 0; + for (int i = 1; i < size; i++) { + if (arr[i] > arr[maxIndex]) { + maxIndex = i; + } + } + int ans = arr[maxIndex]; + arr[maxIndex] = arr[--size]; + return ans; + } + + } + + public static class MyComparator implements Comparator { + + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + + } + + public static void main(String[] args) { + + // 小根堆 + PriorityQueue heap = new PriorityQueue<>(new MyComparator()); + heap.add(5); + heap.add(5); + heap.add(5); + heap.add(3); + // 5 , 3 + System.out.println(heap.peek()); + heap.add(7); + heap.add(0); + heap.add(7); + heap.add(0); + heap.add(7); + heap.add(0); + System.out.println(heap.peek()); + while (!heap.isEmpty()) { + System.out.println(heap.poll()); + } + + int value = 1000; + int limit = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + int curLimit = (int) (Math.random() * limit) + 1; + MyMaxHeap my = new MyMaxHeap(curLimit); + RightMaxHeap test = new RightMaxHeap(curLimit); + int curOpTimes = (int) (Math.random() * limit); + for (int j = 0; j < curOpTimes; j++) { + if (my.isEmpty() != test.isEmpty()) { + System.out.println("Oops!"); + } + if (my.isFull() != test.isFull()) { + System.out.println("Oops!"); + } + if (my.isEmpty()) { + int curValue = (int) (Math.random() * value); + my.push(curValue); + test.push(curValue); + } else if (my.isFull()) { + if (my.pop() != test.pop()) { + System.out.println("Oops!"); + } + } else { + if (Math.random() < 0.5) { + int curValue = (int) (Math.random() * value); + my.push(curValue); + test.push(curValue); + } else { + if (my.pop() != test.pop()) { + System.out.println("Oops!"); + } + } + } + } + } + System.out.println("finish!"); + + } + +} diff --git a/体系学习班/class06/Code03_HeapSort.java b/体系学习班/class06/Code03_HeapSort.java new file mode 100644 index 0000000..49e743a --- /dev/null +++ b/体系学习班/class06/Code03_HeapSort.java @@ -0,0 +1,158 @@ +package class06; + +import java.util.Arrays; +import java.util.PriorityQueue; + +public class Code03_HeapSort { + + // 堆排序额外空间复杂度O(1) + public static void heapSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + // O(N*logN) +// for (int i = 0; i < arr.length; i++) { // O(N) +// heapInsert(arr, i); // O(logN) +// } + // O(N) + for (int i = arr.length - 1; i >= 0; i--) { + heapify(arr, i, arr.length); + } + int heapSize = arr.length; + swap(arr, 0, --heapSize); + // O(N*logN) + while (heapSize > 0) { // O(N) + heapify(arr, 0, heapSize); // O(logN) + swap(arr, 0, --heapSize); // O(1) + } + } + + // arr[index]刚来的数,往上 + public static void heapInsert(int[] arr, int index) { + while (arr[index] > arr[(index - 1) / 2]) { + swap(arr, index, (index - 1) / 2); + index = (index - 1) / 2; + } + } + + // arr[index]位置的数,能否往下移动 + public static void heapify(int[] arr, int index, int heapSize) { + int left = index * 2 + 1; // 左孩子的下标 + while (left < heapSize) { // 下方还有孩子的时候 + // 两个孩子中,谁的值大,把下标给largest + // 1)只有左孩子,left -> largest + // 2) 同时有左孩子和右孩子,右孩子的值<= 左孩子的值,left -> largest + // 3) 同时有左孩子和右孩子并且右孩子的值> 左孩子的值, right -> largest + int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left; + // 父和较大的孩子之间,谁的值大,把下标给largest + largest = arr[largest] > arr[index] ? largest : index; + if (largest == index) { + break; + } + swap(arr, largest, index); + index = largest; + left = index * 2 + 1; + } + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + + // 默认小根堆 + PriorityQueue heap = new PriorityQueue<>(); + heap.add(6); + heap.add(8); + heap.add(0); + heap.add(2); + heap.add(9); + heap.add(1); + + while (!heap.isEmpty()) { + System.out.println(heap.poll()); + } + + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + heapSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + heapSort(arr); + printArray(arr); + } + +} diff --git a/体系学习班/class06/Code04_SortArrayDistanceLessK.java b/体系学习班/class06/Code04_SortArrayDistanceLessK.java new file mode 100644 index 0000000..0d1b1ea --- /dev/null +++ b/体系学习班/class06/Code04_SortArrayDistanceLessK.java @@ -0,0 +1,127 @@ +package class06; + +import java.util.Arrays; +import java.util.PriorityQueue; + +public class Code04_SortArrayDistanceLessK { + + public static void sortedArrDistanceLessK(int[] arr, int k) { + if (k == 0) { + return; + } + // 默认小根堆 + PriorityQueue heap = new PriorityQueue<>(); + int index = 0; + // 0...K-1 + for (; index <= Math.min(arr.length - 1, k - 1); index++) { + heap.add(arr[index]); + } + int i = 0; + for (; index < arr.length; i++, index++) { + heap.add(arr[index]); + arr[i] = heap.poll(); + } + while (!heap.isEmpty()) { + arr[i++] = heap.poll(); + } + } + + // for test + public static void comparator(int[] arr, int k) { + Arrays.sort(arr); + } + + // for test + public static int[] randomArrayNoMoveMoreK(int maxSize, int maxValue, int K) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + // 先排个序 + Arrays.sort(arr); + // 然后开始随意交换,但是保证每个数距离不超过K + // swap[i] == true, 表示i位置已经参与过交换 + // swap[i] == false, 表示i位置没有参与过交换 + boolean[] isSwap = new boolean[arr.length]; + for (int i = 0; i < arr.length; i++) { + int j = Math.min(i + (int) (Math.random() * (K + 1)), arr.length - 1); + if (!isSwap[i] && !isSwap[j]) { + isSwap[i] = true; + isSwap[j] = true; + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + System.out.println("test begin"); + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int k = (int) (Math.random() * maxSize) + 1; + int[] arr = randomArrayNoMoveMoreK(maxSize, maxValue, k); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + sortedArrDistanceLessK(arr1, k); + comparator(arr2, k); + if (!isEqual(arr1, arr2)) { + succeed = false; + System.out.println("K : " + k); + printArray(arr); + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} \ No newline at end of file diff --git a/体系学习班/class07/Code01_CoverMax.java b/体系学习班/class07/Code01_CoverMax.java new file mode 100644 index 0000000..6260f97 --- /dev/null +++ b/体系学习班/class07/Code01_CoverMax.java @@ -0,0 +1,155 @@ +package class07; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.PriorityQueue; + +public class Code01_CoverMax { + + public static int maxCover1(int[][] lines) { + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (int i = 0; i < lines.length; i++) { + min = Math.min(min, lines[i][0]); + max = Math.max(max, lines[i][1]); + } + int cover = 0; + for (double p = min + 0.5; p < max; p += 1) { + int cur = 0; + for (int i = 0; i < lines.length; i++) { + if (lines[i][0] < p && lines[i][1] > p) { + cur++; + } + } + cover = Math.max(cover, cur); + } + return cover; + } + + public static int maxCover2(int[][] m) { + Line[] lines = new Line[m.length]; + for (int i = 0; i < m.length; i++) { + lines[i] = new Line(m[i][0], m[i][1]); + } + Arrays.sort(lines, new StartComparator()); + // 小根堆,每一条线段的结尾数值,使用默认的 + PriorityQueue heap = new PriorityQueue<>(); + int max = 0; + for (int i = 0; i < lines.length; i++) { + // lines[i] -> cur 在黑盒中,把<=cur.start 东西都弹出 + while (!heap.isEmpty() && heap.peek() <= lines[i].start) { + heap.poll(); + } + heap.add(lines[i].end); + max = Math.max(max, heap.size()); + } + return max; + } + + public static class Line { + public int start; + public int end; + + public Line(int s, int e) { + start = s; + end = e; + } + } + + public static class EndComparator implements Comparator { + + @Override + public int compare(Line o1, Line o2) { + return o1.end - o2.end; + } + + } + + // 和maxCover2过程是一样的 + // 只是代码更短 + // 不使用类定义的写法 + public static int maxCover3(int[][] m) { + // m是二维数组,可以认为m内部是一个一个的一维数组 + // 每一个一维数组就是一个对象,也就是线段 + // 如下的code,就是根据每一个线段的开始位置排序 + // 比如, m = { {5,7}, {1,4}, {2,6} } 跑完如下的code之后变成:{ {1,4}, {2,6}, {5,7} } + Arrays.sort(m, (a, b) -> (a[0] - b[0])); + // 准备好小根堆,和课堂的说法一样 + PriorityQueue heap = new PriorityQueue<>(); + int max = 0; + for (int[] line : m) { + while (!heap.isEmpty() && heap.peek() <= line[0]) { + heap.poll(); + } + heap.add(line[1]); + max = Math.max(max, heap.size()); + } + return max; + } + + // for test + public static int[][] generateLines(int N, int L, int R) { + int size = (int) (Math.random() * N) + 1; + int[][] ans = new int[size][2]; + for (int i = 0; i < size; i++) { + int a = L + (int) (Math.random() * (R - L + 1)); + int b = L + (int) (Math.random() * (R - L + 1)); + if (a == b) { + b = a + 1; + } + ans[i][0] = Math.min(a, b); + ans[i][1] = Math.max(a, b); + } + return ans; + } + + public static class StartComparator implements Comparator { + + @Override + public int compare(Line o1, Line o2) { + return o1.start - o2.start; + } + + } + + public static void main(String[] args) { + + Line l1 = new Line(4, 9); + Line l2 = new Line(1, 4); + Line l3 = new Line(7, 15); + Line l4 = new Line(2, 4); + Line l5 = new Line(4, 6); + Line l6 = new Line(3, 7); + + // 底层堆结构,heap + PriorityQueue heap = new PriorityQueue<>(new StartComparator()); + heap.add(l1); + heap.add(l2); + heap.add(l3); + heap.add(l4); + heap.add(l5); + heap.add(l6); + + while (!heap.isEmpty()) { + Line cur = heap.poll(); + System.out.println(cur.start + "," + cur.end); + } + + System.out.println("test begin"); + int N = 100; + int L = 0; + int R = 200; + int testTimes = 200000; + for (int i = 0; i < testTimes; i++) { + int[][] lines = generateLines(N, L, R); + int ans1 = maxCover1(lines); + int ans2 = maxCover2(lines); + int ans3 = maxCover3(lines); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + } + } + System.out.println("test end"); + } + +} diff --git a/体系学习班/class07/Code02_EveryStepShowBoss.java b/体系学习班/class07/Code02_EveryStepShowBoss.java new file mode 100644 index 0000000..a4c123b --- /dev/null +++ b/体系学习班/class07/Code02_EveryStepShowBoss.java @@ -0,0 +1,303 @@ +package class07; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +public class Code02_EveryStepShowBoss { + + public static class Customer { + public int id; + public int buy; + public int enterTime; + + public Customer(int v, int b, int o) { + id = v; + buy = b; + enterTime = 0; + } + } + + public static class CandidateComparator implements Comparator { + + @Override + public int compare(Customer o1, Customer o2) { + return o1.buy != o2.buy ? (o2.buy - o1.buy) : (o1.enterTime - o2.enterTime); + } + + } + + public static class DaddyComparator implements Comparator { + + @Override + public int compare(Customer o1, Customer o2) { + return o1.buy != o2.buy ? (o1.buy - o2.buy) : (o1.enterTime - o2.enterTime); + } + + } + + public static class WhosYourDaddy { + private HashMap customers; + private HeapGreater candHeap; + private HeapGreater daddyHeap; + private final int daddyLimit; + + public WhosYourDaddy(int limit) { + customers = new HashMap(); + candHeap = new HeapGreater<>(new CandidateComparator()); + daddyHeap = new HeapGreater<>(new DaddyComparator()); + daddyLimit = limit; + } + + // 当前处理i号事件,arr[i] -> id, buyOrRefund + public void operate(int time, int id, boolean buyOrRefund) { + if (!buyOrRefund && !customers.containsKey(id)) { + return; + } + if (!customers.containsKey(id)) { + customers.put(id, new Customer(id, 0, 0)); + } + Customer c = customers.get(id); + if (buyOrRefund) { + c.buy++; + } else { + c.buy--; + } + if (c.buy == 0) { + customers.remove(id); + } + if (!candHeap.contains(c) && !daddyHeap.contains(c)) { + if (daddyHeap.size() < daddyLimit) { + c.enterTime = time; + daddyHeap.push(c); + } else { + c.enterTime = time; + candHeap.push(c); + } + } else if (candHeap.contains(c)) { + if (c.buy == 0) { + candHeap.remove(c); + } else { + candHeap.resign(c); + } + } else { + if (c.buy == 0) { + daddyHeap.remove(c); + } else { + daddyHeap.resign(c); + } + } + daddyMove(time); + } + + public List getDaddies() { + List customers = daddyHeap.getAllElements(); + List ans = new ArrayList<>(); + for (Customer c : customers) { + ans.add(c.id); + } + return ans; + } + + private void daddyMove(int time) { + if (candHeap.isEmpty()) { + return; + } + if (daddyHeap.size() < daddyLimit) { + Customer p = candHeap.pop(); + p.enterTime = time; + daddyHeap.push(p); + } else { + if (candHeap.peek().buy > daddyHeap.peek().buy) { + Customer oldDaddy = daddyHeap.pop(); + Customer newDaddy = candHeap.pop(); + oldDaddy.enterTime = time; + newDaddy.enterTime = time; + daddyHeap.push(newDaddy); + candHeap.push(oldDaddy); + } + } + } + + } + + public static List> topK(int[] arr, boolean[] op, int k) { + List> ans = new ArrayList<>(); + WhosYourDaddy whoDaddies = new WhosYourDaddy(k); + for (int i = 0; i < arr.length; i++) { + whoDaddies.operate(i, arr[i], op[i]); + ans.add(whoDaddies.getDaddies()); + } + return ans; + } + + // 干完所有的事,模拟,不优化 + public static List> compare(int[] arr, boolean[] op, int k) { + HashMap map = new HashMap<>(); + ArrayList cands = new ArrayList<>(); + ArrayList daddy = new ArrayList<>(); + List> ans = new ArrayList<>(); + for (int i = 0; i < arr.length; i++) { + int id = arr[i]; + boolean buyOrRefund = op[i]; + if (!buyOrRefund && !map.containsKey(id)) { + ans.add(getCurAns(daddy)); + continue; + } + // 没有发生:用户购买数为0并且又退货了 + // 用户之前购买数是0,此时买货事件 + // 用户之前购买数>0, 此时买货 + // 用户之前购买数>0, 此时退货 + if (!map.containsKey(id)) { + map.put(id, new Customer(id, 0, 0)); + } + // 买、卖 + Customer c = map.get(id); + if (buyOrRefund) { + c.buy++; + } else { + c.buy--; + } + if (c.buy == 0) { + map.remove(id); + } + // c + // 下面做 + if (!cands.contains(c) && !daddy.contains(c)) { + if (daddy.size() < k) { + c.enterTime = i; + daddy.add(c); + } else { + c.enterTime = i; + cands.add(c); + } + } + cleanZeroBuy(cands); + cleanZeroBuy(daddy); + cands.sort(new CandidateComparator()); + daddy.sort(new DaddyComparator()); + move(cands, daddy, k, i); + ans.add(getCurAns(daddy)); + } + return ans; + } + + public static void move(ArrayList cands, ArrayList daddy, int k, int time) { + if (cands.isEmpty()) { + return; + } + // 候选区不为空 + if (daddy.size() < k) { + Customer c = cands.get(0); + c.enterTime = time; + daddy.add(c); + cands.remove(0); + } else { // 等奖区满了,候选区有东西 + if (cands.get(0).buy > daddy.get(0).buy) { + Customer oldDaddy = daddy.get(0); + daddy.remove(0); + Customer newDaddy = cands.get(0); + cands.remove(0); + newDaddy.enterTime = time; + oldDaddy.enterTime = time; + daddy.add(newDaddy); + cands.add(oldDaddy); + } + } + } + + public static void cleanZeroBuy(ArrayList arr) { + List noZero = new ArrayList(); + for (Customer c : arr) { + if (c.buy != 0) { + noZero.add(c); + } + } + arr.clear(); + for (Customer c : noZero) { + arr.add(c); + } + } + + public static List getCurAns(ArrayList daddy) { + List ans = new ArrayList<>(); + for (Customer c : daddy) { + ans.add(c.id); + } + return ans; + } + + // 为了测试 + public static class Data { + public int[] arr; + public boolean[] op; + + public Data(int[] a, boolean[] o) { + arr = a; + op = o; + } + } + + // 为了测试 + public static Data randomData(int maxValue, int maxLen) { + int len = (int) (Math.random() * maxLen) + 1; + int[] arr = new int[len]; + boolean[] op = new boolean[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * maxValue); + op[i] = Math.random() < 0.5 ? true : false; + } + return new Data(arr, op); + } + + // 为了测试 + public static boolean sameAnswer(List> ans1, List> ans2) { + if (ans1.size() != ans2.size()) { + return false; + } + for (int i = 0; i < ans1.size(); i++) { + List cur1 = ans1.get(i); + List cur2 = ans2.get(i); + if (cur1.size() != cur2.size()) { + return false; + } + cur1.sort((a, b) -> a - b); + cur2.sort((a, b) -> a - b); + for (int j = 0; j < cur1.size(); j++) { + if (!cur1.get(j).equals(cur2.get(j))) { + return false; + } + } + } + return true; + } + + public static void main(String[] args) { + int maxValue = 10; + int maxLen = 100; + int maxK = 6; + int testTimes = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + Data testData = randomData(maxValue, maxLen); + int k = (int) (Math.random() * maxK) + 1; + int[] arr = testData.arr; + boolean[] op = testData.op; + List> ans1 = topK(arr, op, k); + List> ans2 = compare(arr, op, k); + if (!sameAnswer(ans1, ans2)) { + for (int j = 0; j < arr.length; j++) { + System.out.println(arr[j] + " , " + op[j]); + } + System.out.println(k); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class07/HeapGreater.java b/体系学习班/class07/HeapGreater.java new file mode 100644 index 0000000..e58da47 --- /dev/null +++ b/体系学习班/class07/HeapGreater.java @@ -0,0 +1,112 @@ +package class07; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +/* + * T一定要是非基础类型,有基础类型需求包一层 + */ +public class HeapGreater { + + private ArrayList heap; + private HashMap indexMap; + private int heapSize; + private Comparator comp; + + public HeapGreater(Comparator c) { + heap = new ArrayList<>(); + indexMap = new HashMap<>(); + heapSize = 0; + comp = c; + } + + public boolean isEmpty() { + return heapSize == 0; + } + + public int size() { + return heapSize; + } + + public boolean contains(T obj) { + return indexMap.containsKey(obj); + } + + public T peek() { + return heap.get(0); + } + + public void push(T obj) { + heap.add(obj); + indexMap.put(obj, heapSize); + heapInsert(heapSize++); + } + + public T pop() { + T ans = heap.get(0); + swap(0, heapSize - 1); + indexMap.remove(ans); + heap.remove(--heapSize); + heapify(0); + return ans; + } + + public void remove(T obj) { + T replace = heap.get(heapSize - 1); + int index = indexMap.get(obj); + indexMap.remove(obj); + heap.remove(--heapSize); + if (obj != replace) { + heap.set(index, replace); + indexMap.put(replace, index); + resign(replace); + } + } + + public void resign(T obj) { + heapInsert(indexMap.get(obj)); + heapify(indexMap.get(obj)); + } + + // 请返回堆上的所有元素 + public List getAllElements() { + List ans = new ArrayList<>(); + for (T c : heap) { + ans.add(c); + } + return ans; + } + + private void heapInsert(int index) { + while (comp.compare(heap.get(index), heap.get((index - 1) / 2)) < 0) { + swap(index, (index - 1) / 2); + index = (index - 1) / 2; + } + } + + private void heapify(int index) { + int left = index * 2 + 1; + while (left < heapSize) { + int best = left + 1 < heapSize && comp.compare(heap.get(left + 1), heap.get(left)) < 0 ? (left + 1) : left; + best = comp.compare(heap.get(best), heap.get(index)) < 0 ? best : index; + if (best == index) { + break; + } + swap(best, index); + index = best; + left = index * 2 + 1; + } + } + + private void swap(int i, int j) { + T o1 = heap.get(i); + T o2 = heap.get(j); + heap.set(i, o2); + heap.set(j, o1); + indexMap.put(o2, i); + indexMap.put(o1, j); + } + +} diff --git a/体系学习班/class07/Inner.java b/体系学习班/class07/Inner.java new file mode 100644 index 0000000..d3d5326 --- /dev/null +++ b/体系学习班/class07/Inner.java @@ -0,0 +1,9 @@ +package class07; + +public class Inner { + public T value; + + public Inner(T v) { + value = v; + } +} diff --git a/体系学习班/class08/Code01_TrieTree.java b/体系学习班/class08/Code01_TrieTree.java new file mode 100644 index 0000000..f7f9cf1 --- /dev/null +++ b/体系学习班/class08/Code01_TrieTree.java @@ -0,0 +1,299 @@ +package class08; + +import java.util.HashMap; + +// 该程序的对数器跑不过,你能发现bug在哪吗? +public class Code01_TrieTree { + + // 前缀树节点类型 + public static class Node1 { + public int pass; + public int end; + public Node1[] nexts; + + public Node1() { + pass = 0; + end = 0; + nexts = new Node1[26]; + } + } + + public static class Trie1 { + private Node1 root; + + public Trie1() { + root = new Node1(); + } + + public void insert(String word) { + if (word == null) { + return; + } + char[] chs = word.toCharArray(); + Node1 node = root; + node.pass++; + int index = 0; + for (int i = 0; i < chs.length; i++) { // 从左往右遍历字符 + index = chs[i] - 'a'; // 由字符,对应成走向哪条路 + if (node.nexts[index] == null) { + node.nexts[index] = new Node1(); + } + node = node.nexts[index]; + node.pass++; + } + node.end++; + } + + public void delete(String word) { + if (search(word) != 0) { + char[] chs = word.toCharArray(); + Node1 node = root; + node.pass--; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (--node.nexts[index].pass == 0) { + node.nexts[index] = null; + return; + } + node = node.nexts[index]; + } + node.end--; + } + } + + // word这个单词之前加入过几次 + public int search(String word) { + if (word == null) { + return 0; + } + char[] chs = word.toCharArray(); + Node1 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + return 0; + } + node = node.nexts[index]; + } + return node.end; + } + + // 所有加入的字符串中,有几个是以pre这个字符串作为前缀的 + public int prefixNumber(String pre) { + if (pre == null) { + return 0; + } + char[] chs = pre.toCharArray(); + Node1 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + return 0; + } + node = node.nexts[index]; + } + return node.pass; + } + } + + public static class Node2 { + public int pass; + public int end; + public HashMap nexts; + + public Node2() { + pass = 0; + end = 0; + nexts = new HashMap<>(); + } + } + + public static class Trie2 { + private Node2 root; + + public Trie2() { + root = new Node2(); + } + + public void insert(String word) { + if (word == null) { + return; + } + char[] chs = word.toCharArray(); + Node2 node = root; + node.pass++; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (!node.nexts.containsKey(index)) { + node.nexts.put(index, new Node2()); + } + node = node.nexts.get(index); + node.pass++; + } + node.end++; + } + + public void delete(String word) { + if (search(word) != 0) { + char[] chs = word.toCharArray(); + Node2 node = root; + node.pass--; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (--node.nexts.get(index).pass == 0) { + node.nexts.remove(index); + return; + } + node = node.nexts.get(index); + } + node.end--; + } + } + + // word这个单词之前加入过几次 + public int search(String word) { + if (word == null) { + return 0; + } + char[] chs = word.toCharArray(); + Node2 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (!node.nexts.containsKey(index)) { + return 0; + } + node = node.nexts.get(index); + } + return node.end; + } + + // 所有加入的字符串中,有几个是以pre这个字符串作为前缀的 + public int prefixNumber(String pre) { + if (pre == null) { + return 0; + } + char[] chs = pre.toCharArray(); + Node2 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (!node.nexts.containsKey(index)) { + return 0; + } + node = node.nexts.get(index); + } + return node.pass; + } + } + + public static class Right { + + private HashMap box; + + public Right() { + box = new HashMap<>(); + } + + public void insert(String word) { + if (!box.containsKey(word)) { + box.put(word, 1); + } else { + box.put(word, box.get(word) + 1); + } + } + + public void delete(String word) { + if (box.containsKey(word)) { + if (box.get(word) == 1) { + box.remove(word); + } else { + box.put(word, box.get(word) - 1); + } + } + } + + public int search(String word) { + if (!box.containsKey(word)) { + return 0; + } else { + return box.get(word); + } + } + + public int prefixNumber(String pre) { + int count = 0; + for (String cur : box.keySet()) { + if (cur.startsWith(pre)) { + count++; + } + } + return count; + } + } + + // for test + public static String generateRandomString(int strLen) { + char[] ans = new char[(int) (Math.random() * strLen) + 1]; + for (int i = 0; i < ans.length; i++) { + int value = (int) (Math.random() * 6); + ans[i] = (char) (97 + value); + } + return String.valueOf(ans); + } + + // for test + public static String[] generateRandomStringArray(int arrLen, int strLen) { + String[] ans = new String[(int) (Math.random() * arrLen) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = generateRandomString(strLen); + } + return ans; + } + + public static void main(String[] args) { + int arrLen = 100; + int strLen = 20; + int testTimes = 100000; + for (int i = 0; i < testTimes; i++) { + String[] arr = generateRandomStringArray(arrLen, strLen); + Trie1 trie1 = new Trie1(); + Trie2 trie2 = new Trie2(); + Right right = new Right(); + for (int j = 0; j < arr.length; j++) { + double decide = Math.random(); + if (decide < 0.25) { + trie1.insert(arr[j]); + trie2.insert(arr[j]); + right.insert(arr[j]); + } else if (decide < 0.5) { + trie1.delete(arr[j]); + trie2.delete(arr[j]); + right.delete(arr[j]); + } else if (decide < 0.75) { + int ans1 = trie1.search(arr[j]); + int ans2 = trie2.search(arr[j]); + int ans3 = right.search(arr[j]); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("Oops!"); + } + } else { + int ans1 = trie1.prefixNumber(arr[j]); + int ans2 = trie2.prefixNumber(arr[j]); + int ans3 = right.prefixNumber(arr[j]); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("Oops!"); + } + } + } + } + System.out.println("finish!"); + + } + +} diff --git a/体系学习班/class08/Code02_TrieTree.java b/体系学习班/class08/Code02_TrieTree.java new file mode 100644 index 0000000..44d35cf --- /dev/null +++ b/体系学习班/class08/Code02_TrieTree.java @@ -0,0 +1,306 @@ +package class08; + +import java.util.HashMap; + +// 该程序完全正确 +public class Code02_TrieTree { + + public static class Node1 { + public int pass; + public int end; + public Node1[] nexts; + + // char tmp = 'b' (tmp - 'a') + public Node1() { + pass = 0; + end = 0; + // 0 a + // 1 b + // 2 c + // .. .. + // 25 z + // nexts[i] == null i方向的路不存在 + // nexts[i] != null i方向的路存在 + nexts = new Node1[26]; + } + } + + public static class Trie1 { + private Node1 root; + + public Trie1() { + root = new Node1(); + } + + public void insert(String word) { + if (word == null) { + return; + } + char[] str = word.toCharArray(); + Node1 node = root; + node.pass++; + int path = 0; + for (int i = 0; i < str.length; i++) { // 从左往右遍历字符 + path = str[i] - 'a'; // 由字符,对应成走向哪条路 + if (node.nexts[path] == null) { + node.nexts[path] = new Node1(); + } + node = node.nexts[path]; + node.pass++; + } + node.end++; + } + + public void delete(String word) { + if (search(word) != 0) { + char[] chs = word.toCharArray(); + Node1 node = root; + node.pass--; + int path = 0; + for (int i = 0; i < chs.length; i++) { + path = chs[i] - 'a'; + if (--node.nexts[path].pass == 0) { + node.nexts[path] = null; + return; + } + node = node.nexts[path]; + } + node.end--; + } + } + + // word这个单词之前加入过几次 + public int search(String word) { + if (word == null) { + return 0; + } + char[] chs = word.toCharArray(); + Node1 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + return 0; + } + node = node.nexts[index]; + } + return node.end; + } + + // 所有加入的字符串中,有几个是以pre这个字符串作为前缀的 + public int prefixNumber(String pre) { + if (pre == null) { + return 0; + } + char[] chs = pre.toCharArray(); + Node1 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + return 0; + } + node = node.nexts[index]; + } + return node.pass; + } + } + + public static class Node2 { + public int pass; + public int end; + public HashMap nexts; + + public Node2() { + pass = 0; + end = 0; + nexts = new HashMap<>(); + } + } + + public static class Trie2 { + private Node2 root; + + public Trie2() { + root = new Node2(); + } + + public void insert(String word) { + if (word == null) { + return; + } + char[] chs = word.toCharArray(); + Node2 node = root; + node.pass++; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (!node.nexts.containsKey(index)) { + node.nexts.put(index, new Node2()); + } + node = node.nexts.get(index); + node.pass++; + } + node.end++; + } + + public void delete(String word) { + if (search(word) != 0) { + char[] chs = word.toCharArray(); + Node2 node = root; + node.pass--; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (--node.nexts.get(index).pass == 0) { + node.nexts.remove(index); + return; + } + node = node.nexts.get(index); + } + node.end--; + } + } + + // word这个单词之前加入过几次 + public int search(String word) { + if (word == null) { + return 0; + } + char[] chs = word.toCharArray(); + Node2 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (!node.nexts.containsKey(index)) { + return 0; + } + node = node.nexts.get(index); + } + return node.end; + } + + // 所有加入的字符串中,有几个是以pre这个字符串作为前缀的 + public int prefixNumber(String pre) { + if (pre == null) { + return 0; + } + char[] chs = pre.toCharArray(); + Node2 node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = (int) chs[i]; + if (!node.nexts.containsKey(index)) { + return 0; + } + node = node.nexts.get(index); + } + return node.pass; + } + } + + public static class Right { + + private HashMap box; + + public Right() { + box = new HashMap<>(); + } + + public void insert(String word) { + if (!box.containsKey(word)) { + box.put(word, 1); + } else { + box.put(word, box.get(word) + 1); + } + } + + public void delete(String word) { + if (box.containsKey(word)) { + if (box.get(word) == 1) { + box.remove(word); + } else { + box.put(word, box.get(word) - 1); + } + } + } + + public int search(String word) { + if (!box.containsKey(word)) { + return 0; + } else { + return box.get(word); + } + } + + public int prefixNumber(String pre) { + int count = 0; + for (String cur : box.keySet()) { + if (cur.startsWith(pre)) { + count += box.get(cur); + } + } + return count; + } + } + + // for test + public static String generateRandomString(int strLen) { + char[] ans = new char[(int) (Math.random() * strLen) + 1]; + for (int i = 0; i < ans.length; i++) { + int value = (int) (Math.random() * 6); + ans[i] = (char) (97 + value); + } + return String.valueOf(ans); + } + + // for test + public static String[] generateRandomStringArray(int arrLen, int strLen) { + String[] ans = new String[(int) (Math.random() * arrLen) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = generateRandomString(strLen); + } + return ans; + } + + public static void main(String[] args) { + int arrLen = 100; + int strLen = 20; + int testTimes = 100000; + for (int i = 0; i < testTimes; i++) { + String[] arr = generateRandomStringArray(arrLen, strLen); + Trie1 trie1 = new Trie1(); + Trie2 trie2 = new Trie2(); + Right right = new Right(); + for (int j = 0; j < arr.length; j++) { + double decide = Math.random(); + if (decide < 0.25) { + trie1.insert(arr[j]); + trie2.insert(arr[j]); + right.insert(arr[j]); + } else if (decide < 0.5) { + trie1.delete(arr[j]); + trie2.delete(arr[j]); + right.delete(arr[j]); + } else if (decide < 0.75) { + int ans1 = trie1.search(arr[j]); + int ans2 = trie2.search(arr[j]); + int ans3 = right.search(arr[j]); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("Oops!"); + } + } else { + int ans1 = trie1.prefixNumber(arr[j]); + int ans2 = trie2.prefixNumber(arr[j]); + int ans3 = right.prefixNumber(arr[j]); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("Oops!"); + } + } + } + } + System.out.println("finish!"); + + } + +} diff --git a/体系学习班/class08/Code03_CountSort.java b/体系学习班/class08/Code03_CountSort.java new file mode 100644 index 0000000..d7864ec --- /dev/null +++ b/体系学习班/class08/Code03_CountSort.java @@ -0,0 +1,111 @@ +package class08; + +import java.util.Arrays; + +public class Code03_CountSort { + + // only for 0~200 value + public static void countSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int max = Integer.MIN_VALUE; + for (int i = 0; i < arr.length; i++) { + max = Math.max(max, arr[i]); + } + int[] bucket = new int[max + 1]; + for (int i = 0; i < arr.length; i++) { + bucket[arr[i]]++; + } + int i = 0; + for (int j = 0; j < bucket.length; j++) { + while (bucket[j]-- > 0) { + arr[i++] = j; + } + } + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 150; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + countSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + succeed = false; + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + countSort(arr); + printArray(arr); + + } + +} diff --git a/体系学习班/class08/Code04_RadixSort.java b/体系学习班/class08/Code04_RadixSort.java new file mode 100644 index 0000000..af35db3 --- /dev/null +++ b/体系学习班/class08/Code04_RadixSort.java @@ -0,0 +1,148 @@ +package class08; + +import java.util.Arrays; + +public class Code04_RadixSort { + + // only for no-negative value + public static void radixSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + radixSort(arr, 0, arr.length - 1, maxbits(arr)); + } + + public static int maxbits(int[] arr) { + int max = Integer.MIN_VALUE; + for (int i = 0; i < arr.length; i++) { + max = Math.max(max, arr[i]); + } + int res = 0; + while (max != 0) { + res++; + max /= 10; + } + return res; + } + + // arr[L..R]排序 , 最大值的十进制位数digit + public static void radixSort(int[] arr, int L, int R, int digit) { + final int radix = 10; + int i = 0, j = 0; + // 有多少个数准备多少个辅助空间 + int[] help = new int[R - L + 1]; + for (int d = 1; d <= digit; d++) { // 有多少位就进出几次 + // 10个空间 + // count[0] 当前位(d位)是0的数字有多少个 + // count[1] 当前位(d位)是(0和1)的数字有多少个 + // count[2] 当前位(d位)是(0、1和2)的数字有多少个 + // count[i] 当前位(d位)是(0~i)的数字有多少个 + int[] count = new int[radix]; // count[0..9] + for (i = L; i <= R; i++) { + // 103 1 3 + // 209 1 9 + j = getDigit(arr[i], d); + count[j]++; + } + for (i = 1; i < radix; i++) { + count[i] = count[i] + count[i - 1]; + } + for (i = R; i >= L; i--) { + j = getDigit(arr[i], d); + help[count[j] - 1] = arr[i]; + count[j]--; + } + for (i = L, j = 0; i <= R; i++, j++) { + arr[i] = help[j]; + } + } + } + + public static int getDigit(int x, int d) { + return ((x / ((int) Math.pow(10, d - 1))) % 10); + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100000; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + radixSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + succeed = false; + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + radixSort(arr); + printArray(arr); + + } + +} diff --git a/体系学习班/class09/Code01_LinkedListMid.java b/体系学习班/class09/Code01_LinkedListMid.java new file mode 100644 index 0000000..3283443 --- /dev/null +++ b/体系学习班/class09/Code01_LinkedListMid.java @@ -0,0 +1,162 @@ +package class09; + +import java.util.ArrayList; + +public class Code01_LinkedListMid { + + public static class Node { + public int value; + public Node next; + + public Node(int v) { + value = v; + } + } + + // head 头 + public static Node midOrUpMidNode(Node head) { + if (head == null || head.next == null || head.next.next == null) { + return head; + } + // 链表有3个点或以上 + Node slow = head.next; + Node fast = head.next.next; + while (fast.next != null && fast.next.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + public static Node midOrDownMidNode(Node head) { + if (head == null || head.next == null) { + return head; + } + Node slow = head.next; + Node fast = head.next; + while (fast.next != null && fast.next.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + public static Node midOrUpMidPreNode(Node head) { + if (head == null || head.next == null || head.next.next == null) { + return null; + } + Node slow = head; + Node fast = head.next.next; + while (fast.next != null && fast.next.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + public static Node midOrDownMidPreNode(Node head) { + if (head == null || head.next == null) { + return null; + } + if (head.next.next == null) { + return head; + } + Node slow = head; + Node fast = head.next; + while (fast.next != null && fast.next.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + public static Node right1(Node head) { + if (head == null) { + return null; + } + Node cur = head; + ArrayList arr = new ArrayList<>(); + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + return arr.get((arr.size() - 1) / 2); + } + + public static Node right2(Node head) { + if (head == null) { + return null; + } + Node cur = head; + ArrayList arr = new ArrayList<>(); + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + return arr.get(arr.size() / 2); + } + + public static Node right3(Node head) { + if (head == null || head.next == null || head.next.next == null) { + return null; + } + Node cur = head; + ArrayList arr = new ArrayList<>(); + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + return arr.get((arr.size() - 3) / 2); + } + + public static Node right4(Node head) { + if (head == null || head.next == null) { + return null; + } + Node cur = head; + ArrayList arr = new ArrayList<>(); + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + return arr.get((arr.size() - 2) / 2); + } + + public static void main(String[] args) { + Node test = null; + test = new Node(0); + test.next = new Node(1); + test.next.next = new Node(2); + test.next.next.next = new Node(3); + test.next.next.next.next = new Node(4); + test.next.next.next.next.next = new Node(5); + test.next.next.next.next.next.next = new Node(6); + test.next.next.next.next.next.next.next = new Node(7); + test.next.next.next.next.next.next.next.next = new Node(8); + + Node ans1 = null; + Node ans2 = null; + + ans1 = midOrUpMidNode(test); + ans2 = right1(test); + System.out.println(ans1 != null ? ans1.value : "无"); + System.out.println(ans2 != null ? ans2.value : "无"); + + ans1 = midOrDownMidNode(test); + ans2 = right2(test); + System.out.println(ans1 != null ? ans1.value : "无"); + System.out.println(ans2 != null ? ans2.value : "无"); + + ans1 = midOrUpMidPreNode(test); + ans2 = right3(test); + System.out.println(ans1 != null ? ans1.value : "无"); + System.out.println(ans2 != null ? ans2.value : "无"); + + ans1 = midOrDownMidPreNode(test); + ans2 = right4(test); + System.out.println(ans1 != null ? ans1.value : "无"); + System.out.println(ans2 != null ? ans2.value : "无"); + + } + +} diff --git a/体系学习班/class09/Code02_IsPalindromeList.java b/体系学习班/class09/Code02_IsPalindromeList.java new file mode 100644 index 0000000..63d6eed --- /dev/null +++ b/体系学习班/class09/Code02_IsPalindromeList.java @@ -0,0 +1,204 @@ +package class09; + +import java.util.Stack; + +public class Code02_IsPalindromeList { + + public static class Node { + public int value; + public Node next; + + public Node(int data) { + this.value = data; + } + } + + // need n extra space + public static boolean isPalindrome1(Node head) { + Stack stack = new Stack(); + Node cur = head; + while (cur != null) { + stack.push(cur); + cur = cur.next; + } + while (head != null) { + if (head.value != stack.pop().value) { + return false; + } + head = head.next; + } + return true; + } + + // need n/2 extra space + public static boolean isPalindrome2(Node head) { + if (head == null || head.next == null) { + return true; + } + Node right = head.next; + Node cur = head; + while (cur.next != null && cur.next.next != null) { + right = right.next; + cur = cur.next.next; + } + Stack stack = new Stack(); + while (right != null) { + stack.push(right); + right = right.next; + } + while (!stack.isEmpty()) { + if (head.value != stack.pop().value) { + return false; + } + head = head.next; + } + return true; + } + + // need O(1) extra space + public static boolean isPalindrome3(Node head) { + if (head == null || head.next == null) { + return true; + } + Node n1 = head; + Node n2 = head; + while (n2.next != null && n2.next.next != null) { // find mid node + n1 = n1.next; // n1 -> mid + n2 = n2.next.next; // n2 -> end + } + // n1 中点 + + + n2 = n1.next; // n2 -> right part first node + n1.next = null; // mid.next -> null + Node n3 = null; + while (n2 != null) { // right part convert + n3 = n2.next; // n3 -> save next node + n2.next = n1; // next of right node convert + n1 = n2; // n1 move + n2 = n3; // n2 move + } + n3 = n1; // n3 -> save last node + n2 = head;// n2 -> left first node + boolean res = true; + while (n1 != null && n2 != null) { // check palindrome + if (n1.value != n2.value) { + res = false; + break; + } + n1 = n1.next; // left to mid + n2 = n2.next; // right to mid + } + n1 = n3.next; + n3.next = null; + while (n1 != null) { // recover list + n2 = n1.next; + n1.next = n3; + n3 = n1; + n1 = n2; + } + return res; + } + + public static void printLinkedList(Node node) { + System.out.print("Linked List: "); + while (node != null) { + System.out.print(node.value + " "); + node = node.next; + } + System.out.println(); + } + + public static void main(String[] args) { + + Node head = null; + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + head.next = new Node(2); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + head.next = new Node(1); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + head.next = new Node(2); + head.next.next = new Node(3); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + head.next = new Node(2); + head.next.next = new Node(1); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + head.next = new Node(2); + head.next.next = new Node(3); + head.next.next.next = new Node(1); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + head.next = new Node(2); + head.next.next = new Node(2); + head.next.next.next = new Node(1); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + head = new Node(1); + head.next = new Node(2); + head.next.next = new Node(3); + head.next.next.next = new Node(2); + head.next.next.next.next = new Node(1); + printLinkedList(head); + System.out.print(isPalindrome1(head) + " | "); + System.out.print(isPalindrome2(head) + " | "); + System.out.println(isPalindrome3(head) + " | "); + printLinkedList(head); + System.out.println("========================="); + + } + +} diff --git a/体系学习班/class09/Code03_SmallerEqualBigger.java b/体系学习班/class09/Code03_SmallerEqualBigger.java new file mode 100644 index 0000000..33203e4 --- /dev/null +++ b/体系学习班/class09/Code03_SmallerEqualBigger.java @@ -0,0 +1,138 @@ +package class09; + +public class Code03_SmallerEqualBigger { + + public static class Node { + public int value; + public Node next; + + public Node(int data) { + this.value = data; + } + } + + public static Node listPartition1(Node head, int pivot) { + if (head == null) { + return head; + } + Node cur = head; + int i = 0; + while (cur != null) { + i++; + cur = cur.next; + } + Node[] nodeArr = new Node[i]; + i = 0; + cur = head; + for (i = 0; i != nodeArr.length; i++) { + nodeArr[i] = cur; + cur = cur.next; + } + arrPartition(nodeArr, pivot); + for (i = 1; i != nodeArr.length; i++) { + nodeArr[i - 1].next = nodeArr[i]; + } + nodeArr[i - 1].next = null; + return nodeArr[0]; + } + + public static void arrPartition(Node[] nodeArr, int pivot) { + int small = -1; + int big = nodeArr.length; + int index = 0; + while (index != big) { + if (nodeArr[index].value < pivot) { + swap(nodeArr, ++small, index++); + } else if (nodeArr[index].value == pivot) { + index++; + } else { + swap(nodeArr, --big, index); + } + } + } + + public static void swap(Node[] nodeArr, int a, int b) { + Node tmp = nodeArr[a]; + nodeArr[a] = nodeArr[b]; + nodeArr[b] = tmp; + } + + public static Node listPartition2(Node head, int pivot) { + Node sH = null; // small head + Node sT = null; // small tail + Node eH = null; // equal head + Node eT = null; // equal tail + Node mH = null; // big head + Node mT = null; // big tail + Node next = null; // save next node + // every node distributed to three lists + while (head != null) { + next = head.next; + head.next = null; + if (head.value < pivot) { + if (sH == null) { + sH = head; + sT = head; + } else { + sT.next = head; + sT = head; + } + } else if (head.value == pivot) { + if (eH == null) { + eH = head; + eT = head; + } else { + eT.next = head; + eT = head; + } + } else { + if (mH == null) { + mH = head; + mT = head; + } else { + mT.next = head; + mT = head; + } + } + head = next; + } + // 小于区域的尾巴,连等于区域的头,等于区域的尾巴连大于区域的头 + if (sT != null) { // 如果有小于区域 + sT.next = eH; + eT = eT == null ? sT : eT; // 下一步,谁去连大于区域的头,谁就变成eT + } + // 下一步,一定是需要用eT 去接 大于区域的头 + // 有等于区域,eT -> 等于区域的尾结点 + // 无等于区域,eT -> 小于区域的尾结点 + // eT 尽量不为空的尾巴节点 + if (eT != null) { // 如果小于区域和等于区域,不是都没有 + eT.next = mH; + } + return sH != null ? sH : (eH != null ? eH : mH); + } + + public static void printLinkedList(Node node) { + System.out.print("Linked List: "); + while (node != null) { + System.out.print(node.value + " "); + node = node.next; + } + System.out.println(); + } + + public static void main(String[] args) { + Node head1 = new Node(7); + head1.next = new Node(9); + head1.next.next = new Node(1); + head1.next.next.next = new Node(8); + head1.next.next.next.next = new Node(5); + head1.next.next.next.next.next = new Node(2); + head1.next.next.next.next.next.next = new Node(5); + printLinkedList(head1); + // head1 = listPartition1(head1, 4); + head1 = listPartition2(head1, 5); + printLinkedList(head1); + + } + +} diff --git a/体系学习班/class09/Code04_CopyListWithRandom.java b/体系学习班/class09/Code04_CopyListWithRandom.java new file mode 100644 index 0000000..f42454f --- /dev/null +++ b/体系学习班/class09/Code04_CopyListWithRandom.java @@ -0,0 +1,79 @@ +package class09; + +import java.util.HashMap; + +// 测试链接 : https://leetcode.com/problems/copy-list-with-random-pointer/ +public class Code04_CopyListWithRandom { + + public static class Node { + int val; + Node next; + Node random; + + public Node(int val) { + this.val = val; + this.next = null; + this.random = null; + } + } + + public static Node copyRandomList1(Node head) { + // key 老节点 + // value 新节点 + HashMap map = new HashMap(); + Node cur = head; + while (cur != null) { + map.put(cur, new Node(cur.val)); + cur = cur.next; + } + cur = head; + while (cur != null) { + // cur 老 + // map.get(cur) 新 + // 新.next -> cur.next克隆节点找到 + map.get(cur).next = map.get(cur.next); + map.get(cur).random = map.get(cur.random); + cur = cur.next; + } + return map.get(head); + } + + public static Node copyRandomList2(Node head) { + if (head == null) { + return null; + } + Node cur = head; + Node next = null; + // 1 -> 2 -> 3 -> null + // 1 -> 1' -> 2 -> 2' -> 3 -> 3' + while (cur != null) { + next = cur.next; + cur.next = new Node(cur.val); + cur.next.next = next; + cur = next; + } + cur = head; + Node copy = null; + // 1 1' 2 2' 3 3' + // 依次设置 1' 2' 3' random指针 + while (cur != null) { + next = cur.next.next; + copy = cur.next; + copy.random = cur.random != null ? cur.random.next : null; + cur = next; + } + Node res = head.next; + cur = head; + // 老 新 混在一起,next方向上,random正确 + // next方向上,把新老链表分离 + while (cur != null) { + next = cur.next.next; + copy = cur.next; + cur.next = next; + copy.next = next != null ? next.next : null; + cur = next; + } + return res; + } + +} diff --git a/体系学习班/class10/Code01_FindFirstIntersectNode.java b/体系学习班/class10/Code01_FindFirstIntersectNode.java new file mode 100644 index 0000000..8cec65d --- /dev/null +++ b/体系学习班/class10/Code01_FindFirstIntersectNode.java @@ -0,0 +1,170 @@ +package class10; + +public class Code01_FindFirstIntersectNode { + + public static class Node { + public int value; + public Node next; + + public Node(int data) { + this.value = data; + } + } + + public static Node getIntersectNode(Node head1, Node head2) { + if (head1 == null || head2 == null) { + return null; + } + Node loop1 = getLoopNode(head1); + Node loop2 = getLoopNode(head2); + if (loop1 == null && loop2 == null) { + return noLoop(head1, head2); + } + if (loop1 != null && loop2 != null) { + return bothLoop(head1, loop1, head2, loop2); + } + return null; + } + + // 找到链表第一个入环节点,如果无环,返回null + public static Node getLoopNode(Node head) { + if (head == null || head.next == null || head.next.next == null) { + return null; + } + // n1 慢 n2 快 + Node slow = head.next; // n1 -> slow + Node fast = head.next.next; // n2 -> fast + while (slow != fast) { + if (fast.next == null || fast.next.next == null) { + return null; + } + fast = fast.next.next; + slow = slow.next; + } + // slow fast 相遇 + fast = head; // n2 -> walk again from head + while (slow != fast) { + slow = slow.next; + fast = fast.next; + } + return slow; + } + + // 如果两个链表都无环,返回第一个相交节点,如果不想交,返回null + public static Node noLoop(Node head1, Node head2) { + if (head1 == null || head2 == null) { + return null; + } + Node cur1 = head1; + Node cur2 = head2; + int n = 0; + while (cur1.next != null) { + n++; + cur1 = cur1.next; + } + while (cur2.next != null) { + n--; + cur2 = cur2.next; + } + if (cur1 != cur2) { + return null; + } + // n : 链表1长度减去链表2长度的值 + cur1 = n > 0 ? head1 : head2; // 谁长,谁的头变成cur1 + cur2 = cur1 == head1 ? head2 : head1; // 谁短,谁的头变成cur2 + n = Math.abs(n); + while (n != 0) { + n--; + cur1 = cur1.next; + } + while (cur1 != cur2) { + cur1 = cur1.next; + cur2 = cur2.next; + } + return cur1; + } + + // 两个有环链表,返回第一个相交节点,如果不想交返回null + public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) { + Node cur1 = null; + Node cur2 = null; + if (loop1 == loop2) { + cur1 = head1; + cur2 = head2; + int n = 0; + while (cur1 != loop1) { + n++; + cur1 = cur1.next; + } + while (cur2 != loop2) { + n--; + cur2 = cur2.next; + } + cur1 = n > 0 ? head1 : head2; + cur2 = cur1 == head1 ? head2 : head1; + n = Math.abs(n); + while (n != 0) { + n--; + cur1 = cur1.next; + } + while (cur1 != cur2) { + cur1 = cur1.next; + cur2 = cur2.next; + } + return cur1; + } else { + cur1 = loop1.next; + while (cur1 != loop1) { + if (cur1 == loop2) { + return loop1; + } + cur1 = cur1.next; + } + return null; + } + } + + public static void main(String[] args) { + // 1->2->3->4->5->6->7->null + Node head1 = new Node(1); + head1.next = new Node(2); + head1.next.next = new Node(3); + head1.next.next.next = new Node(4); + head1.next.next.next.next = new Node(5); + head1.next.next.next.next.next = new Node(6); + head1.next.next.next.next.next.next = new Node(7); + + // 0->9->8->6->7->null + Node head2 = new Node(0); + head2.next = new Node(9); + head2.next.next = new Node(8); + head2.next.next.next = head1.next.next.next.next.next; // 8->6 + System.out.println(getIntersectNode(head1, head2).value); + + // 1->2->3->4->5->6->7->4... + head1 = new Node(1); + head1.next = new Node(2); + head1.next.next = new Node(3); + head1.next.next.next = new Node(4); + head1.next.next.next.next = new Node(5); + head1.next.next.next.next.next = new Node(6); + head1.next.next.next.next.next.next = new Node(7); + head1.next.next.next.next.next.next = head1.next.next.next; // 7->4 + + // 0->9->8->2... + head2 = new Node(0); + head2.next = new Node(9); + head2.next.next = new Node(8); + head2.next.next.next = head1.next; // 8->2 + System.out.println(getIntersectNode(head1, head2).value); + + // 0->9->8->6->4->5->6.. + head2 = new Node(0); + head2.next = new Node(9); + head2.next.next = new Node(8); + head2.next.next.next = head1.next.next.next.next.next; // 8->6 + System.out.println(getIntersectNode(head1, head2).value); + + } + +} diff --git a/体系学习班/class10/Code02_RecursiveTraversalBT.java b/体系学习班/class10/Code02_RecursiveTraversalBT.java new file mode 100644 index 0000000..c7f2b2c --- /dev/null +++ b/体系学习班/class10/Code02_RecursiveTraversalBT.java @@ -0,0 +1,72 @@ +package class10; + +public class Code02_RecursiveTraversalBT { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + public static void f(Node head) { + if (head == null) { + return; + } + // 1 + f(head.left); + // 2 + f(head.right); + // 3 + } + + // 先序打印所有节点 + public static void pre(Node head) { + if (head == null) { + return; + } + System.out.println(head.value); + pre(head.left); + pre(head.right); + } + + public static void in(Node head) { + if (head == null) { + return; + } + in(head.left); + System.out.println(head.value); + in(head.right); + } + + public static void pos(Node head) { + if (head == null) { + return; + } + pos(head.left); + pos(head.right); + System.out.println(head.value); + } + + public static void main(String[] args) { + Node head = new Node(1); + head.left = new Node(2); + head.right = new Node(3); + head.left.left = new Node(4); + head.left.right = new Node(5); + head.right.left = new Node(6); + head.right.right = new Node(7); + + pre(head); + System.out.println("========"); + in(head); + System.out.println("========"); + pos(head); + System.out.println("========"); + + } + +} diff --git a/体系学习班/class10/Code03_UnRecursiveTraversalBT.java b/体系学习班/class10/Code03_UnRecursiveTraversalBT.java new file mode 100644 index 0000000..96ef25e --- /dev/null +++ b/体系学习班/class10/Code03_UnRecursiveTraversalBT.java @@ -0,0 +1,118 @@ +package class10; + +import java.util.Stack; + +public class Code03_UnRecursiveTraversalBT { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + public static void pre(Node head) { + System.out.print("pre-order: "); + if (head != null) { + Stack stack = new Stack(); + stack.add(head); + while (!stack.isEmpty()) { + head = stack.pop(); + System.out.print(head.value + " "); + if (head.right != null) { + stack.push(head.right); + } + if (head.left != null) { + stack.push(head.left); + } + } + } + System.out.println(); + } + + public static void in(Node cur) { + System.out.print("in-order: "); + if (cur != null) { + Stack stack = new Stack(); + while (!stack.isEmpty() || cur != null) { + if (cur != null) { + stack.push(cur); + cur = cur.left; + } else { + cur = stack.pop(); + System.out.print(cur.value + " "); + cur = cur.right; + } + } + } + System.out.println(); + } + + public static void pos1(Node head) { + System.out.print("pos-order: "); + if (head != null) { + Stack s1 = new Stack(); + Stack s2 = new Stack(); + s1.push(head); + while (!s1.isEmpty()) { + head = s1.pop(); // 头 右 左 + s2.push(head); + if (head.left != null) { + s1.push(head.left); + } + if (head.right != null) { + s1.push(head.right); + } + } + // 左 右 头 + while (!s2.isEmpty()) { + System.out.print(s2.pop().value + " "); + } + } + System.out.println(); + } + + public static void pos2(Node h) { + System.out.print("pos-order: "); + if (h != null) { + Stack stack = new Stack(); + stack.push(h); + Node c = null; + while (!stack.isEmpty()) { + c = stack.peek(); + if (c.left != null && h != c.left && h != c.right) { + stack.push(c.left); + } else if (c.right != null && h != c.right) { + stack.push(c.right); + } else { + System.out.print(stack.pop().value + " "); + h = c; + } + } + } + System.out.println(); + } + + public static void main(String[] args) { + Node head = new Node(1); + head.left = new Node(2); + head.right = new Node(3); + head.left.left = new Node(4); + head.left.right = new Node(5); + head.right.left = new Node(6); + head.right.right = new Node(7); + + pre(head); + System.out.println("========"); + in(head); + System.out.println("========"); + pos1(head); + System.out.println("========"); + pos2(head); + System.out.println("========"); + } + +} diff --git a/体系学习班/class11/Code01_LevelTraversalBT.java b/体系学习班/class11/Code01_LevelTraversalBT.java new file mode 100644 index 0000000..12e8ef0 --- /dev/null +++ b/体系学习班/class11/Code01_LevelTraversalBT.java @@ -0,0 +1,49 @@ +package class11; + +import java.util.LinkedList; +import java.util.Queue; + +public class Code01_LevelTraversalBT { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + public static void level(Node head) { + if (head == null) { + return; + } + Queue queue = new LinkedList<>(); + queue.add(head); + while (!queue.isEmpty()) { + Node cur = queue.poll(); + System.out.println(cur.value); + if (cur.left != null) { + queue.add(cur.left); + } + if (cur.right != null) { + queue.add(cur.right); + } + } + } + + public static void main(String[] args) { + Node head = new Node(1); + head.left = new Node(2); + head.right = new Node(3); + head.left.left = new Node(4); + head.left.right = new Node(5); + head.right.left = new Node(6); + head.right.right = new Node(7); + + level(head); + System.out.println("========"); + } + +} diff --git a/体系学习班/class11/Code02_SerializeAndReconstructTree.java b/体系学习班/class11/Code02_SerializeAndReconstructTree.java new file mode 100644 index 0000000..6e6da7e --- /dev/null +++ b/体系学习班/class11/Code02_SerializeAndReconstructTree.java @@ -0,0 +1,264 @@ +package class11; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Stack; + +public class Code02_SerializeAndReconstructTree { + /* + * 二叉树可以通过先序、后序或者按层遍历的方式序列化和反序列化, + * 以下代码全部实现了。 + * 但是,二叉树无法通过中序遍历的方式实现序列化和反序列化 + * 因为不同的两棵树,可能得到同样的中序序列,即便补了空位置也可能一样。 + * 比如如下两棵树 + * __2 + * / + * 1 + * 和 + * 1__ + * \ + * 2 + * 补足空位置的中序遍历结果都是{ null, 1, null, 2, null} + * + * */ + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static Queue preSerial(Node head) { + Queue ans = new LinkedList<>(); + pres(head, ans); + return ans; + } + + public static void pres(Node head, Queue ans) { + if (head == null) { + ans.add(null); + } else { + ans.add(String.valueOf(head.value)); + pres(head.left, ans); + pres(head.right, ans); + } + } + + public static Queue inSerial(Node head) { + Queue ans = new LinkedList<>(); + ins(head, ans); + return ans; + } + + public static void ins(Node head, Queue ans) { + if (head == null) { + ans.add(null); + } else { + ins(head.left, ans); + ans.add(String.valueOf(head.value)); + ins(head.right, ans); + } + } + + public static Queue posSerial(Node head) { + Queue ans = new LinkedList<>(); + poss(head, ans); + return ans; + } + + public static void poss(Node head, Queue ans) { + if (head == null) { + ans.add(null); + } else { + poss(head.left, ans); + poss(head.right, ans); + ans.add(String.valueOf(head.value)); + } + } + + public static Node buildByPreQueue(Queue prelist) { + if (prelist == null || prelist.size() == 0) { + return null; + } + return preb(prelist); + } + + public static Node preb(Queue prelist) { + String value = prelist.poll(); + if (value == null) { + return null; + } + Node head = new Node(Integer.valueOf(value)); + head.left = preb(prelist); + head.right = preb(prelist); + return head; + } + + public static Node buildByPosQueue(Queue poslist) { + if (poslist == null || poslist.size() == 0) { + return null; + } + // 左右中 -> stack(中右左) + Stack stack = new Stack<>(); + while (!poslist.isEmpty()) { + stack.push(poslist.poll()); + } + return posb(stack); + } + + public static Node posb(Stack posstack) { + String value = posstack.pop(); + if (value == null) { + return null; + } + Node head = new Node(Integer.valueOf(value)); + head.right = posb(posstack); + head.left = posb(posstack); + return head; + } + + public static Queue levelSerial(Node head) { + Queue ans = new LinkedList<>(); + if (head == null) { + ans.add(null); + } else { + ans.add(String.valueOf(head.value)); + Queue queue = new LinkedList(); + queue.add(head); + while (!queue.isEmpty()) { + head = queue.poll(); // head 父 子 + if (head.left != null) { + ans.add(String.valueOf(head.left.value)); + queue.add(head.left); + } else { + ans.add(null); + } + if (head.right != null) { + ans.add(String.valueOf(head.right.value)); + queue.add(head.right); + } else { + ans.add(null); + } + } + } + return ans; + } + + public static Node buildByLevelQueue(Queue levelList) { + if (levelList == null || levelList.size() == 0) { + return null; + } + Node head = generateNode(levelList.poll()); + Queue queue = new LinkedList(); + if (head != null) { + queue.add(head); + } + Node node = null; + while (!queue.isEmpty()) { + node = queue.poll(); + node.left = generateNode(levelList.poll()); + node.right = generateNode(levelList.poll()); + if (node.left != null) { + queue.add(node.left); + } + if (node.right != null) { + queue.add(node.right); + } + } + return head; + } + + public static Node generateNode(String val) { + if (val == null) { + return null; + } + return new Node(Integer.valueOf(val)); + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + // for test + public static boolean isSameValueStructure(Node head1, Node head2) { + if (head1 == null && head2 != null) { + return false; + } + if (head1 != null && head2 == null) { + return false; + } + if (head1 == null && head2 == null) { + return true; + } + if (head1.value != head2.value) { + return false; + } + return isSameValueStructure(head1.left, head2.left) && isSameValueStructure(head1.right, head2.right); + } + + // for test + public static void printTree(Node head) { + System.out.println("Binary Tree:"); + printInOrder(head, 0, "H", 17); + System.out.println(); + } + + public static void printInOrder(Node head, int height, String to, int len) { + if (head == null) { + return; + } + printInOrder(head.right, height + 1, "v", len); + String val = to + head.value + to; + int lenM = val.length(); + int lenL = (len - lenM) / 2; + int lenR = len - lenM - lenL; + val = getSpace(lenL) + val + getSpace(lenR); + System.out.println(getSpace(height * len) + val); + printInOrder(head.left, height + 1, "^", len); + } + + public static String getSpace(int num) { + String space = " "; + StringBuffer buf = new StringBuffer(""); + for (int i = 0; i < num; i++) { + buf.append(space); + } + return buf.toString(); + } + + public static void main(String[] args) { + int maxLevel = 5; + int maxValue = 100; + int testTimes = 1000000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + Queue pre = preSerial(head); + Queue pos = posSerial(head); + Queue level = levelSerial(head); + Node preBuild = buildByPreQueue(pre); + Node posBuild = buildByPosQueue(pos); + Node levelBuild = buildByLevelQueue(level); + if (!isSameValueStructure(preBuild, posBuild) || !isSameValueStructure(posBuild, levelBuild)) { + System.out.println("Oops!"); + } + } + System.out.println("test finish!"); + + } +} diff --git a/体系学习班/class11/Code03_EncodeNaryTreeToBinaryTree.java b/体系学习班/class11/Code03_EncodeNaryTreeToBinaryTree.java new file mode 100644 index 0000000..aa9f2ab --- /dev/null +++ b/体系学习班/class11/Code03_EncodeNaryTreeToBinaryTree.java @@ -0,0 +1,86 @@ +package class11; + +import java.util.ArrayList; +import java.util.List; + +// 本题测试链接:https://leetcode.com/problems/encode-n-ary-tree-to-binary-tree +public class Code03_EncodeNaryTreeToBinaryTree { + + // 提交时不要提交这个类 + public static class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val) { + val = _val; + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + }; + + // 提交时不要提交这个类 + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + // 只提交这个类即可 + class Codec { + // Encodes an n-ary tree to a binary tree. + public TreeNode encode(Node root) { + if (root == null) { + return null; + } + TreeNode head = new TreeNode(root.val); + head.left = en(root.children); + return head; + } + + private TreeNode en(List children) { + TreeNode head = null; + TreeNode cur = null; + for (Node child : children) { + TreeNode tNode = new TreeNode(child.val); + if (head == null) { + head = tNode; + } else { + cur.right = tNode; + } + cur = tNode; + cur.left = en(child.children); + } + return head; + } + + // Decodes your binary tree to an n-ary tree. + public Node decode(TreeNode root) { + if (root == null) { + return null; + } + return new Node(root.val, de(root.left)); + } + + public List de(TreeNode root) { + List children = new ArrayList<>(); + while (root != null) { + Node cur = new Node(root.val, de(root.left)); + children.add(cur); + root = root.right; + } + return children; + } + + } + +} diff --git a/体系学习班/class11/Code04_PrintBinaryTree.java b/体系学习班/class11/Code04_PrintBinaryTree.java new file mode 100644 index 0000000..75125f7 --- /dev/null +++ b/体系学习班/class11/Code04_PrintBinaryTree.java @@ -0,0 +1,74 @@ +package class11; + +public class Code04_PrintBinaryTree { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static void printTree(Node head) { + System.out.println("Binary Tree:"); + printInOrder(head, 0, "H", 17); + System.out.println(); + } + + public static void printInOrder(Node head, int height, String to, int len) { + if (head == null) { + return; + } + printInOrder(head.right, height + 1, "v", len); + String val = to + head.value + to; + int lenM = val.length(); + int lenL = (len - lenM) / 2; + int lenR = len - lenM - lenL; + val = getSpace(lenL) + val + getSpace(lenR); + System.out.println(getSpace(height * len) + val); + printInOrder(head.left, height + 1, "^", len); + } + + public static String getSpace(int num) { + String space = " "; + StringBuffer buf = new StringBuffer(""); + for (int i = 0; i < num; i++) { + buf.append(space); + } + return buf.toString(); + } + + public static void main(String[] args) { + Node head = new Node(1); + head.left = new Node(-222222222); + head.right = new Node(3); + head.left.left = new Node(Integer.MIN_VALUE); + head.right.left = new Node(55555555); + head.right.right = new Node(66); + head.left.left.right = new Node(777); + printTree(head); + + head = new Node(1); + head.left = new Node(2); + head.right = new Node(3); + head.left.left = new Node(4); + head.right.left = new Node(5); + head.right.right = new Node(6); + head.left.left.right = new Node(7); + printTree(head); + + head = new Node(1); + head.left = new Node(1); + head.right = new Node(1); + head.left.left = new Node(1); + head.right.left = new Node(1); + head.right.right = new Node(1); + head.left.left.right = new Node(1); + printTree(head); + + } + +} diff --git a/体系学习班/class11/Code05_TreeMaxWidth.java b/体系学习班/class11/Code05_TreeMaxWidth.java new file mode 100644 index 0000000..787bd01 --- /dev/null +++ b/体系学习班/class11/Code05_TreeMaxWidth.java @@ -0,0 +1,114 @@ +package class11; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; + +public class Code05_TreeMaxWidth { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static int maxWidthUseMap(Node head) { + if (head == null) { + return 0; + } + Queue queue = new LinkedList<>(); + queue.add(head); + // key 在 哪一层,value + HashMap levelMap = new HashMap<>(); + levelMap.put(head, 1); + int curLevel = 1; // 当前你正在统计哪一层的宽度 + int curLevelNodes = 0; // 当前层curLevel层,宽度目前是多少 + int max = 0; + while (!queue.isEmpty()) { + Node cur = queue.poll(); + int curNodeLevel = levelMap.get(cur); + if (cur.left != null) { + levelMap.put(cur.left, curNodeLevel + 1); + queue.add(cur.left); + } + if (cur.right != null) { + levelMap.put(cur.right, curNodeLevel + 1); + queue.add(cur.right); + } + if (curNodeLevel == curLevel) { + curLevelNodes++; + } else { + max = Math.max(max, curLevelNodes); + curLevel++; + curLevelNodes = 1; + } + } + max = Math.max(max, curLevelNodes); + return max; + } + + public static int maxWidthNoMap(Node head) { + if (head == null) { + return 0; + } + Queue queue = new LinkedList<>(); + queue.add(head); + Node curEnd = head; // 当前层,最右节点是谁 + Node nextEnd = null; // 下一层,最右节点是谁 + int max = 0; + int curLevelNodes = 0; // 当前层的节点数 + while (!queue.isEmpty()) { + Node cur = queue.poll(); + if (cur.left != null) { + queue.add(cur.left); + nextEnd = cur.left; + } + if (cur.right != null) { + queue.add(cur.right); + nextEnd = cur.right; + } + curLevelNodes++; + if (cur == curEnd) { + max = Math.max(max, curLevelNodes); + curLevelNodes = 0; + curEnd = nextEnd; + } + } + return max; + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 10; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + if (maxWidthUseMap(head) != maxWidthNoMap(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + + } + +} diff --git a/体系学习班/class11/Code06_SuccessorNode.java b/体系学习班/class11/Code06_SuccessorNode.java new file mode 100644 index 0000000..b33d4b5 --- /dev/null +++ b/体系学习班/class11/Code06_SuccessorNode.java @@ -0,0 +1,86 @@ +package class11; + +public class Code06_SuccessorNode { + + public static class Node { + public int value; + public Node left; + public Node right; + public Node parent; + + public Node(int data) { + this.value = data; + } + } + + public static Node getSuccessorNode(Node node) { + if (node == null) { + return node; + } + if (node.right != null) { + return getLeftMost(node.right); + } else { // 无右子树 + Node parent = node.parent; + while (parent != null && parent.right == node) { // 当前节点是其父亲节点右孩子 + node = parent; + parent = node.parent; + } + return parent; + } + } + + public static Node getLeftMost(Node node) { + if (node == null) { + return node; + } + while (node.left != null) { + node = node.left; + } + return node; + } + + public static void main(String[] args) { + Node head = new Node(6); + head.parent = null; + head.left = new Node(3); + head.left.parent = head; + head.left.left = new Node(1); + head.left.left.parent = head.left; + head.left.left.right = new Node(2); + head.left.left.right.parent = head.left.left; + head.left.right = new Node(4); + head.left.right.parent = head.left; + head.left.right.right = new Node(5); + head.left.right.right.parent = head.left.right; + head.right = new Node(9); + head.right.parent = head; + head.right.left = new Node(8); + head.right.left.parent = head.right; + head.right.left.left = new Node(7); + head.right.left.left.parent = head.right.left; + head.right.right = new Node(10); + head.right.right.parent = head.right; + + Node test = head.left.left; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.left.left.right; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.left; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.left.right; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.left.right.right; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.right.left.left; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.right.left; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.right; + System.out.println(test.value + " next: " + getSuccessorNode(test).value); + test = head.right.right; // 10's next is null + System.out.println(test.value + " next: " + getSuccessorNode(test)); + } + +} diff --git a/体系学习班/class11/Code07_PaperFolding.java b/体系学习班/class11/Code07_PaperFolding.java new file mode 100644 index 0000000..3f3b0db --- /dev/null +++ b/体系学习班/class11/Code07_PaperFolding.java @@ -0,0 +1,28 @@ +package class11; + +public class Code07_PaperFolding { + + public static void printAllFolds(int N) { + process(1, N, true); + System.out.println(); + } + + // 当前你来了一个节点,脑海中想象的! + // 这个节点在第i层,一共有N层,N固定不变的 + // 这个节点如果是凹的话,down = T + // 这个节点如果是凸的话,down = F + // 函数的功能:中序打印以你想象的节点为头的整棵树! + public static void process(int i, int N, boolean down) { + if (i > N) { + return; + } + process(i + 1, N, true); + System.out.print(down ? "凹 " : "凸 "); + process(i + 1, N, false); + } + + public static void main(String[] args) { + int N = 4; + printAllFolds(N); + } +} diff --git a/体系学习班/class12/Code01_IsCBT.java b/体系学习班/class12/Code01_IsCBT.java new file mode 100644 index 0000000..e0b9cab --- /dev/null +++ b/体系学习班/class12/Code01_IsCBT.java @@ -0,0 +1,149 @@ +package class12; + +import java.util.LinkedList; + +public class Code01_IsCBT { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static boolean isCBT1(Node head) { + if (head == null) { + return true; + } + LinkedList queue = new LinkedList<>(); + // 是否遇到过左右两个孩子不双全的节点 + boolean leaf = false; + Node l = null; + Node r = null; + queue.add(head); + while (!queue.isEmpty()) { + head = queue.poll(); + l = head.left; + r = head.right; + if ( + // 如果遇到了不双全的节点之后,又发现当前节点不是叶节点 + (leaf && (l != null || r != null)) + || + (l == null && r != null) + + ) { + return false; + } + if (l != null) { + queue.add(l); + } + if (r != null) { + queue.add(r); + } + if (l == null || r == null) { + leaf = true; + } + } + return true; + } + + public static boolean isCBT2(Node head) { + if (head == null) { + return true; + } + return process(head).isCBT; + } + + // 对每一棵子树,是否是满二叉树、是否是完全二叉树、高度 + public static class Info { + public boolean isFull; + public boolean isCBT; + public int height; + + public Info(boolean full, boolean cbt, int h) { + isFull = full; + isCBT = cbt; + height = h; + } + } + + public static Info process(Node X) { + if (X == null) { + return new Info(true, true, 0); + } + Info leftInfo = process(X.left); + Info rightInfo = process(X.right); + + + + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + + + boolean isFull = leftInfo.isFull + && + rightInfo.isFull + && leftInfo.height == rightInfo.height; + + + boolean isCBT = false; + if (isFull) { + isCBT = true; + } else { // 以x为头整棵树,不满 + if (leftInfo.isCBT && rightInfo.isCBT) { + + + if (leftInfo.isCBT + && rightInfo.isFull + && leftInfo.height == rightInfo.height + 1) { + isCBT = true; + } + if (leftInfo.isFull + && + rightInfo.isFull + && leftInfo.height == rightInfo.height + 1) { + isCBT = true; + } + if (leftInfo.isFull + && rightInfo.isCBT && leftInfo.height == rightInfo.height) { + isCBT = true; + } + + + } + } + return new Info(isFull, isCBT, height); + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 5; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + if (isCBT1(head) != isCBT2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class12/Code02_IsBST.java b/体系学习班/class12/Code02_IsBST.java new file mode 100644 index 0000000..30d99dc --- /dev/null +++ b/体系学习班/class12/Code02_IsBST.java @@ -0,0 +1,125 @@ +package class12; + +import java.util.ArrayList; + +public class Code02_IsBST { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static boolean isBST1(Node head) { + if (head == null) { + return true; + } + ArrayList arr = new ArrayList<>(); + in(head, arr); + for (int i = 1; i < arr.size(); i++) { + if (arr.get(i).value <= arr.get(i - 1).value) { + return false; + } + } + return true; + } + + public static void in(Node head, ArrayList arr) { + if (head == null) { + return; + } + in(head.left, arr); + arr.add(head); + in(head.right, arr); + } + + public static boolean isBST2(Node head) { + if (head == null) { + return true; + } + return process(head).isBST; + } + + public static class Info { + public boolean isBST; + public int max; + public int min; + + public Info(boolean i, int ma, int mi) { + isBST = i; + max = ma; + min = mi; + } + + } + + public static Info process(Node x) { + if (x == null) { + return null; + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int max = x.value; + if (leftInfo != null) { + max = Math.max(max, leftInfo.max); + } + if (rightInfo != null) { + max = Math.max(max, rightInfo.max); + } + int min = x.value; + if (leftInfo != null) { + min = Math.min(min, leftInfo.min); + } + if (rightInfo != null) { + min = Math.min(min, rightInfo.min); + } + boolean isBST = true; + if (leftInfo != null && !leftInfo.isBST) { + isBST = false; + } + if (rightInfo != null && !rightInfo.isBST) { + isBST = false; + } + if (leftInfo != null && leftInfo.max >= x.value) { + isBST = false; + } + if (rightInfo != null && rightInfo.min <= x.value) { + isBST = false; + } + return new Info(isBST, max, min); + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 4; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + if (isBST1(head) != isBST2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class12/Code03_IsBalanced.java b/体系学习班/class12/Code03_IsBalanced.java new file mode 100644 index 0000000..e8a6c0f --- /dev/null +++ b/体系学习班/class12/Code03_IsBalanced.java @@ -0,0 +1,102 @@ +package class12; + +public class Code03_IsBalanced { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static boolean isBalanced1(Node head) { + boolean[] ans = new boolean[1]; + ans[0] = true; + process1(head, ans); + return ans[0]; + } + + public static int process1(Node head, boolean[] ans) { + if (!ans[0] || head == null) { + return -1; + } + int leftHeight = process1(head.left, ans); + int rightHeight = process1(head.right, ans); + if (Math.abs(leftHeight - rightHeight) > 1) { + ans[0] = false; + } + return Math.max(leftHeight, rightHeight) + 1; + } + + public static boolean isBalanced2(Node head) { + return process(head).isBalanced; + } + + public static class Info{ + public boolean isBalanced; + public int height; + + public Info(boolean i, int h) { + isBalanced = i; + height = h; + } + } + + public static Info process(Node x) { + if(x == null) { + return new Info(true, 0); + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + boolean isBalanced = true; + if(!leftInfo.isBalanced) { + isBalanced = false; + } + if(!rightInfo.isBalanced) { + isBalanced = false; + } + if(Math.abs(leftInfo.height - rightInfo.height) > 1) { + isBalanced = false; + } + return new Info(isBalanced, height); + } + + + + + + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 5; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + if (isBalanced1(head) != isBalanced2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class12/Code04_IsFull.java b/体系学习班/class12/Code04_IsFull.java new file mode 100644 index 0000000..a83c952 --- /dev/null +++ b/体系学习班/class12/Code04_IsFull.java @@ -0,0 +1,109 @@ +package class12; + +public class Code04_IsFull { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + // 第一种方法 + // 收集整棵树的高度h,和节点数n + // 只有满二叉树满足 : 2 ^ h - 1 == n + public static boolean isFull1(Node head) { + if (head == null) { + return true; + } + Info1 all = process1(head); + return (1 << all.height) - 1 == all.nodes; + } + + public static class Info1 { + public int height; + public int nodes; + + public Info1(int h, int n) { + height = h; + nodes = n; + } + } + + public static Info1 process1(Node head) { + if (head == null) { + return new Info1(0, 0); + } + Info1 leftInfo = process1(head.left); + Info1 rightInfo = process1(head.right); + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + int nodes = leftInfo.nodes + rightInfo.nodes + 1; + return new Info1(height, nodes); + } + + // 第二种方法 + // 收集子树是否是满二叉树 + // 收集子树的高度 + // 左树满 && 右树满 && 左右树高度一样 -> 整棵树是满的 + public static boolean isFull2(Node head) { + if (head == null) { + return true; + } + return process2(head).isFull; + } + + public static class Info2 { + public boolean isFull; + public int height; + + public Info2(boolean f, int h) { + isFull = f; + height = h; + } + } + + public static Info2 process2(Node h) { + if (h == null) { + return new Info2(true, 0); + } + Info2 leftInfo = process2(h.left); + Info2 rightInfo = process2(h.right); + boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height; + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + return new Info2(isFull, height); + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 5; + int maxValue = 100; + int testTimes = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + if (isFull1(head) != isFull2(head)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class12/Code05_MaxSubBSTSize.java b/体系学习班/class12/Code05_MaxSubBSTSize.java new file mode 100644 index 0000000..85ee0cd --- /dev/null +++ b/体系学习班/class12/Code05_MaxSubBSTSize.java @@ -0,0 +1,157 @@ +package class12; + +import java.util.ArrayList; + +// 在线测试链接 : https://leetcode.com/problems/largest-bst-subtree +public class Code05_MaxSubBSTSize { + + // 提交时不要提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int value) { + val = value; + } + } + + // 提交如下的largestBSTSubtree方法,可以直接通过 + public static int largestBSTSubtree(TreeNode head) { + if (head == null) { + return 0; + } + return process(head).maxBSTSubtreeSize; + } + + public static class Info { + public int maxBSTSubtreeSize; + public int allSize; + public int max; + public int min; + + public Info(int m, int a, int ma, int mi) { + maxBSTSubtreeSize = m; + allSize = a; + max = ma; + min = mi; + } + } + + public static Info process(TreeNode x) { + if (x == null) { + return null; + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int max = x.val; + int min = x.val; + int allSize = 1; + if (leftInfo != null) { + max = Math.max(leftInfo.max, max); + min = Math.min(leftInfo.min, min); + allSize += leftInfo.allSize; + } + if (rightInfo != null) { + max = Math.max(rightInfo.max, max); + min = Math.min(rightInfo.min, min); + allSize += rightInfo.allSize; + } + int p1 = -1; + if (leftInfo != null) { + p1 = leftInfo.maxBSTSubtreeSize; + } + int p2 = -1; + if (rightInfo != null) { + p2 = rightInfo.maxBSTSubtreeSize; + } + int p3 = -1; + boolean leftBST = leftInfo == null ? true : (leftInfo.maxBSTSubtreeSize == leftInfo.allSize); + boolean rightBST = rightInfo == null ? true : (rightInfo.maxBSTSubtreeSize == rightInfo.allSize); + if (leftBST && rightBST) { + boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.val); + boolean rightMinMoreX = rightInfo == null ? true : (x.val < rightInfo.min); + if (leftMaxLessX && rightMinMoreX) { + int leftSize = leftInfo == null ? 0 : leftInfo.allSize; + int rightSize = rightInfo == null ? 0 : rightInfo.allSize; + p3 = leftSize + rightSize + 1; + } + } + return new Info(Math.max(p1, Math.max(p2, p3)), allSize, max, min); + } + + // 为了验证 + // 对数器方法 + public static int right(TreeNode head) { + if (head == null) { + return 0; + } + int h = getBSTSize(head); + if (h != 0) { + return h; + } + return Math.max(right(head.left), right(head.right)); + } + + // 为了验证 + // 对数器方法 + public static int getBSTSize(TreeNode head) { + if (head == null) { + return 0; + } + ArrayList arr = new ArrayList<>(); + in(head, arr); + for (int i = 1; i < arr.size(); i++) { + if (arr.get(i).val <= arr.get(i - 1).val) { + return 0; + } + } + return arr.size(); + } + + // 为了验证 + // 对数器方法 + public static void in(TreeNode head, ArrayList arr) { + if (head == null) { + return; + } + in(head.left, arr); + arr.add(head); + in(head.right, arr); + } + + // 为了验证 + // 对数器方法 + public static TreeNode generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // 为了验证 + // 对数器方法 + public static TreeNode generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + TreeNode head = new TreeNode((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + // 为了验证 + // 对数器方法 + public static void main(String[] args) { + int maxLevel = 4; + int maxValue = 100; + int testTimes = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + TreeNode head = generateRandomBST(maxLevel, maxValue); + if (largestBSTSubtree(head) != right(head)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class12/Code06_MaxDistance.java b/体系学习班/class12/Code06_MaxDistance.java new file mode 100644 index 0000000..8c82c0e --- /dev/null +++ b/体系学习班/class12/Code06_MaxDistance.java @@ -0,0 +1,180 @@ +package class12; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +public class Code06_MaxDistance { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static int maxDistance1(Node head) { + if (head == null) { + return 0; + } + ArrayList arr = getPrelist(head); + HashMap parentMap = getParentMap(head); + int max = 0; + for (int i = 0; i < arr.size(); i++) { + for (int j = i; j < arr.size(); j++) { + max = Math.max(max, distance(parentMap, arr.get(i), arr.get(j))); + } + } + return max; + } + + public static ArrayList getPrelist(Node head) { + ArrayList arr = new ArrayList<>(); + fillPrelist(head, arr); + return arr; + } + + public static void fillPrelist(Node head, ArrayList arr) { + if (head == null) { + return; + } + arr.add(head); + fillPrelist(head.left, arr); + fillPrelist(head.right, arr); + } + + public static HashMap getParentMap(Node head) { + HashMap map = new HashMap<>(); + map.put(head, null); + fillParentMap(head, map); + return map; + } + + public static void fillParentMap(Node head, HashMap parentMap) { + if (head.left != null) { + parentMap.put(head.left, head); + fillParentMap(head.left, parentMap); + } + if (head.right != null) { + parentMap.put(head.right, head); + fillParentMap(head.right, parentMap); + } + } + + public static int distance(HashMap parentMap, Node o1, Node o2) { + HashSet o1Set = new HashSet<>(); + Node cur = o1; + o1Set.add(cur); + while (parentMap.get(cur) != null) { + cur = parentMap.get(cur); + o1Set.add(cur); + } + cur = o2; + while (!o1Set.contains(cur)) { + cur = parentMap.get(cur); + } + Node lowestAncestor = cur; + cur = o1; + int distance1 = 1; + while (cur != lowestAncestor) { + cur = parentMap.get(cur); + distance1++; + } + cur = o2; + int distance2 = 1; + while (cur != lowestAncestor) { + cur = parentMap.get(cur); + distance2++; + } + return distance1 + distance2 - 1; + } + +// public static int maxDistance2(Node head) { +// return process(head).maxDistance; +// } +// +// public static class Info { +// public int maxDistance; +// public int height; +// +// public Info(int dis, int h) { +// maxDistance = dis; +// height = h; +// } +// } +// +// public static Info process(Node X) { +// if (X == null) { +// return new Info(0, 0); +// } +// Info leftInfo = process(X.left); +// Info rightInfo = process(X.right); +// int height = Math.max(leftInfo.height, rightInfo.height) + 1; +// int maxDistance = Math.max( +// Math.max(leftInfo.maxDistance, rightInfo.maxDistance), +// leftInfo.height + rightInfo.height + 1); +// return new Info(maxDistance, height); +// } + + public static int maxDistance2(Node head) { + return process(head).maxDistance; + } + + public static class Info { + public int maxDistance; + public int height; + + public Info(int m, int h) { + maxDistance = m; + height = h; + } + + } + + public static Info process(Node x) { + if (x == null) { + return new Info(0, 0); + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + int p1 = leftInfo.maxDistance; + int p2 = rightInfo.maxDistance; + int p3 = leftInfo.height + rightInfo.height + 1; + int maxDistance = Math.max(Math.max(p1, p2), p3); + return new Info(maxDistance, height); + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 4; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + if (maxDistance1(head) != maxDistance2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class13/Code01_IsCBT.java b/体系学习班/class13/Code01_IsCBT.java new file mode 100644 index 0000000..c991c8e --- /dev/null +++ b/体系学习班/class13/Code01_IsCBT.java @@ -0,0 +1,120 @@ +package class13; + +import java.util.LinkedList; + +// 测试链接 : https://leetcode.com/problems/check-completeness-of-a-binary-tree/ + +public class Code01_IsCBT { + + // 不要提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int v) { + val = v; + } + } + + public static boolean isCompleteTree1(TreeNode head) { + if (head == null) { + return true; + } + LinkedList queue = new LinkedList<>(); + // 是否遇到过左右两个孩子不双全的节点 + boolean leaf = false; + TreeNode l = null; + TreeNode r = null; + queue.add(head); + while (!queue.isEmpty()) { + head = queue.poll(); + l = head.left; + r = head.right; + if ( + // 如果遇到了不双全的节点之后,又发现当前节点不是叶节点 + (leaf && (l != null || r != null)) || (l == null && r != null) + + ) { + return false; + } + if (l != null) { + queue.add(l); + } + if (r != null) { + queue.add(r); + } + if (l == null || r == null) { + leaf = true; + } + } + return true; + } + + public static boolean isCompleteTree2(TreeNode head) { + return process(head).isCBT; + } + + public static class Info { + public boolean isFull; + public boolean isCBT; + public int height; + + public Info(boolean full, boolean cbt, int h) { + isFull = full; + isCBT = cbt; + height = h; + } + } + + public static Info process(TreeNode x) { + if (x == null) { + return new Info(true, true, 0); + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height; + boolean isCBT = false; + if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height) { + isCBT = true; + } else if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) { + isCBT = true; + } else if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) { + isCBT = true; + } else if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) { + isCBT = true; + } + return new Info(isFull, isCBT, height); + } + + // for test + public static TreeNode generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static TreeNode generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + TreeNode head = new TreeNode((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 5; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + TreeNode head = generateRandomBST(maxLevel, maxValue); + if (isCompleteTree1(head) != isCompleteTree2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class13/Code02_MaxSubBSTHead.java b/体系学习班/class13/Code02_MaxSubBSTHead.java new file mode 100644 index 0000000..e64c37a --- /dev/null +++ b/体系学习班/class13/Code02_MaxSubBSTHead.java @@ -0,0 +1,136 @@ +package class13; + +import java.util.ArrayList; + +public class Code02_MaxSubBSTHead { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static int getBSTSize(Node head) { + if (head == null) { + return 0; + } + ArrayList arr = new ArrayList<>(); + in(head, arr); + for (int i = 1; i < arr.size(); i++) { + if (arr.get(i).value <= arr.get(i - 1).value) { + return 0; + } + } + return arr.size(); + } + + public static void in(Node head, ArrayList arr) { + if (head == null) { + return; + } + in(head.left, arr); + arr.add(head); + in(head.right, arr); + } + + public static Node maxSubBSTHead1(Node head) { + if (head == null) { + return null; + } + if (getBSTSize(head) != 0) { + return head; + } + Node leftAns = maxSubBSTHead1(head.left); + Node rightAns = maxSubBSTHead1(head.right); + return getBSTSize(leftAns) >= getBSTSize(rightAns) ? leftAns : rightAns; + } + + public static Node maxSubBSTHead2(Node head) { + if (head == null) { + return null; + } + return process(head).maxSubBSTHead; + } + + // 每一棵子树 + public static class Info { + public Node maxSubBSTHead; + public int maxSubBSTSize; + public int min; + public int max; + + public Info(Node h, int size, int mi, int ma) { + maxSubBSTHead = h; + maxSubBSTSize = size; + min = mi; + max = ma; + } + } + + public static Info process(Node X) { + if (X == null) { + return null; + } + Info leftInfo = process(X.left); + Info rightInfo = process(X.right); + int min = X.value; + int max = X.value; + Node maxSubBSTHead = null; + int maxSubBSTSize = 0; + if (leftInfo != null) { + min = Math.min(min, leftInfo.min); + max = Math.max(max, leftInfo.max); + maxSubBSTHead = leftInfo.maxSubBSTHead; + maxSubBSTSize = leftInfo.maxSubBSTSize; + } + if (rightInfo != null) { + min = Math.min(min, rightInfo.min); + max = Math.max(max, rightInfo.max); + if (rightInfo.maxSubBSTSize > maxSubBSTSize) { + maxSubBSTHead = rightInfo.maxSubBSTHead; + maxSubBSTSize = rightInfo.maxSubBSTSize; + } + } + if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.value)) + && (rightInfo == null ? true : (rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.value))) { + maxSubBSTHead = X; + maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1; + } + return new Info(maxSubBSTHead, maxSubBSTSize, min, max); + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 4; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + if (maxSubBSTHead1(head) != maxSubBSTHead2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class13/Code03_lowestAncestor.java b/体系学习班/class13/Code03_lowestAncestor.java new file mode 100644 index 0000000..e2a9d5f --- /dev/null +++ b/体系学习班/class13/Code03_lowestAncestor.java @@ -0,0 +1,141 @@ +package class13; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +public class Code03_lowestAncestor { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + public static Node lowestAncestor1(Node head, Node o1, Node o2) { + if (head == null) { + return null; + } + // key的父节点是value + HashMap parentMap = new HashMap<>(); + parentMap.put(head, null); + fillParentMap(head, parentMap); + HashSet o1Set = new HashSet<>(); + Node cur = o1; + o1Set.add(cur); + while (parentMap.get(cur) != null) { + cur = parentMap.get(cur); + o1Set.add(cur); + } + cur = o2; + while (!o1Set.contains(cur)) { + cur = parentMap.get(cur); + } + return cur; + } + + public static void fillParentMap(Node head, HashMap parentMap) { + if (head.left != null) { + parentMap.put(head.left, head); + fillParentMap(head.left, parentMap); + } + if (head.right != null) { + parentMap.put(head.right, head); + fillParentMap(head.right, parentMap); + } + } + + public static Node lowestAncestor2(Node head, Node a, Node b) { + return process(head, a, b).ans; + } + + public static class Info { + public boolean findA; + public boolean findB; + public Node ans; + + public Info(boolean fA, boolean fB, Node an) { + findA = fA; + findB = fB; + ans = an; + } + } + + public static Info process(Node x, Node a, Node b) { + if (x == null) { + return new Info(false, false, null); + } + Info leftInfo = process(x.left, a, b); + Info rightInfo = process(x.right, a, b); + boolean findA = (x == a) || leftInfo.findA || rightInfo.findA; + boolean findB = (x == b) || leftInfo.findB || rightInfo.findB; + Node ans = null; + if (leftInfo.ans != null) { + ans = leftInfo.ans; + } else if (rightInfo.ans != null) { + ans = rightInfo.ans; + } else { + if (findA && findB) { + ans = x; + } + } + return new Info(findA, findB, ans); + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + // for test + public static Node pickRandomOne(Node head) { + if (head == null) { + return null; + } + ArrayList arr = new ArrayList<>(); + fillPrelist(head, arr); + int randomIndex = (int) (Math.random() * arr.size()); + return arr.get(randomIndex); + } + + // for test + public static void fillPrelist(Node head, ArrayList arr) { + if (head == null) { + return; + } + arr.add(head); + fillPrelist(head.left, arr); + fillPrelist(head.right, arr); + } + + public static void main(String[] args) { + int maxLevel = 4; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(maxLevel, maxValue); + Node o1 = pickRandomOne(head); + Node o2 = pickRandomOne(head); + if (lowestAncestor1(head, o1, o2) != lowestAncestor2(head, o1, o2)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class13/Code04_MaxHappy.java b/体系学习班/class13/Code04_MaxHappy.java new file mode 100644 index 0000000..3cb21e7 --- /dev/null +++ b/体系学习班/class13/Code04_MaxHappy.java @@ -0,0 +1,116 @@ +package class13; + +import java.util.ArrayList; +import java.util.List; + +public class Code04_MaxHappy { + + public static class Employee { + public int happy; + public List nexts; + + public Employee(int h) { + happy = h; + nexts = new ArrayList<>(); + } + + } + + public static int maxHappy1(Employee boss) { + if (boss == null) { + return 0; + } + return process1(boss, false); + } + + // 当前来到的节点叫cur, + // up表示cur的上级是否来, + // 该函数含义: + // 如果up为true,表示在cur上级已经确定来,的情况下,cur整棵树能够提供最大的快乐值是多少? + // 如果up为false,表示在cur上级已经确定不来,的情况下,cur整棵树能够提供最大的快乐值是多少? + public static int process1(Employee cur, boolean up) { + if (up) { // 如果cur的上级来的话,cur没得选,只能不来 + int ans = 0; + for (Employee next : cur.nexts) { + ans += process1(next, false); + } + return ans; + } else { // 如果cur的上级不来的话,cur可以选,可以来也可以不来 + int p1 = cur.happy; + int p2 = 0; + for (Employee next : cur.nexts) { + p1 += process1(next, true); + p2 += process1(next, false); + } + return Math.max(p1, p2); + } + } + + public static int maxHappy2(Employee head) { + Info allInfo = process(head); + return Math.max(allInfo.no, allInfo.yes); + } + + public static class Info { + public int no; + public int yes; + + public Info(int n, int y) { + no = n; + yes = y; + } + } + + public static Info process(Employee x) { + if (x == null) { + return new Info(0, 0); + } + int no = 0; + int yes = x.happy; + for (Employee next : x.nexts) { + Info nextInfo = process(next); + no += Math.max(nextInfo.no, nextInfo.yes); + yes += nextInfo.no; + + } + return new Info(no, yes); + } + + // for test + public static Employee genarateBoss(int maxLevel, int maxNexts, int maxHappy) { + if (Math.random() < 0.02) { + return null; + } + Employee boss = new Employee((int) (Math.random() * (maxHappy + 1))); + genarateNexts(boss, 1, maxLevel, maxNexts, maxHappy); + return boss; + } + + // for test + public static void genarateNexts(Employee e, int level, int maxLevel, int maxNexts, int maxHappy) { + if (level > maxLevel) { + return; + } + int nextsSize = (int) (Math.random() * (maxNexts + 1)); + for (int i = 0; i < nextsSize; i++) { + Employee next = new Employee((int) (Math.random() * (maxHappy + 1))); + e.nexts.add(next); + genarateNexts(next, level + 1, maxLevel, maxNexts, maxHappy); + } + } + + public static void main(String[] args) { + int maxLevel = 4; + int maxNexts = 7; + int maxHappy = 100; + int testTimes = 100000; + for (int i = 0; i < testTimes; i++) { + Employee boss = genarateBoss(maxLevel, maxNexts, maxHappy); + if (maxHappy1(boss) != maxHappy2(boss)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class13/Code05_LowestLexicography.java b/体系学习班/class13/Code05_LowestLexicography.java new file mode 100644 index 0000000..13ceedd --- /dev/null +++ b/体系学习班/class13/Code05_LowestLexicography.java @@ -0,0 +1,116 @@ +package class13; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.TreeSet; + +public class Code05_LowestLexicography { + + public static String lowestString1(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + TreeSet ans = process(strs); + return ans.size() == 0 ? "" : ans.first(); + } + + // strs中所有字符串全排列,返回所有可能的结果 + public static TreeSet process(String[] strs) { + TreeSet ans = new TreeSet<>(); + if (strs.length == 0) { + ans.add(""); + return ans; + } + for (int i = 0; i < strs.length; i++) { + String first = strs[i]; + String[] nexts = removeIndexString(strs, i); + TreeSet next = process(nexts); + for (String cur : next) { + ans.add(first + cur); + } + } + return ans; + } + + // {"abc", "cks", "bct"} + // 0 1 2 + // removeIndexString(arr , 1) -> {"abc", "bct"} + public static String[] removeIndexString(String[] arr, int index) { + int N = arr.length; + String[] ans = new String[N - 1]; + int ansIndex = 0; + for (int i = 0; i < N; i++) { + if (i != index) { + ans[ansIndex++] = arr[i]; + } + } + return ans; + } + + public static class MyComparator implements Comparator { + @Override + public int compare(String a, String b) { + return (a + b).compareTo(b + a); + } + } + + public static String lowestString2(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + Arrays.sort(strs, new MyComparator()); + String res = ""; + for (int i = 0; i < strs.length; i++) { + res += strs[i]; + } + return res; + } + + // for test + public static String generateRandomString(int strLen) { + char[] ans = new char[(int) (Math.random() * strLen) + 1]; + for (int i = 0; i < ans.length; i++) { + int value = (int) (Math.random() * 5); + ans[i] = (Math.random() <= 0.5) ? (char) (65 + value) : (char) (97 + value); + } + return String.valueOf(ans); + } + + // for test + public static String[] generateRandomStringArray(int arrLen, int strLen) { + String[] ans = new String[(int) (Math.random() * arrLen) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = generateRandomString(strLen); + } + return ans; + } + + // for test + public static String[] copyStringArray(String[] arr) { + String[] ans = new String[arr.length]; + for (int i = 0; i < ans.length; i++) { + ans[i] = String.valueOf(arr[i]); + } + return ans; + } + + public static void main(String[] args) { + int arrLen = 6; + int strLen = 5; + int testTimes = 10000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + String[] arr1 = generateRandomStringArray(arrLen, strLen); + String[] arr2 = copyStringArray(arr1); + if (!lowestString1(arr1).equals(lowestString2(arr2))) { + for (String str : arr1) { + System.out.print(str + ","); + } + System.out.println(); + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class14/Code01_Light.java b/体系学习班/class14/Code01_Light.java new file mode 100644 index 0000000..7cbfde2 --- /dev/null +++ b/体系学习班/class14/Code01_Light.java @@ -0,0 +1,105 @@ +package class14; + +import java.util.HashSet; + +public class Code01_Light { + + public static int minLight1(String road) { + if (road == null || road.length() == 0) { + return 0; + } + return process(road.toCharArray(), 0, new HashSet<>()); + } + + // str[index....]位置,自由选择放灯还是不放灯 + // str[0..index-1]位置呢?已经做完决定了,那些放了灯的位置,存在lights里 + // 要求选出能照亮所有.的方案,并且在这些有效的方案中,返回最少需要几个灯 + public static int process(char[] str, int index, HashSet lights) { + if (index == str.length) { // 结束的时候 + for (int i = 0; i < str.length; i++) { + if (str[i] != 'X') { // 当前位置是点的话 + if (!lights.contains(i - 1) && !lights.contains(i) && !lights.contains(i + 1)) { + return Integer.MAX_VALUE; + } + } + } + return lights.size(); + } else { // str还没结束 + // i X . + int no = process(str, index + 1, lights); + int yes = Integer.MAX_VALUE; + if (str[index] == '.') { + lights.add(index); + yes = process(str, index + 1, lights); + lights.remove(index); + } + return Math.min(no, yes); + } + } + + public static int minLight2(String road) { + char[] str = road.toCharArray(); + int i = 0; + int light = 0; + while (i < str.length) { + if (str[i] == 'X') { + i++; + } else { + light++; + if (i + 1 == str.length) { + break; + } else { // 有i位置 i+ 1 X . + if (str[i + 1] == 'X') { + i = i + 2; + } else { + i = i + 3; + } + } + } + } + return light; + } + + // 更简洁的解法 + // 两个X之间,数一下.的数量,然后除以3,向上取整 + // 把灯数累加 + public static int minLight3(String road) { + char[] str = road.toCharArray(); + int cur = 0; + int light = 0; + for (char c : str) { + if (c == 'X') { + light += (cur + 2) / 3; + cur = 0; + } else { + cur++; + } + } + light += (cur + 2) / 3; + return light; + } + + // for test + public static String randomString(int len) { + char[] res = new char[(int) (Math.random() * len) + 1]; + for (int i = 0; i < res.length; i++) { + res[i] = Math.random() < 0.5 ? 'X' : '.'; + } + return String.valueOf(res); + } + + public static void main(String[] args) { + int len = 20; + int testTime = 100000; + for (int i = 0; i < testTime; i++) { + String test = randomString(len); + int ans1 = minLight1(test); + int ans2 = minLight2(test); + int ans3 = minLight3(test); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("oops!"); + } + } + System.out.println("finish!"); + } +} diff --git a/体系学习班/class14/Code02_LessMoneySplitGold.java b/体系学习班/class14/Code02_LessMoneySplitGold.java new file mode 100644 index 0000000..682fa12 --- /dev/null +++ b/体系学习班/class14/Code02_LessMoneySplitGold.java @@ -0,0 +1,79 @@ +package class14; + +import java.util.PriorityQueue; + +public class Code02_LessMoneySplitGold { + + // 纯暴力! + public static int lessMoney1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + return process(arr, 0); + } + + // 等待合并的数都在arr里,pre之前的合并行为产生了多少总代价 + // arr中只剩一个数字的时候,停止合并,返回最小的总代价 + public static int process(int[] arr, int pre) { + if (arr.length == 1) { + return pre; + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + ans = Math.min(ans, process(copyAndMergeTwo(arr, i, j), pre + arr[i] + arr[j])); + } + } + return ans; + } + + public static int[] copyAndMergeTwo(int[] arr, int i, int j) { + int[] ans = new int[arr.length - 1]; + int ansi = 0; + for (int arri = 0; arri < arr.length; arri++) { + if (arri != i && arri != j) { + ans[ansi++] = arr[arri]; + } + } + ans[ansi] = arr[i] + arr[j]; + return ans; + } + + public static int lessMoney2(int[] arr) { + PriorityQueue pQ = new PriorityQueue<>(); + for (int i = 0; i < arr.length; i++) { + pQ.add(arr[i]); + } + int sum = 0; + int cur = 0; + while (pQ.size() > 1) { + cur = pQ.poll() + pQ.poll(); + sum += cur; + pQ.add(cur); + } + return sum; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * (maxValue + 1)); + } + return arr; + } + + public static void main(String[] args) { + int testTime = 100000; + int maxSize = 6; + int maxValue = 1000; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + if (lessMoney1(arr) != lessMoney2(arr)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class14/Code03_BestArrange.java b/体系学习班/class14/Code03_BestArrange.java new file mode 100644 index 0000000..c8a79ff --- /dev/null +++ b/体系学习班/class14/Code03_BestArrange.java @@ -0,0 +1,111 @@ +package class14; + +import java.util.Arrays; +import java.util.Comparator; + +public class Code03_BestArrange { + + public static class Program { + public int start; + public int end; + + public Program(int start, int end) { + this.start = start; + this.end = end; + } + } + + // 暴力!所有情况都尝试! + public static int bestArrange1(Program[] programs) { + if (programs == null || programs.length == 0) { + return 0; + } + return process(programs, 0, 0); + } + + // 还剩下的会议都放在programs里 + // done之前已经安排了多少会议的数量 + // timeLine目前来到的时间点是什么 + + // 目前来到timeLine的时间点,已经安排了done多的会议,剩下的会议programs可以自由安排 + // 返回能安排的最多会议数量 + public static int process(Program[] programs, int done, int timeLine) { + if (programs.length == 0) { + return done; + } + // 还剩下会议 + int max = done; + // 当前安排的会议是什么会,每一个都枚举 + for (int i = 0; i < programs.length; i++) { + if (programs[i].start >= timeLine) { + Program[] next = copyButExcept(programs, i); + max = Math.max(max, process(next, done + 1, programs[i].end)); + } + } + return max; + } + + public static Program[] copyButExcept(Program[] programs, int i) { + Program[] ans = new Program[programs.length - 1]; + int index = 0; + for (int k = 0; k < programs.length; k++) { + if (k != i) { + ans[index++] = programs[k]; + } + } + return ans; + } + + // 会议的开始时间和结束时间,都是数值,不会 < 0 + public static int bestArrange2(Program[] programs) { + Arrays.sort(programs, new ProgramComparator()); + int timeLine = 0; + int result = 0; + // 依次遍历每一个会议,结束时间早的会议先遍历 + for (int i = 0; i < programs.length; i++) { + if (timeLine <= programs[i].start) { + result++; + timeLine = programs[i].end; + } + } + return result; + } + + public static class ProgramComparator implements Comparator { + + @Override + public int compare(Program o1, Program o2) { + return o1.end - o2.end; + } + + } + + // for test + public static Program[] generatePrograms(int programSize, int timeMax) { + Program[] ans = new Program[(int) (Math.random() * (programSize + 1))]; + for (int i = 0; i < ans.length; i++) { + int r1 = (int) (Math.random() * (timeMax + 1)); + int r2 = (int) (Math.random() * (timeMax + 1)); + if (r1 == r2) { + ans[i] = new Program(r1, r1 + 1); + } else { + ans[i] = new Program(Math.min(r1, r2), Math.max(r1, r2)); + } + } + return ans; + } + + public static void main(String[] args) { + int programSize = 12; + int timeMax = 20; + int timeTimes = 1000000; + for (int i = 0; i < timeTimes; i++) { + Program[] programs = generatePrograms(programSize, timeMax); + if (bestArrange1(programs) != bestArrange2(programs)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/体系学习班/class14/Code04_IPO.java b/体系学习班/class14/Code04_IPO.java new file mode 100644 index 0000000..0ac3b85 --- /dev/null +++ b/体系学习班/class14/Code04_IPO.java @@ -0,0 +1,58 @@ +package class14; + +import java.util.Comparator; +import java.util.PriorityQueue; + +public class Code04_IPO { + + // 最多K个项目 + // W是初始资金 + // Profits[] Capital[] 一定等长 + // 返回最终最大的资金 + public static int findMaximizedCapital(int K, int W, int[] Profits, int[] Capital) { + PriorityQueue minCostQ = new PriorityQueue<>(new MinCostComparator()); + PriorityQueue maxProfitQ = new PriorityQueue<>(new MaxProfitComparator()); + for (int i = 0; i < Profits.length; i++) { + minCostQ.add(new Program(Profits[i], Capital[i])); + } + for (int i = 0; i < K; i++) { + while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) { + maxProfitQ.add(minCostQ.poll()); + } + if (maxProfitQ.isEmpty()) { + return W; + } + W += maxProfitQ.poll().p; + } + return W; + } + + public static class Program { + public int p; + public int c; + + public Program(int p, int c) { + this.p = p; + this.c = c; + } + } + + public static class MinCostComparator implements Comparator { + + @Override + public int compare(Program o1, Program o2) { + return o1.c - o2.c; + } + + } + + public static class MaxProfitComparator implements Comparator { + + @Override + public int compare(Program o1, Program o2) { + return o2.p - o1.p; + } + + } + +} diff --git a/体系学习班/class14/Code05_UnionFind.java b/体系学习班/class14/Code05_UnionFind.java new file mode 100644 index 0000000..0d9e246 --- /dev/null +++ b/体系学习班/class14/Code05_UnionFind.java @@ -0,0 +1,70 @@ +package class14; + +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class Code05_UnionFind { + + public static class Node { + V value; + + public Node(V v) { + value = v; + } + } + + public static class UnionFind { + public HashMap> nodes; + public HashMap, Node> parents; + public HashMap, Integer> sizeMap; + + public UnionFind(List values) { + nodes = new HashMap<>(); + parents = new HashMap<>(); + sizeMap = new HashMap<>(); + for (V cur : values) { + Node node = new Node<>(cur); + nodes.put(cur, node); + parents.put(node, node); + sizeMap.put(node, 1); + } + } + + // 给你一个节点,请你往上到不能再往上,把代表返回 + public Node findFather(Node cur) { + Stack> path = new Stack<>(); + while (cur != parents.get(cur)) { + path.push(cur); + cur = parents.get(cur); + } + while (!path.isEmpty()) { + parents.put(path.pop(), cur); + } + return cur; + } + + public boolean isSameSet(V a, V b) { + return findFather(nodes.get(a)) == findFather(nodes.get(b)); + } + + public void union(V a, V b) { + Node aHead = findFather(nodes.get(a)); + Node bHead = findFather(nodes.get(b)); + if (aHead != bHead) { + int aSetSize = sizeMap.get(aHead); + int bSetSize = sizeMap.get(bHead); + Node big = aSetSize >= bSetSize ? aHead : bHead; + Node small = big == aHead ? bHead : aHead; + parents.put(small, big); + sizeMap.put(big, aSetSize + bSetSize); + sizeMap.remove(small); + } + } + + public int sets() { + return sizeMap.size(); + } + + } +} diff --git a/体系学习班/class15/Code01_FriendCircles.java b/体系学习班/class15/Code01_FriendCircles.java new file mode 100644 index 0000000..dd4253d --- /dev/null +++ b/体系学习班/class15/Code01_FriendCircles.java @@ -0,0 +1,78 @@ +package class15; + +// 本题为leetcode原题 +// 测试链接:https://leetcode.com/problems/friend-circles/ +// 可以直接通过 +public class Code01_FriendCircles { + + public static int findCircleNum(int[][] M) { + int N = M.length; + // {0} {1} {2} {N-1} + UnionFind unionFind = new UnionFind(N); + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + if (M[i][j] == 1) { // i和j互相认识 + unionFind.union(i, j); + } + } + } + return unionFind.sets(); + } + + public static class UnionFind { + // parent[i] = k : i的父亲是k + private int[] parent; + // size[i] = k : 如果i是代表节点,size[i]才有意义,否则无意义 + // i所在的集合大小是多少 + private int[] size; + // 辅助结构 + private int[] help; + // 一共有多少个集合 + private int sets; + + public UnionFind(int N) { + parent = new int[N]; + size = new int[N]; + help = new int[N]; + sets = N; + for (int i = 0; i < N; i++) { + parent[i] = i; + size[i] = 1; + } + } + + // 从i开始一直往上,往上到不能再往上,代表节点,返回 + // 这个过程要做路径压缩 + private int find(int i) { + int hi = 0; + while (i != parent[i]) { + help[hi++] = i; + i = parent[i]; + } + for (hi--; hi >= 0; hi--) { + parent[help[hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int f1 = find(i); + int f2 = find(j); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + size[f1] += size[f2]; + parent[f2] = f1; + } else { + size[f2] += size[f1]; + parent[f1] = f2; + } + sets--; + } + } + + public int sets() { + return sets; + } + } + +} diff --git a/体系学习班/class15/Code02_NumberOfIslands.java b/体系学习班/class15/Code02_NumberOfIslands.java new file mode 100644 index 0000000..6065d42 --- /dev/null +++ b/体系学习班/class15/Code02_NumberOfIslands.java @@ -0,0 +1,317 @@ +package class15; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +// 本题为leetcode原题 +// 测试链接:https://leetcode.com/problems/number-of-islands/ +// 所有方法都可以直接通过 +public class Code02_NumberOfIslands { + + public static int numIslands3(char[][] board) { + int islands = 0; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == '1') { + islands++; + infect(board, i, j); + } + } + } + return islands; + } + + // 从(i,j)这个位置出发,把所有练成一片的'1'字符,变成0 + public static void infect(char[][] board, int i, int j) { + if (i < 0 || i == board.length || j < 0 || j == board[0].length || board[i][j] != '1') { + return; + } + board[i][j] = 0; + infect(board, i - 1, j); + infect(board, i + 1, j); + infect(board, i, j - 1); + infect(board, i, j + 1); + } + + public static int numIslands1(char[][] board) { + int row = board.length; + int col = board[0].length; + Dot[][] dots = new Dot[row][col]; + List dotList = new ArrayList<>(); + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (board[i][j] == '1') { + dots[i][j] = new Dot(); + dotList.add(dots[i][j]); + } + } + } + UnionFind1 uf = new UnionFind1<>(dotList); + for (int j = 1; j < col; j++) { + // (0,j) (0,0)跳过了 (0,1) (0,2) (0,3) + if (board[0][j - 1] == '1' && board[0][j] == '1') { + uf.union(dots[0][j - 1], dots[0][j]); + } + } + for (int i = 1; i < row; i++) { + if (board[i - 1][0] == '1' && board[i][0] == '1') { + uf.union(dots[i - 1][0], dots[i][0]); + } + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + if (board[i][j] == '1') { + if (board[i][j - 1] == '1') { + uf.union(dots[i][j - 1], dots[i][j]); + } + if (board[i - 1][j] == '1') { + uf.union(dots[i - 1][j], dots[i][j]); + } + } + } + } + return uf.sets(); + } + + public static class Dot { + + } + + public static class Node { + + V value; + + public Node(V v) { + value = v; + } + + } + + public static class UnionFind1 { + public HashMap> nodes; + public HashMap, Node> parents; + public HashMap, Integer> sizeMap; + + public UnionFind1(List values) { + nodes = new HashMap<>(); + parents = new HashMap<>(); + sizeMap = new HashMap<>(); + for (V cur : values) { + Node node = new Node<>(cur); + nodes.put(cur, node); + parents.put(node, node); + sizeMap.put(node, 1); + } + } + + public Node findFather(Node cur) { + Stack> path = new Stack<>(); + while (cur != parents.get(cur)) { + path.push(cur); + cur = parents.get(cur); + } + while (!path.isEmpty()) { + parents.put(path.pop(), cur); + } + return cur; + } + + public void union(V a, V b) { + Node aHead = findFather(nodes.get(a)); + Node bHead = findFather(nodes.get(b)); + if (aHead != bHead) { + int aSetSize = sizeMap.get(aHead); + int bSetSize = sizeMap.get(bHead); + Node big = aSetSize >= bSetSize ? aHead : bHead; + Node small = big == aHead ? bHead : aHead; + parents.put(small, big); + sizeMap.put(big, aSetSize + bSetSize); + sizeMap.remove(small); + } + } + + public int sets() { + return sizeMap.size(); + } + + } + + public static int numIslands2(char[][] board) { + int row = board.length; + int col = board[0].length; + UnionFind2 uf = new UnionFind2(board); + for (int j = 1; j < col; j++) { + if (board[0][j - 1] == '1' && board[0][j] == '1') { + uf.union(0, j - 1, 0, j); + } + } + for (int i = 1; i < row; i++) { + if (board[i - 1][0] == '1' && board[i][0] == '1') { + uf.union(i - 1, 0, i, 0); + } + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + if (board[i][j] == '1') { + if (board[i][j - 1] == '1') { + uf.union(i, j - 1, i, j); + } + if (board[i - 1][j] == '1') { + uf.union(i - 1, j, i, j); + } + } + } + } + return uf.sets(); + } + + public static class UnionFind2 { + private int[] parent; + private int[] size; + private int[] help; + private int col; + private int sets; + + public UnionFind2(char[][] board) { + col = board[0].length; + sets = 0; + int row = board.length; + int len = row * col; + parent = new int[len]; + size = new int[len]; + help = new int[len]; + for (int r = 0; r < row; r++) { + for (int c = 0; c < col; c++) { + if (board[r][c] == '1') { + int i = index(r, c); + parent[i] = i; + size[i] = 1; + sets++; + } + } + } + } + + // (r,c) -> i + private int index(int r, int c) { + return r * col + c; + } + + // 原始位置 -> 下标 + private int find(int i) { + int hi = 0; + while (i != parent[i]) { + help[hi++] = i; + i = parent[i]; + } + for (hi--; hi >= 0; hi--) { + parent[help[hi]] = i; + } + return i; + } + + public void union(int r1, int c1, int r2, int c2) { + int i1 = index(r1, c1); + int i2 = index(r2, c2); + int f1 = find(i1); + int f2 = find(i2); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + size[f1] += size[f2]; + parent[f2] = f1; + } else { + size[f2] += size[f1]; + parent[f1] = f2; + } + sets--; + } + } + + public int sets() { + return sets; + } + + } + + // 为了测试 + public static char[][] generateRandomMatrix(int row, int col) { + char[][] board = new char[row][col]; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + board[i][j] = Math.random() < 0.5 ? '1' : '0'; + } + } + return board; + } + + // 为了测试 + public static char[][] copy(char[][] board) { + int row = board.length; + int col = board[0].length; + char[][] ans = new char[row][col]; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + ans[i][j] = board[i][j]; + } + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int row = 0; + int col = 0; + char[][] board1 = null; + char[][] board2 = null; + char[][] board3 = null; + long start = 0; + long end = 0; + + row = 1000; + col = 1000; + board1 = generateRandomMatrix(row, col); + board2 = copy(board1); + board3 = copy(board1); + + System.out.println("感染方法、并查集(map实现)、并查集(数组实现)的运行结果和运行时间"); + System.out.println("随机生成的二维矩阵规模 : " + row + " * " + col); + + start = System.currentTimeMillis(); + System.out.println("感染方法的运行结果: " + numIslands3(board1)); + end = System.currentTimeMillis(); + System.out.println("感染方法的运行时间: " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + System.out.println("并查集(map实现)的运行结果: " + numIslands1(board2)); + end = System.currentTimeMillis(); + System.out.println("并查集(map实现)的运行时间: " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行结果: " + numIslands2(board3)); + end = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行时间: " + (end - start) + " ms"); + + System.out.println(); + + row = 10000; + col = 10000; + board1 = generateRandomMatrix(row, col); + board3 = copy(board1); + System.out.println("感染方法、并查集(数组实现)的运行结果和运行时间"); + System.out.println("随机生成的二维矩阵规模 : " + row + " * " + col); + + start = System.currentTimeMillis(); + System.out.println("感染方法的运行结果: " + numIslands3(board1)); + end = System.currentTimeMillis(); + System.out.println("感染方法的运行时间: " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行结果: " + numIslands2(board3)); + end = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行时间: " + (end - start) + " ms"); + + } + +} diff --git a/体系学习班/class15/Code03_NumberOfIslandsII.java b/体系学习班/class15/Code03_NumberOfIslandsII.java new file mode 100644 index 0000000..d109276 --- /dev/null +++ b/体系学习班/class15/Code03_NumberOfIslandsII.java @@ -0,0 +1,165 @@ +package class15; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +// 本题为leetcode原题 +// 测试链接:https://leetcode.com/problems/number-of-islands-ii/ +// 所有方法都可以直接通过 +public class Code03_NumberOfIslandsII { + + public static List numIslands21(int m, int n, int[][] positions) { + UnionFind1 uf = new UnionFind1(m, n); + List ans = new ArrayList<>(); + for (int[] position : positions) { + ans.add(uf.connect(position[0], position[1])); + } + return ans; + } + + public static class UnionFind1 { + private int[] parent; + private int[] size; + private int[] help; + private final int row; + private final int col; + private int sets; + + public UnionFind1(int m, int n) { + row = m; + col = n; + sets = 0; + int len = row * col; + parent = new int[len]; + size = new int[len]; + help = new int[len]; + } + + private int index(int r, int c) { + return r * col + c; + } + + private int find(int i) { + int hi = 0; + while (i != parent[i]) { + help[hi++] = i; + i = parent[i]; + } + for (hi--; hi >= 0; hi--) { + parent[help[hi]] = i; + } + return i; + } + + private void union(int r1, int c1, int r2, int c2) { + if (r1 < 0 || r1 == row || r2 < 0 || r2 == row || c1 < 0 || c1 == col || c2 < 0 || c2 == col) { + return; + } + int i1 = index(r1, c1); + int i2 = index(r2, c2); + if (size[i1] == 0 || size[i2] == 0) { + return; + } + int f1 = find(i1); + int f2 = find(i2); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + size[f1] += size[f2]; + parent[f2] = f1; + } else { + size[f2] += size[f1]; + parent[f1] = f2; + } + sets--; + } + } + + public int connect(int r, int c) { + int index = index(r, c); + if (size[index] == 0) { + parent[index] = index; + size[index] = 1; + sets++; + union(r - 1, c, r, c); + union(r + 1, c, r, c); + union(r, c - 1, r, c); + union(r, c + 1, r, c); + } + return sets; + } + + } + + // 课上讲的如果m*n比较大,会经历很重的初始化,而k比较小,怎么优化的方法 + public static List numIslands22(int m, int n, int[][] positions) { + UnionFind2 uf = new UnionFind2(); + List ans = new ArrayList<>(); + for (int[] position : positions) { + ans.add(uf.connect(position[0], position[1])); + } + return ans; + } + + public static class UnionFind2 { + private HashMap parent; + private HashMap size; + private ArrayList help; + private int sets; + + public UnionFind2() { + parent = new HashMap<>(); + size = new HashMap<>(); + help = new ArrayList<>(); + sets = 0; + } + + private String find(String cur) { + while (!cur.equals(parent.get(cur))) { + help.add(cur); + cur = parent.get(cur); + } + for (String str : help) { + parent.put(str, cur); + } + help.clear(); + return cur; + } + + private void union(String s1, String s2) { + if (parent.containsKey(s1) && parent.containsKey(s2)) { + String f1 = find(s1); + String f2 = find(s2); + if (!f1.equals(f2)) { + int size1 = size.get(f1); + int size2 = size.get(f2); + String big = size1 >= size2 ? f1 : f2; + String small = big == f1 ? f2 : f1; + parent.put(small, big); + size.put(big, size1 + size2); + sets--; + } + } + } + + public int connect(int r, int c) { + String key = String.valueOf(r) + "_" + String.valueOf(c); + if (!parent.containsKey(key)) { + parent.put(key, key); + size.put(key, 1); + sets++; + String up = String.valueOf(r - 1) + "_" + String.valueOf(c); + String down = String.valueOf(r + 1) + "_" + String.valueOf(c); + String left = String.valueOf(r) + "_" + String.valueOf(c - 1); + String right = String.valueOf(r) + "_" + String.valueOf(c + 1); + union(up, key); + union(down, key); + union(left, key); + union(right, key); + } + return sets; + } + + } + +} diff --git a/体系学习班/class16/Code01_BFS.java b/体系学习班/class16/Code01_BFS.java new file mode 100644 index 0000000..c2d4d9f --- /dev/null +++ b/体系学习班/class16/Code01_BFS.java @@ -0,0 +1,30 @@ +package class16; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; + +public class Code01_BFS { + + // 从node出发,进行宽度优先遍历 + public static void bfs(Node start) { + if (start == null) { + return; + } + Queue queue = new LinkedList<>(); + HashSet set = new HashSet<>(); + queue.add(start); + set.add(start); + while (!queue.isEmpty()) { + Node cur = queue.poll(); + System.out.println(cur.value); + for (Node next : cur.nexts) { + if (!set.contains(next)) { + set.add(next); + queue.add(next); + } + } + } + } + +} diff --git a/体系学习班/class16/Code02_DFS.java b/体系学习班/class16/Code02_DFS.java new file mode 100644 index 0000000..fa85ca4 --- /dev/null +++ b/体系学习班/class16/Code02_DFS.java @@ -0,0 +1,34 @@ +package class16; + +import java.util.HashSet; +import java.util.Stack; + +public class Code02_DFS { + + public static void dfs(Node node) { + if (node == null) { + return; + } + Stack stack = new Stack<>(); + HashSet set = new HashSet<>(); + stack.add(node); + set.add(node); + System.out.println(node.value); + while (!stack.isEmpty()) { + Node cur = stack.pop(); + for (Node next : cur.nexts) { + if (!set.contains(next)) { + stack.push(cur); + stack.push(next); + set.add(next); + System.out.println(next.value); + break; + } + } + } + } + + + + +} diff --git a/体系学习班/class16/Code03_TopologicalOrderBFS.java b/体系学习班/class16/Code03_TopologicalOrderBFS.java new file mode 100644 index 0000000..5e54581 --- /dev/null +++ b/体系学习班/class16/Code03_TopologicalOrderBFS.java @@ -0,0 +1,53 @@ +package class16; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; + +// OJ链接:https://www.lintcode.com/problem/topological-sorting +public class Code03_TopologicalOrderBFS { + + // 不要提交这个类 + public static class DirectedGraphNode { + public int label; + public ArrayList neighbors; + + public DirectedGraphNode(int x) { + label = x; + neighbors = new ArrayList(); + } + } + + // 提交下面的 + public static ArrayList topSort(ArrayList graph) { + HashMap indegreeMap = new HashMap<>(); + for (DirectedGraphNode cur : graph) { + indegreeMap.put(cur, 0); + } + for (DirectedGraphNode cur : graph) { + for (DirectedGraphNode next : cur.neighbors) { + indegreeMap.put(next, indegreeMap.get(next) + 1); + } + } + Queue zeroQueue = new LinkedList<>(); + for (DirectedGraphNode cur : indegreeMap.keySet()) { + if (indegreeMap.get(cur) == 0) { + zeroQueue.add(cur); + } + } + ArrayList ans = new ArrayList<>(); + while (!zeroQueue.isEmpty()) { + DirectedGraphNode cur = zeroQueue.poll(); + ans.add(cur); + for (DirectedGraphNode next : cur.neighbors) { + indegreeMap.put(next, indegreeMap.get(next) - 1); + if (indegreeMap.get(next) == 0) { + zeroQueue.offer(next); + } + } + } + return ans; + } + +} diff --git a/体系学习班/class16/Code03_TopologicalOrderDFS1.java b/体系学习班/class16/Code03_TopologicalOrderDFS1.java new file mode 100644 index 0000000..57f1bb7 --- /dev/null +++ b/体系学习班/class16/Code03_TopologicalOrderDFS1.java @@ -0,0 +1,70 @@ +package class16; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; + +// OJ链接:https://www.lintcode.com/problem/topological-sorting +public class Code03_TopologicalOrderDFS1 { + + // 不要提交这个类 + public static class DirectedGraphNode { + public int label; + public ArrayList neighbors; + + public DirectedGraphNode(int x) { + label = x; + neighbors = new ArrayList(); + } + } + + // 提交下面的 + public static class Record { + public DirectedGraphNode node; + public int deep; + + public Record(DirectedGraphNode n, int o) { + node = n; + deep = o; + } + } + + public static class MyComparator implements Comparator { + + @Override + public int compare(Record o1, Record o2) { + return o2.deep - o1.deep; + } + } + + public static ArrayList topSort(ArrayList graph) { + HashMap order = new HashMap<>(); + for (DirectedGraphNode cur : graph) { + f(cur, order); + } + ArrayList recordArr = new ArrayList<>(); + for (Record r : order.values()) { + recordArr.add(r); + } + recordArr.sort(new MyComparator()); + ArrayList ans = new ArrayList(); + for (Record r : recordArr) { + ans.add(r.node); + } + return ans; + } + + public static Record f(DirectedGraphNode cur, HashMap order) { + if (order.containsKey(cur)) { + return order.get(cur); + } + int follow = 0; + for (DirectedGraphNode next : cur.neighbors) { + follow = Math.max(follow, f(next, order).deep); + } + Record ans = new Record(cur, follow + 1); + order.put(cur, ans); + return ans; + } + +} diff --git a/体系学习班/class16/Code03_TopologicalOrderDFS2.java b/体系学习班/class16/Code03_TopologicalOrderDFS2.java new file mode 100644 index 0000000..4c90d1d --- /dev/null +++ b/体系学习班/class16/Code03_TopologicalOrderDFS2.java @@ -0,0 +1,76 @@ +package class16; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; + +// OJ链接:https://www.lintcode.com/problem/topological-sorting +public class Code03_TopologicalOrderDFS2 { + + // 不要提交这个类 + public static class DirectedGraphNode { + public int label; + public ArrayList neighbors; + + public DirectedGraphNode(int x) { + label = x; + neighbors = new ArrayList(); + } + } + + // 提交下面的 + public static class Record { + public DirectedGraphNode node; + public long nodes; + + public Record(DirectedGraphNode n, long o) { + node = n; + nodes = o; + } + } + + public static class MyComparator implements Comparator { + + @Override + public int compare(Record o1, Record o2) { + return o1.nodes == o2.nodes ? 0 : (o1.nodes > o2.nodes ? -1 : 1); + } + } + + public static ArrayList topSort(ArrayList graph) { + HashMap order = new HashMap<>(); + for (DirectedGraphNode cur : graph) { + f(cur, order); + } + ArrayList recordArr = new ArrayList<>(); + for (Record r : order.values()) { + recordArr.add(r); + } + recordArr.sort(new MyComparator()); + ArrayList ans = new ArrayList(); + for (Record r : recordArr) { + ans.add(r.node); + } + return ans; + } + + // 当前来到cur点,请返回cur点所到之处,所有的点次! + // 返回(cur,点次) + // 缓存!!!!!order + // key : 某一个点的点次,之前算过了! + // value : 点次是多少 + public static Record f(DirectedGraphNode cur, HashMap order) { + if (order.containsKey(cur)) { + return order.get(cur); + } + // cur的点次之前没算过! + long nodes = 0; + for (DirectedGraphNode next : cur.neighbors) { + nodes += f(next, order).nodes; + } + Record ans = new Record(cur, nodes + 1); + order.put(cur, ans); + return ans; + } + +} diff --git a/体系学习班/class16/Code03_TopologySort.java b/体系学习班/class16/Code03_TopologySort.java new file mode 100644 index 0000000..735dca0 --- /dev/null +++ b/体系学习班/class16/Code03_TopologySort.java @@ -0,0 +1,36 @@ +package class16; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class Code03_TopologySort { + + // directed graph and no loop + public static List sortedTopology(Graph graph) { + // key 某个节点 value 剩余的入度 + HashMap inMap = new HashMap<>(); + // 只有剩余入度为0的点,才进入这个队列 + Queue zeroInQueue = new LinkedList<>(); + for (Node node : graph.nodes.values()) { + inMap.put(node, node.in); + if (node.in == 0) { + zeroInQueue.add(node); + } + } + List result = new ArrayList<>(); + while (!zeroInQueue.isEmpty()) { + Node cur = zeroInQueue.poll(); + result.add(cur); + for (Node next : cur.nexts) { + inMap.put(next, inMap.get(next) - 1); + if (inMap.get(next) == 0) { + zeroInQueue.add(next); + } + } + } + return result; + } +} diff --git a/体系学习班/class16/Code04_Kruskal.java b/体系学习班/class16/Code04_Kruskal.java new file mode 100644 index 0000000..cb0ea62 --- /dev/null +++ b/体系学习班/class16/Code04_Kruskal.java @@ -0,0 +1,101 @@ +package class16; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.PriorityQueue; +import java.util.Set; +import java.util.Stack; + +//undirected graph only +public class Code04_Kruskal { + + // Union-Find Set + public static class UnionFind { + // key 某一个节点, value key节点往上的节点 + private HashMap fatherMap; + // key 某一个集合的代表节点, value key所在集合的节点个数 + private HashMap sizeMap; + + public UnionFind() { + fatherMap = new HashMap(); + sizeMap = new HashMap(); + } + + public void makeSets(Collection nodes) { + fatherMap.clear(); + sizeMap.clear(); + for (Node node : nodes) { + fatherMap.put(node, node); + sizeMap.put(node, 1); + } + } + + private Node findFather(Node n) { + Stack path = new Stack<>(); + while(n != fatherMap.get(n)) { + path.add(n); + n = fatherMap.get(n); + } + while(!path.isEmpty()) { + fatherMap.put(path.pop(), n); + } + return n; + } + + public boolean isSameSet(Node a, Node b) { + return findFather(a) == findFather(b); + } + + public void union(Node a, Node b) { + if (a == null || b == null) { + return; + } + Node aDai = findFather(a); + Node bDai = findFather(b); + if (aDai != bDai) { + int aSetSize = sizeMap.get(aDai); + int bSetSize = sizeMap.get(bDai); + if (aSetSize <= bSetSize) { + fatherMap.put(aDai, bDai); + sizeMap.put(bDai, aSetSize + bSetSize); + sizeMap.remove(aDai); + } else { + fatherMap.put(bDai, aDai); + sizeMap.put(aDai, aSetSize + bSetSize); + sizeMap.remove(bDai); + } + } + } + } + + + public static class EdgeComparator implements Comparator { + + @Override + public int compare(Edge o1, Edge o2) { + return o1.weight - o2.weight; + } + + } + + public static Set kruskalMST(Graph graph) { + UnionFind unionFind = new UnionFind(); + unionFind.makeSets(graph.nodes.values()); + // 从小的边到大的边,依次弹出,小根堆! + PriorityQueue priorityQueue = new PriorityQueue<>(new EdgeComparator()); + for (Edge edge : graph.edges) { // M 条边 + priorityQueue.add(edge); // O(logM) + } + Set result = new HashSet<>(); + while (!priorityQueue.isEmpty()) { // M 条边 + Edge edge = priorityQueue.poll(); // O(logM) + if (!unionFind.isSameSet(edge.from, edge.to)) { // O(1) + result.add(edge); + unionFind.union(edge.from, edge.to); + } + } + return result; + } +} diff --git a/体系学习班/class16/Code05_Prim.java b/体系学习班/class16/Code05_Prim.java new file mode 100644 index 0000000..d00dc75 --- /dev/null +++ b/体系学习班/class16/Code05_Prim.java @@ -0,0 +1,94 @@ +package class16; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.PriorityQueue; +import java.util.Set; + +// undirected graph only +public class Code05_Prim { + + public static class EdgeComparator implements Comparator { + + @Override + public int compare(Edge o1, Edge o2) { + return o1.weight - o2.weight; + } + + } + + public static Set primMST(Graph graph) { + // 解锁的边进入小根堆 + PriorityQueue priorityQueue = new PriorityQueue<>(new EdgeComparator()); + + // 哪些点被解锁出来了 + HashSet nodeSet = new HashSet<>(); + + + + Set result = new HashSet<>(); // 依次挑选的的边在result里 + + for (Node node : graph.nodes.values()) { // 随便挑了一个点 + // node 是开始点 + if (!nodeSet.contains(node)) { + nodeSet.add(node); + for (Edge edge : node.edges) { // 由一个点,解锁所有相连的边 + priorityQueue.add(edge); + } + while (!priorityQueue.isEmpty()) { + Edge edge = priorityQueue.poll(); // 弹出解锁的边中,最小的边 + Node toNode = edge.to; // 可能的一个新的点 + if (!nodeSet.contains(toNode)) { // 不含有的时候,就是新的点 + nodeSet.add(toNode); + result.add(edge); + for (Edge nextEdge : toNode.edges) { + priorityQueue.add(nextEdge); + } + } + } + } + // break; + } + return result; + } + + // 请保证graph是连通图 + // graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路 + // 返回值是最小连通图的路径之和 + public static int prim(int[][] graph) { + int size = graph.length; + int[] distances = new int[size]; + boolean[] visit = new boolean[size]; + visit[0] = true; + for (int i = 0; i < size; i++) { + distances[i] = graph[0][i]; + } + int sum = 0; + for (int i = 1; i < size; i++) { + int minPath = Integer.MAX_VALUE; + int minIndex = -1; + for (int j = 0; j < size; j++) { + if (!visit[j] && distances[j] < minPath) { + minPath = distances[j]; + minIndex = j; + } + } + if (minIndex == -1) { + return sum; + } + visit[minIndex] = true; + sum += minPath; + for (int j = 0; j < size; j++) { + if (!visit[j] && distances[j] > graph[minIndex][j]) { + distances[j] = graph[minIndex][j]; + } + } + } + return sum; + } + + public static void main(String[] args) { + System.out.println("hello world!"); + } + +} diff --git a/体系学习班/class16/Code06_Dijkstra.java b/体系学习班/class16/Code06_Dijkstra.java new file mode 100644 index 0000000..2b819c6 --- /dev/null +++ b/体系学习班/class16/Code06_Dijkstra.java @@ -0,0 +1,160 @@ +package class16; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map.Entry; + +// no negative weight +public class Code06_Dijkstra { + + public static HashMap dijkstra1(Node from) { + HashMap distanceMap = new HashMap<>(); + distanceMap.put(from, 0); + // 打过对号的点 + HashSet selectedNodes = new HashSet<>(); + Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); + while (minNode != null) { + // 原始点 -> minNode(跳转点) 最小距离distance + int distance = distanceMap.get(minNode); + for (Edge edge : minNode.edges) { + Node toNode = edge.to; + if (!distanceMap.containsKey(toNode)) { + distanceMap.put(toNode, distance + edge.weight); + } else { // toNode + distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight)); + } + } + selectedNodes.add(minNode); + minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); + } + return distanceMap; + } + + public static Node getMinDistanceAndUnselectedNode(HashMap distanceMap, HashSet touchedNodes) { + Node minNode = null; + int minDistance = Integer.MAX_VALUE; + for (Entry entry : distanceMap.entrySet()) { + Node node = entry.getKey(); + int distance = entry.getValue(); + if (!touchedNodes.contains(node) && distance < minDistance) { + minNode = node; + minDistance = distance; + } + } + return minNode; + } + + public static class NodeRecord { + public Node node; + public int distance; + + public NodeRecord(Node node, int distance) { + this.node = node; + this.distance = distance; + } + } + + public static class NodeHeap { + private Node[] nodes; // 实际的堆结构 + // key 某一个node, value 上面堆中的位置 + private HashMap heapIndexMap; + // key 某一个节点, value 从源节点出发到该节点的目前最小距离 + private HashMap distanceMap; + private int size; // 堆上有多少个点 + + public NodeHeap(int size) { + nodes = new Node[size]; + heapIndexMap = new HashMap<>(); + distanceMap = new HashMap<>(); + size = 0; + } + + public boolean isEmpty() { + return size == 0; + } + + // 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance + // 判断要不要更新,如果需要的话,就更新 + public void addOrUpdateOrIgnore(Node node, int distance) { + if (inHeap(node)) { + distanceMap.put(node, Math.min(distanceMap.get(node), distance)); + insertHeapify(heapIndexMap.get(node)); + } + if (!isEntered(node)) { + nodes[size] = node; + heapIndexMap.put(node, size); + distanceMap.put(node, distance); + insertHeapify(size++); + } + } + + public NodeRecord pop() { + NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0])); + swap(0, size - 1); + heapIndexMap.put(nodes[size - 1], -1); + distanceMap.remove(nodes[size - 1]); + // free C++同学还要把原本堆顶节点析构,对java同学不必 + nodes[size - 1] = null; + heapify(0, --size); + return nodeRecord; + } + + private void insertHeapify(int index) { + while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) { + swap(index, (index - 1) / 2); + index = (index - 1) / 2; + } + } + + private void heapify(int index, int size) { + int left = index * 2 + 1; + while (left < size) { + int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left]) + ? left + 1 + : left; + smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index; + if (smallest == index) { + break; + } + swap(smallest, index); + index = smallest; + left = index * 2 + 1; + } + } + + private boolean isEntered(Node node) { + return heapIndexMap.containsKey(node); + } + + private boolean inHeap(Node node) { + return isEntered(node) && heapIndexMap.get(node) != -1; + } + + private void swap(int index1, int index2) { + heapIndexMap.put(nodes[index1], index2); + heapIndexMap.put(nodes[index2], index1); + Node tmp = nodes[index1]; + nodes[index1] = nodes[index2]; + nodes[index2] = tmp; + } + } + + // 改进后的dijkstra算法 + // 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回 + public static HashMap dijkstra2(Node head, int size) { + NodeHeap nodeHeap = new NodeHeap(size); + nodeHeap.addOrUpdateOrIgnore(head, 0); + HashMap result = new HashMap<>(); + while (!nodeHeap.isEmpty()) { + NodeRecord record = nodeHeap.pop(); + Node cur = record.node; + int distance = record.distance; + for (Edge edge : cur.edges) { + nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance); + } + result.put(cur, distance); + } + return result; + } + +} diff --git a/体系学习班/class16/Code06_NetworkDelayTime.java b/体系学习班/class16/Code06_NetworkDelayTime.java new file mode 100644 index 0000000..0be520a --- /dev/null +++ b/体系学习班/class16/Code06_NetworkDelayTime.java @@ -0,0 +1,151 @@ +package class16; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.PriorityQueue; + +// 课上没有讲这个题,这是我给同学们找的练习题 +// leetcode 743题,可以用这道题来练习Dijkstra算法 +// 测试链接 : https://leetcode.com/problems/network-delay-time +public class Code06_NetworkDelayTime { + + // 方法一 : 普通堆 + 屏蔽已经计算过的点 + public static int networkDelayTime1(int[][] times, int n, int k) { + ArrayList> nexts = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + nexts.add(new ArrayList<>()); + } + for (int[] delay : times) { + nexts.get(delay[0]).add(new int[] { delay[1], delay[2] }); + } + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[1] - b[1]); + heap.add(new int[] { k, 0 }); + boolean[] used = new boolean[n + 1]; + int num = 0; + int max = 0; + while (!heap.isEmpty() && num < n) { + int[] record = heap.poll(); + int cur = record[0]; + int delay = record[1]; + if (used[cur]) { + continue; + } + used[cur] = true; + num++; + max = Math.max(max, delay); + for (int[] next : nexts.get(cur)) { + heap.add(new int[] { next[0], delay + next[1] }); + } + } + return num < n ? -1 : max; + } + + // 方法二 : 加强堆的解法 + public static int networkDelayTime2(int[][] times, int n, int k) { + ArrayList> nexts = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + nexts.add(new ArrayList<>()); + } + for (int[] delay : times) { + nexts.get(delay[0]).add(new int[] { delay[1], delay[2] }); + } + Heap heap = new Heap(n); + heap.add(k, 0); + int num = 0; + int max = 0; + while (!heap.isEmpty()) { + int[] record = heap.poll(); + int cur = record[0]; + int delay = record[1]; + num++; + max = Math.max(max, delay); + for (int[] next : nexts.get(cur)) { + heap.add(next[0], delay + next[1]); + } + } + return num < n ? -1 : max; + } + + // 加强堆 + public static class Heap { + public boolean[] used; + public int[][] heap; + public int[] hIndex; + public int size; + + public Heap(int n) { + used = new boolean[n + 1]; + heap = new int[n + 1][2]; + hIndex = new int[n + 1]; + Arrays.fill(hIndex, -1); + size = 0; + } + + public void add(int cur, int delay) { + if (used[cur]) { + return; + } + if (hIndex[cur] == -1) { + heap[size][0] = cur; + heap[size][1] = delay; + hIndex[cur] = size; + heapInsert(size++); + } else { + int hi = hIndex[cur]; + if (delay <= heap[hi][1]) { + heap[hi][1] = delay; + heapInsert(hi); + } + } + } + + public int[] poll() { + int[] ans = heap[0]; + swap(0, --size); + heapify(0); + used[ans[0]] = true; + hIndex[ans[0]] = -1; + return ans; + } + + public boolean isEmpty() { + return size == 0; + } + + private void heapInsert(int i) { + int parent = (i - 1) / 2; + while (heap[i][1] < heap[parent][1]) { + swap(i, parent); + i = parent; + parent = (i - 1) / 2; + } + } + + private void heapify(int i) { + int l = (i * 2) + 1; + while (l < size) { + int smallest = l + 1 < size && heap[l + 1][1] < heap[l][1] ? (l + 1) : l; + smallest = heap[smallest][1] < heap[i][1] ? smallest : i; + if (smallest == i) { + break; + } + swap(smallest, i); + i = smallest; + l = (i * 2) + 1; + } + } + + private void swap(int i, int j) { + int[] o1 = heap[i]; + int[] o2 = heap[j]; + int o1hi = hIndex[o1[0]]; + int o2hi = hIndex[o2[0]]; + heap[i] = o2; + heap[j] = o1; + hIndex[o1[0]] = o2hi; + hIndex[o2[0]] = o1hi; + } + + } + +} diff --git a/体系学习班/class16/Edge.java b/体系学习班/class16/Edge.java new file mode 100644 index 0000000..7c56891 --- /dev/null +++ b/体系学习班/class16/Edge.java @@ -0,0 +1,14 @@ +package class16; + +public class Edge { + public int weight; + public Node from; + public Node to; + + public Edge(int weight, Node from, Node to) { + this.weight = weight; + this.from = from; + this.to = to; + } + +} diff --git a/体系学习班/class16/Graph.java b/体系学习班/class16/Graph.java new file mode 100644 index 0000000..933e43b --- /dev/null +++ b/体系学习班/class16/Graph.java @@ -0,0 +1,14 @@ +package class16; + +import java.util.HashMap; +import java.util.HashSet; + +public class Graph { + public HashMap nodes; + public HashSet edges; + + public Graph() { + nodes = new HashMap<>(); + edges = new HashSet<>(); + } +} diff --git a/体系学习班/class16/GraphGenerator.java b/体系学习班/class16/GraphGenerator.java new file mode 100644 index 0000000..fe3387d --- /dev/null +++ b/体系学习班/class16/GraphGenerator.java @@ -0,0 +1,37 @@ +package class16; + +public class GraphGenerator { + + // matrix 所有的边 + // N*3 的矩阵 + // [weight, from节点上面的值,to节点上面的值] + // + // [ 5 , 0 , 7] + // [ 3 , 0, 1] + // + public static Graph createGraph(int[][] matrix) { + Graph graph = new Graph(); + for (int i = 0; i < matrix.length; i++) { + // 拿到每一条边, matrix[i] + int weight = matrix[i][0]; + int from = matrix[i][1]; + int to = matrix[i][2]; + if (!graph.nodes.containsKey(from)) { + graph.nodes.put(from, new Node(from)); + } + if (!graph.nodes.containsKey(to)) { + graph.nodes.put(to, new Node(to)); + } + Node fromNode = graph.nodes.get(from); + Node toNode = graph.nodes.get(to); + Edge newEdge = new Edge(weight, fromNode, toNode); + fromNode.nexts.add(toNode); + fromNode.out++; + toNode.in++; + fromNode.edges.add(newEdge); + graph.edges.add(newEdge); + } + return graph; + } + +} diff --git a/体系学习班/class16/Node.java b/体系学习班/class16/Node.java new file mode 100644 index 0000000..a468aaf --- /dev/null +++ b/体系学习班/class16/Node.java @@ -0,0 +1,20 @@ +package class16; + +import java.util.ArrayList; + +// 点结构的描述 +public class Node { + public int value; + public int in; + public int out; + public ArrayList nexts; + public ArrayList edges; + + public Node(int value) { + this.value = value; + in = 0; + out = 0; + nexts = new ArrayList<>(); + edges = new ArrayList<>(); + } +} diff --git a/体系学习班/class17/Code01_Dijkstra.java b/体系学习班/class17/Code01_Dijkstra.java new file mode 100644 index 0000000..0b39615 --- /dev/null +++ b/体系学习班/class17/Code01_Dijkstra.java @@ -0,0 +1,162 @@ +package class17; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map.Entry; + +// no negative weight +public class Code01_Dijkstra { + + public static HashMap dijkstra1(Node from) { + HashMap distanceMap = new HashMap<>(); + distanceMap.put(from, 0); + // 打过对号的点 + HashSet selectedNodes = new HashSet<>(); + Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); + while (minNode != null) { + // 原始点 -> minNode(跳转点) 最小距离distance + int distance = distanceMap.get(minNode); + for (Edge edge : minNode.edges) { + Node toNode = edge.to; + if (!distanceMap.containsKey(toNode)) { + distanceMap.put(toNode, distance + edge.weight); + } else { // toNode + distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight)); + } + } + selectedNodes.add(minNode); + minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); + } + return distanceMap; + } + + public static Node getMinDistanceAndUnselectedNode(HashMap distanceMap, HashSet touchedNodes) { + Node minNode = null; + int minDistance = Integer.MAX_VALUE; + for (Entry entry : distanceMap.entrySet()) { + Node node = entry.getKey(); + int distance = entry.getValue(); + if (!touchedNodes.contains(node) && distance < minDistance) { + minNode = node; + minDistance = distance; + } + } + return minNode; + } + + public static class NodeRecord { + public Node node; + public int distance; + + public NodeRecord(Node node, int distance) { + this.node = node; + this.distance = distance; + } + } + + public static class NodeHeap { + // 堆! + private Node[] nodes; + // node -> 堆上的什么位置? + + private HashMap heapIndexMap; + private HashMap distanceMap; + private int size; + + public NodeHeap(int size) { + nodes = new Node[size]; + heapIndexMap = new HashMap<>(); + distanceMap = new HashMap<>(); + size = 0; + } + + public boolean isEmpty() { + return size == 0; + } + + // 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance + // 判断要不要更新,如果需要的话,就更新 + public void addOrUpdateOrIgnore(Node node, int distance) { + if (inHeap(node)) { // update + distanceMap.put(node, Math.min(distanceMap.get(node), distance)); + insertHeapify(node, heapIndexMap.get(node)); + } + if (!isEntered(node)) { // add + nodes[size] = node; + heapIndexMap.put(node, size); + distanceMap.put(node, distance); + insertHeapify(node, size++); + } + // ignore + } + + public NodeRecord pop() { + NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0])); + swap(0, size - 1); // 0 > size - 1 size - 1 > 0 + heapIndexMap.put(nodes[size - 1], -1); + distanceMap.remove(nodes[size - 1]); + // free C++同学还要把原本堆顶节点析构,对java同学不必 + nodes[size - 1] = null; + heapify(0, --size); + return nodeRecord; + } + + private void insertHeapify(Node node, int index) { + while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) { + swap(index, (index - 1) / 2); + index = (index - 1) / 2; + } + } + + private void heapify(int index, int size) { + int left = index * 2 + 1; + while (left < size) { + int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left]) + ? left + 1 + : left; + smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index; + if (smallest == index) { + break; + } + swap(smallest, index); + index = smallest; + left = index * 2 + 1; + } + } + + private boolean isEntered(Node node) { + return heapIndexMap.containsKey(node); + } + + private boolean inHeap(Node node) { + return isEntered(node) && heapIndexMap.get(node) != -1; + } + + private void swap(int index1, int index2) { + heapIndexMap.put(nodes[index1], index2); + heapIndexMap.put(nodes[index2], index1); + Node tmp = nodes[index1]; + nodes[index1] = nodes[index2]; + nodes[index2] = tmp; + } + } + + // 改进后的dijkstra算法 + // 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回 + public static HashMap dijkstra2(Node head, int size) { + NodeHeap nodeHeap = new NodeHeap(size); + nodeHeap.addOrUpdateOrIgnore(head, 0); + HashMap result = new HashMap<>(); + while (!nodeHeap.isEmpty()) { + NodeRecord record = nodeHeap.pop(); + Node cur = record.node; + int distance = record.distance; + for (Edge edge : cur.edges) { + nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance); + } + result.put(cur, distance); + } + return result; + } + +} diff --git a/体系学习班/class17/Code02_Hanoi.java b/体系学习班/class17/Code02_Hanoi.java new file mode 100644 index 0000000..3d9ba6e --- /dev/null +++ b/体系学习班/class17/Code02_Hanoi.java @@ -0,0 +1,139 @@ +package class17; + +import java.util.Stack; + +public class Code02_Hanoi { + + public static void hanoi1(int n) { + leftToRight(n); + } + + // 请把1~N层圆盘 从左 -> 右 + public static void leftToRight(int n) { + if (n == 1) { // base case + System.out.println("Move 1 from left to right"); + return; + } + leftToMid(n - 1); + System.out.println("Move " + n + " from left to right"); + midToRight(n - 1); + } + + // 请把1~N层圆盘 从左 -> 中 + public static void leftToMid(int n) { + if (n == 1) { + System.out.println("Move 1 from left to mid"); + return; + } + leftToRight(n - 1); + System.out.println("Move " + n + " from left to mid"); + rightToMid(n - 1); + } + + public static void rightToMid(int n) { + if (n == 1) { + System.out.println("Move 1 from right to mid"); + return; + } + rightToLeft(n - 1); + System.out.println("Move " + n + " from right to mid"); + leftToMid(n - 1); + } + + public static void midToRight(int n) { + if (n == 1) { + System.out.println("Move 1 from mid to right"); + return; + } + midToLeft(n - 1); + System.out.println("Move " + n + " from mid to right"); + leftToRight(n - 1); + } + + public static void midToLeft(int n) { + if (n == 1) { + System.out.println("Move 1 from mid to left"); + return; + } + midToRight(n - 1); + System.out.println("Move " + n + " from mid to left"); + rightToLeft(n - 1); + } + + public static void rightToLeft(int n) { + if (n == 1) { + System.out.println("Move 1 from right to left"); + return; + } + rightToMid(n - 1); + System.out.println("Move " + n + " from right to left"); + midToLeft(n - 1); + } + + public static void hanoi2(int n) { + if (n > 0) { + func(n, "left", "right", "mid"); + } + } + + public static void func(int N, String from, String to, String other) { + if (N == 1) { // base + System.out.println("Move 1 from " + from + " to " + to); + } else { + func(N - 1, from, other, to); + System.out.println("Move " + N + " from " + from + " to " + to); + func(N - 1, other, to, from); + } + } + + public static class Record { + public boolean finish1; + public int base; + public String from; + public String to; + public String other; + + public Record(boolean f1, int b, String f, String t, String o) { + finish1 = false; + base = b; + from = f; + to = t; + other = o; + } + } + + public static void hanoi3(int N) { + if (N < 1) { + return; + } + Stack stack = new Stack<>(); + stack.add(new Record(false, N, "left", "right", "mid")); + while (!stack.isEmpty()) { + Record cur = stack.pop(); + if (cur.base == 1) { + System.out.println("Move 1 from " + cur.from + " to " + cur.to); + if (!stack.isEmpty()) { + stack.peek().finish1 = true; + } + } else { + if (!cur.finish1) { + stack.push(cur); + stack.push(new Record(false, cur.base - 1, cur.from, cur.other, cur.to)); + } else { + System.out.println("Move " + cur.base + " from " + cur.from + " to " + cur.to); + stack.push(new Record(false, cur.base - 1, cur.other, cur.to, cur.from)); + } + } + } + } + + public static void main(String[] args) { + int n = 3; + hanoi1(n); + System.out.println("============"); + hanoi2(n); +// System.out.println("============"); +// hanoi3(n); + } + +} diff --git a/体系学习班/class17/Code03_PrintAllSubsquences.java b/体系学习班/class17/Code03_PrintAllSubsquences.java new file mode 100644 index 0000000..05b6a03 --- /dev/null +++ b/体系学习班/class17/Code03_PrintAllSubsquences.java @@ -0,0 +1,74 @@ +package class17; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class Code03_PrintAllSubsquences { + + // s -> "abc" -> + public static List subs(String s) { + char[] str = s.toCharArray(); + String path = ""; + List ans = new ArrayList<>(); + process1(str, 0, ans, path); + return ans; + } + + // str 固定参数 + // 来到了str[index]字符,index是位置 + // str[0..index-1]已经走过了!之前的决定,都在path上 + // 之前的决定已经不能改变了,就是path + // str[index....]还能决定,之前已经确定,而后面还能自由选择的话, + // 把所有生成的子序列,放入到ans里去 + public static void process1(char[] str, int index, List ans, String path) { + if (index == str.length) { + ans.add(path); + return; + } + // 没有要index位置的字符 + process1(str, index + 1, ans, path); + // 要了index位置的字符 + process1(str, index + 1, ans, path + String.valueOf(str[index])); + } + + public static List subsNoRepeat(String s) { + char[] str = s.toCharArray(); + String path = ""; + HashSet set = new HashSet<>(); + process2(str, 0, set, path); + List ans = new ArrayList<>(); + for (String cur : set) { + ans.add(cur); + } + return ans; + } + + public static void process2(char[] str, int index, HashSet set, String path) { + if (index == str.length) { + set.add(path); + return; + } + String no = path; + process2(str, index + 1, set, no); + String yes = path + String.valueOf(str[index]); + process2(str, index + 1, set, yes); + } + + public static void main(String[] args) { + String test = "acccc"; + List ans1 = subs(test); + List ans2 = subsNoRepeat(test); + + for (String str : ans1) { + System.out.println(str); + } + System.out.println("================="); + for (String str : ans2) { + System.out.println(str); + } + System.out.println("================="); + + } + +} diff --git a/体系学习班/class17/Code04_PrintAllPermutations.java b/体系学习班/class17/Code04_PrintAllPermutations.java new file mode 100644 index 0000000..0337a95 --- /dev/null +++ b/体系学习班/class17/Code04_PrintAllPermutations.java @@ -0,0 +1,110 @@ +package class17; + +import java.util.ArrayList; +import java.util.List; + +public class Code04_PrintAllPermutations { + + public static List permutation1(String s) { + List ans = new ArrayList<>(); + if (s == null || s.length() == 0) { + return ans; + } + char[] str = s.toCharArray(); + ArrayList rest = new ArrayList(); + for (char cha : str) { + rest.add(cha); + } + String path = ""; + f(rest, path, ans); + return ans; + } + + public static void f(ArrayList rest, String path, List ans) { + if (rest.isEmpty()) { + ans.add(path); + } else { + int N = rest.size(); + for (int i = 0; i < N; i++) { + char cur = rest.get(i); + rest.remove(i); + f(rest, path + cur, ans); + rest.add(i, cur); + } + } + } + + public static List permutation2(String s) { + List ans = new ArrayList<>(); + if (s == null || s.length() == 0) { + return ans; + } + char[] str = s.toCharArray(); + g1(str, 0, ans); + return ans; + } + + public static void g1(char[] str, int index, List ans) { + if (index == str.length) { + ans.add(String.valueOf(str)); + } else { + for (int i = index; i < str.length; i++) { + swap(str, index, i); + g1(str, index + 1, ans); + swap(str, index, i); + } + } + } + + public static List permutation3(String s) { + List ans = new ArrayList<>(); + if (s == null || s.length() == 0) { + return ans; + } + char[] str = s.toCharArray(); + g2(str, 0, ans); + return ans; + } + + public static void g2(char[] str, int index, List ans) { + if (index == str.length) { + ans.add(String.valueOf(str)); + } else { + boolean[] visited = new boolean[256]; + for (int i = index; i < str.length; i++) { + if (!visited[str[i]]) { + visited[str[i]] = true; + swap(str, index, i); + g2(str, index + 1, ans); + swap(str, index, i); + } + } + } + } + + public static void swap(char[] chs, int i, int j) { + char tmp = chs[i]; + chs[i] = chs[j]; + chs[j] = tmp; + } + + public static void main(String[] args) { + String s = "acc"; + List ans1 = permutation1(s); + for (String str : ans1) { + System.out.println(str); + } + System.out.println("======="); + List ans2 = permutation2(s); + for (String str : ans2) { + System.out.println(str); + } + System.out.println("======="); + List ans3 = permutation3(s); + for (String str : ans3) { + System.out.println(str); + } + + } + +} diff --git a/体系学习班/class17/Code05_ReverseStackUsingRecursive.java b/体系学习班/class17/Code05_ReverseStackUsingRecursive.java new file mode 100644 index 0000000..731056a --- /dev/null +++ b/体系学习班/class17/Code05_ReverseStackUsingRecursive.java @@ -0,0 +1,44 @@ +package class17; + +import java.util.Stack; + +public class Code05_ReverseStackUsingRecursive { + + public static void reverse(Stack stack) { + if (stack.isEmpty()) { + return; + } + int i = f(stack); + reverse(stack); + stack.push(i); + } + + // 栈底元素移除掉 + // 上面的元素盖下来 + // 返回移除掉的栈底元素 + public static int f(Stack stack) { + int result = stack.pop(); + if (stack.isEmpty()) { + return result; + } else { + int last = f(stack); + stack.push(result); + return last; + } + } + + public static void main(String[] args) { + Stack test = new Stack(); + test.push(1); + test.push(2); + test.push(3); + test.push(4); + test.push(5); + reverse(test); + while (!test.isEmpty()) { + System.out.println(test.pop()); + } + + } + +} diff --git a/体系学习班/class17/Edge.java b/体系学习班/class17/Edge.java new file mode 100644 index 0000000..eacd58b --- /dev/null +++ b/体系学习班/class17/Edge.java @@ -0,0 +1,14 @@ +package class17; + +public class Edge { + public int weight; + public Node from; + public Node to; + + public Edge(int weight, Node from, Node to) { + this.weight = weight; + this.from = from; + this.to = to; + } + +} diff --git a/体系学习班/class17/Graph.java b/体系学习班/class17/Graph.java new file mode 100644 index 0000000..f9314a2 --- /dev/null +++ b/体系学习班/class17/Graph.java @@ -0,0 +1,14 @@ +package class17; + +import java.util.HashMap; +import java.util.HashSet; + +public class Graph { + public HashMap nodes; + public HashSet edges; + + public Graph() { + nodes = new HashMap<>(); + edges = new HashSet<>(); + } +} diff --git a/体系学习班/class17/Node.java b/体系学习班/class17/Node.java new file mode 100644 index 0000000..1056ead --- /dev/null +++ b/体系学习班/class17/Node.java @@ -0,0 +1,20 @@ +package class17; + +import java.util.ArrayList; + +// 点结构的描述 +public class Node { + public int value; + public int in; + public int out; + public ArrayList nexts; + public ArrayList edges; + + public Node(int value) { + this.value = value; + in = 0; + out = 0; + nexts = new ArrayList<>(); + edges = new ArrayList<>(); + } +} diff --git a/体系学习班/class18/Code01_RobotWalk.java b/体系学习班/class18/Code01_RobotWalk.java new file mode 100644 index 0000000..f9ee3f2 --- /dev/null +++ b/体系学习班/class18/Code01_RobotWalk.java @@ -0,0 +1,94 @@ +package class18; + +public class Code01_RobotWalk { + + public static int ways1(int N, int start, int aim, int K) { + if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1) { + return -1; + } + return process1(start, K, aim, N); + } + + // 机器人当前来到的位置是cur, + // 机器人还有rest步需要去走, + // 最终的目标是aim, + // 有哪些位置?1~N + // 返回:机器人从cur出发,走过rest步之后,最终停在aim的方法数,是多少? + public static int process1(int cur, int rest, int aim, int N) { + if (rest == 0) { // 如果已经不需要走了,走完了! + return cur == aim ? 1 : 0; + } + // (cur, rest) + if (cur == 1) { // 1 -> 2 + return process1(2, rest - 1, aim, N); + } + // (cur, rest) + if (cur == N) { // N-1 <- N + return process1(N - 1, rest - 1, aim, N); + } + // (cur, rest) + return process1(cur - 1, rest - 1, aim, N) + process1(cur + 1, rest - 1, aim, N); + } + + public static int ways2(int N, int start, int aim, int K) { + if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1) { + return -1; + } + int[][] dp = new int[N + 1][K + 1]; + for (int i = 0; i <= N; i++) { + for (int j = 0; j <= K; j++) { + dp[i][j] = -1; + } + } + // dp就是缓存表 + // dp[cur][rest] == -1 -> process1(cur, rest)之前没算过! + // dp[cur][rest] != -1 -> process1(cur, rest)之前算过!返回值,dp[cur][rest] + // N+1 * K+1 + return process2(start, K, aim, N, dp); + } + + // cur 范: 1 ~ N + // rest 范:0 ~ K + public static int process2(int cur, int rest, int aim, int N, int[][] dp) { + if (dp[cur][rest] != -1) { + return dp[cur][rest]; + } + // 之前没算过! + int ans = 0; + if (rest == 0) { + ans = cur == aim ? 1 : 0; + } else if (cur == 1) { + ans = process2(2, rest - 1, aim, N, dp); + } else if (cur == N) { + ans = process2(N - 1, rest - 1, aim, N, dp); + } else { + ans = process2(cur - 1, rest - 1, aim, N, dp) + process2(cur + 1, rest - 1, aim, N, dp); + } + dp[cur][rest] = ans; + return ans; + + } + + public static int ways3(int N, int start, int aim, int K) { + if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1) { + return -1; + } + int[][] dp = new int[N + 1][K + 1]; + dp[aim][0] = 1; + for (int rest = 1; rest <= K; rest++) { + dp[1][rest] = dp[2][rest - 1]; + for (int cur = 2; cur < N; cur++) { + dp[cur][rest] = dp[cur - 1][rest - 1] + dp[cur + 1][rest - 1]; + } + dp[N][rest] = dp[N - 1][rest - 1]; + } + return dp[start][K]; + } + + public static void main(String[] args) { + System.out.println(ways1(5, 2, 4, 6)); + System.out.println(ways2(5, 2, 4, 6)); + System.out.println(ways3(5, 2, 4, 6)); + } + +} diff --git a/体系学习班/class18/Code02_CardsInLine.java b/体系学习班/class18/Code02_CardsInLine.java new file mode 100644 index 0000000..919a4cb --- /dev/null +++ b/体系学习班/class18/Code02_CardsInLine.java @@ -0,0 +1,116 @@ +package class18; + +public class Code02_CardsInLine { + + // 根据规则,返回获胜者的分数 + public static int win1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int first = f1(arr, 0, arr.length - 1); + int second = g1(arr, 0, arr.length - 1); + return Math.max(first, second); + } + + // arr[L..R],先手获得的最好分数返回 + public static int f1(int[] arr, int L, int R) { + if (L == R) { + return arr[L]; + } + int p1 = arr[L] + g1(arr, L + 1, R); + int p2 = arr[R] + g1(arr, L, R - 1); + return Math.max(p1, p2); + } + + // // arr[L..R],后手获得的最好分数返回 + public static int g1(int[] arr, int L, int R) { + if (L == R) { + return 0; + } + int p1 = f1(arr, L + 1, R); // 对手拿走了L位置的数 + int p2 = f1(arr, L, R - 1); // 对手拿走了R位置的数 + return Math.min(p1, p2); + } + + public static int win2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + int[][] fmap = new int[N][N]; + int[][] gmap = new int[N][N]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + fmap[i][j] = -1; + gmap[i][j] = -1; + } + } + int first = f2(arr, 0, arr.length - 1, fmap, gmap); + int second = g2(arr, 0, arr.length - 1, fmap, gmap); + return Math.max(first, second); + } + + // arr[L..R],先手获得的最好分数返回 + public static int f2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) { + if (fmap[L][R] != -1) { + return fmap[L][R]; + } + int ans = 0; + if (L == R) { + ans = arr[L]; + } else { + int p1 = arr[L] + g2(arr, L + 1, R, fmap, gmap); + int p2 = arr[R] + g2(arr, L, R - 1, fmap, gmap); + ans = Math.max(p1, p2); + } + fmap[L][R] = ans; + return ans; + } + + // // arr[L..R],后手获得的最好分数返回 + public static int g2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) { + if (gmap[L][R] != -1) { + return gmap[L][R]; + } + int ans = 0; + if (L != R) { + int p1 = f2(arr, L + 1, R, fmap, gmap); // 对手拿走了L位置的数 + int p2 = f2(arr, L, R - 1, fmap, gmap); // 对手拿走了R位置的数 + ans = Math.min(p1, p2); + } + gmap[L][R] = ans; + return ans; + } + + public static int win3(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + int[][] fmap = new int[N][N]; + int[][] gmap = new int[N][N]; + for (int i = 0; i < N; i++) { + fmap[i][i] = arr[i]; + } + for (int startCol = 1; startCol < N; startCol++) { + int L = 0; + int R = startCol; + while (R < N) { + fmap[L][R] = Math.max(arr[L] + gmap[L + 1][R], arr[R] + gmap[L][R - 1]); + gmap[L][R] = Math.min(fmap[L + 1][R], fmap[L][R - 1]); + L++; + R++; + } + } + return Math.max(fmap[0][N - 1], gmap[0][N - 1]); + } + + public static void main(String[] args) { + int[] arr = { 5, 7, 4, 5, 8, 1, 6, 0, 3, 4, 6, 1, 7 }; + System.out.println(win1(arr)); + System.out.println(win2(arr)); + System.out.println(win3(arr)); + + } + +} diff --git a/体系学习班/class19/Code01_Knapsack.java b/体系学习班/class19/Code01_Knapsack.java new file mode 100644 index 0000000..ab355a2 --- /dev/null +++ b/体系学习班/class19/Code01_Knapsack.java @@ -0,0 +1,63 @@ +package class19; + +public class Code01_Knapsack { + + // 所有的货,重量和价值,都在w和v数组里 + // 为了方便,其中没有负数 + // bag背包容量,不能超过这个载重 + // 返回:不超重的情况下,能够得到的最大价值 + public static int maxValue(int[] w, int[] v, int bag) { + if (w == null || v == null || w.length != v.length || w.length == 0) { + return 0; + } + // 尝试函数! + return process(w, v, 0, bag); + } + + // index 0~N + // rest 负~bag + public static int process(int[] w, int[] v, int index, int rest) { + if (rest < 0) { + return -1; + } + if (index == w.length) { + return 0; + } + int p1 = process(w, v, index + 1, rest); + int p2 = 0; + int next = process(w, v, index + 1, rest - w[index]); + if (next != -1) { + p2 = v[index] + next; + } + return Math.max(p1, p2); + } + + public static int dp(int[] w, int[] v, int bag) { + if (w == null || v == null || w.length != v.length || w.length == 0) { + return 0; + } + int N = w.length; + int[][] dp = new int[N + 1][bag + 1]; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= bag; rest++) { + int p1 = dp[index + 1][rest]; + int p2 = 0; + int next = rest - w[index] < 0 ? -1 : dp[index + 1][rest - w[index]]; + if (next != -1) { + p2 = v[index] + next; + } + dp[index][rest] = Math.max(p1, p2); + } + } + return dp[0][bag]; + } + + public static void main(String[] args) { + int[] weights = { 3, 2, 4, 7, 3, 1, 7 }; + int[] values = { 5, 6, 3, 19, 12, 4, 2 }; + int bag = 15; + System.out.println(maxValue(weights, values, bag)); + System.out.println(dp(weights, values, bag)); + } + +} diff --git a/体系学习班/class19/Code02_ConvertToLetterString.java b/体系学习班/class19/Code02_ConvertToLetterString.java new file mode 100644 index 0000000..b780ca7 --- /dev/null +++ b/体系学习班/class19/Code02_ConvertToLetterString.java @@ -0,0 +1,123 @@ +package class19; + +public class Code02_ConvertToLetterString { + + // str只含有数字字符0~9 + // 返回多少种转化方案 + public static int number(String str) { + if (str == null || str.length() == 0) { + return 0; + } + return process(str.toCharArray(), 0); + } + + // str[0..i-1]转化无需过问 + // str[i.....]去转化,返回有多少种转化方法 + public static int process(char[] str, int i) { + if (i == str.length) { + return 1; + } + // i没到最后,说明有字符 + if (str[i] == '0') { // 之前的决定有问题 + return 0; + } + // str[i] != '0' + // 可能性一,i单转 + int ways = process(str, i + 1); + if (i + 1 < str.length && (str[i] - '0') * 10 + str[i + 1] - '0' < 27) { + ways += process(str, i + 2); + } + return ways; + } + + // 从右往左的动态规划 + // 就是上面方法的动态规划版本 + // dp[i]表示:str[i...]有多少种转化方式 + public static int dp1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + if (str[i] != '0') { + int ways = dp[i + 1]; + if (i + 1 < str.length && (str[i] - '0') * 10 + str[i + 1] - '0' < 27) { + ways += dp[i + 2]; + } + dp[i] = ways; + } + } + return dp[0]; + } + + // 从左往右的动态规划 + // dp[i]表示:str[0...i]有多少种转化方式 + public static int dp2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + if (str[0] == '0') { + return 0; + } + int[] dp = new int[N]; + dp[0] = 1; + for (int i = 1; i < N; i++) { + if (str[i] == '0') { + // 如果此时str[i]=='0',那么他是一定要拉前一个字符(i-1的字符)一起拼的, + // 那么就要求前一个字符,不能也是‘0’,否则拼不了。 + // 前一个字符不是‘0’就够了嘛?不够,还得要求拼完了要么是10,要么是20,如果更大的话,拼不了。 + // 这就够了嘛?还不够,你们拼完了,还得要求str[0...i-2]真的可以被分解! + // 如果str[0...i-2]都不存在分解方案,那i和i-1拼成了也不行,因为之前的搞定不了。 + if (str[i - 1] == '0' || str[i - 1] > '2' || (i - 2 >= 0 && dp[i - 2] == 0)) { + return 0; + } else { + dp[i] = i - 2 >= 0 ? dp[i - 2] : 1; + } + } else { + dp[i] = dp[i - 1]; + if (str[i - 1] != '0' && (str[i - 1] - '0') * 10 + str[i] - '0' <= 26) { + dp[i] += i - 2 >= 0 ? dp[i - 2] : 1; + } + } + } + return dp[N - 1]; + } + + // 为了测试 + public static String randomString(int len) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * 10) + '0'); + } + return String.valueOf(str); + } + + // 为了测试 + public static void main(String[] args) { + int N = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N); + String s = randomString(len); + int ans0 = number(s); + int ans1 = dp1(s); + int ans2 = dp2(s); + if (ans0 != ans1 || ans0 != ans2) { + System.out.println(s); + System.out.println(ans0); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class19/Code03_StickersToSpellWord.java b/体系学习班/class19/Code03_StickersToSpellWord.java new file mode 100644 index 0000000..90b3a4d --- /dev/null +++ b/体系学习班/class19/Code03_StickersToSpellWord.java @@ -0,0 +1,151 @@ +package class19; + +import java.util.HashMap; + +// 本题测试链接:https://leetcode.com/problems/stickers-to-spell-word +public class Code03_StickersToSpellWord { + + public static int minStickers1(String[] stickers, String target) { + int ans = process1(stickers, target); + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + // 所有贴纸stickers,每一种贴纸都有无穷张 + // target + // 最少张数 + public static int process1(String[] stickers, String target) { + if (target.length() == 0) { + return 0; + } + int min = Integer.MAX_VALUE; + for (String first : stickers) { + String rest = minus(target, first); + if (rest.length() != target.length()) { + min = Math.min(min, process1(stickers, rest)); + } + } + return min + (min == Integer.MAX_VALUE ? 0 : 1); + } + + public static String minus(String s1, String s2) { + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int[] count = new int[26]; + for (char cha : str1) { + count[cha - 'a']++; + } + for (char cha : str2) { + count[cha - 'a']--; + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 26; i++) { + if (count[i] > 0) { + for (int j = 0; j < count[i]; j++) { + builder.append((char) (i + 'a')); + } + } + } + return builder.toString(); + } + + public static int minStickers2(String[] stickers, String target) { + int N = stickers.length; + // 关键优化(用词频表替代贴纸数组) + int[][] counts = new int[N][26]; + for (int i = 0; i < N; i++) { + char[] str = stickers[i].toCharArray(); + for (char cha : str) { + counts[i][cha - 'a']++; + } + } + int ans = process2(counts, target); + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + // stickers[i] 数组,当初i号贴纸的字符统计 int[][] stickers -> 所有的贴纸 + // 每一种贴纸都有无穷张 + // 返回搞定target的最少张数 + // 最少张数 + public static int process2(int[][] stickers, String t) { + if (t.length() == 0) { + return 0; + } + // target做出词频统计 + // target aabbc 2 2 1.. + // 0 1 2.. + char[] target = t.toCharArray(); + int[] tcounts = new int[26]; + for (char cha : target) { + tcounts[cha - 'a']++; + } + int N = stickers.length; + int min = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + // 尝试第一张贴纸是谁 + int[] sticker = stickers[i]; + // 最关键的优化(重要的剪枝!这一步也是贪心!) + if (sticker[target[0] - 'a'] > 0) { + StringBuilder builder = new StringBuilder(); + for (int j = 0; j < 26; j++) { + if (tcounts[j] > 0) { + int nums = tcounts[j] - sticker[j]; + for (int k = 0; k < nums; k++) { + builder.append((char) (j + 'a')); + } + } + } + String rest = builder.toString(); + min = Math.min(min, process2(stickers, rest)); + } + } + return min + (min == Integer.MAX_VALUE ? 0 : 1); + } + + public static int minStickers3(String[] stickers, String target) { + int N = stickers.length; + int[][] counts = new int[N][26]; + for (int i = 0; i < N; i++) { + char[] str = stickers[i].toCharArray(); + for (char cha : str) { + counts[i][cha - 'a']++; + } + } + HashMap dp = new HashMap<>(); + dp.put("", 0); + int ans = process3(counts, target, dp); + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + public static int process3(int[][] stickers, String t, HashMap dp) { + if (dp.containsKey(t)) { + return dp.get(t); + } + char[] target = t.toCharArray(); + int[] tcounts = new int[26]; + for (char cha : target) { + tcounts[cha - 'a']++; + } + int N = stickers.length; + int min = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + int[] sticker = stickers[i]; + if (sticker[target[0] - 'a'] > 0) { + StringBuilder builder = new StringBuilder(); + for (int j = 0; j < 26; j++) { + if (tcounts[j] > 0) { + int nums = tcounts[j] - sticker[j]; + for (int k = 0; k < nums; k++) { + builder.append((char) (j + 'a')); + } + } + } + String rest = builder.toString(); + min = Math.min(min, process3(stickers, rest, dp)); + } + } + int ans = min + (min == Integer.MAX_VALUE ? 0 : 1); + dp.put(t, ans); + return ans; + } + +} diff --git a/体系学习班/class19/Code04_LongestCommonSubsequence.java b/体系学习班/class19/Code04_LongestCommonSubsequence.java new file mode 100644 index 0000000..5e49551 --- /dev/null +++ b/体系学习班/class19/Code04_LongestCommonSubsequence.java @@ -0,0 +1,124 @@ +package class19; + +// 这个问题leetcode上可以直接测 +// 链接:https://leetcode.com/problems/longest-common-subsequence/ +public class Code04_LongestCommonSubsequence { + + public static int longestCommonSubsequence1(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + // 尝试 + return process1(str1, str2, str1.length - 1, str2.length - 1); + } + + // str1[0...i]和str2[0...j],这个范围上最长公共子序列长度是多少? + // 可能性分类: + // a) 最长公共子序列,一定不以str1[i]字符结尾、也一定不以str2[j]字符结尾 + // b) 最长公共子序列,可能以str1[i]字符结尾、但是一定不以str2[j]字符结尾 + // c) 最长公共子序列,一定不以str1[i]字符结尾、但是可能以str2[j]字符结尾 + // d) 最长公共子序列,必须以str1[i]字符结尾、也必须以str2[j]字符结尾 + // 注意:a)、b)、c)、d)并不是完全互斥的,他们可能会有重叠的情况 + // 但是可以肯定,答案不会超过这四种可能性的范围 + // 那么我们分别来看一下,这几种可能性怎么调用后续的递归。 + // a) 最长公共子序列,一定不以str1[i]字符结尾、也一定不以str2[j]字符结尾 + // 如果是这种情况,那么有没有str1[i]和str2[j]就根本不重要了,因为这两个字符一定没用啊 + // 所以砍掉这两个字符,最长公共子序列 = str1[0...i-1]与str2[0...j-1]的最长公共子序列长度(后续递归) + // b) 最长公共子序列,可能以str1[i]字符结尾、但是一定不以str2[j]字符结尾 + // 如果是这种情况,那么我们可以确定str2[j]一定没有用,要砍掉;但是str1[i]可能有用,所以要保留 + // 所以,最长公共子序列 = str1[0...i]与str2[0...j-1]的最长公共子序列长度(后续递归) + // c) 最长公共子序列,一定不以str1[i]字符结尾、但是可能以str2[j]字符结尾 + // 跟上面分析过程类似,最长公共子序列 = str1[0...i-1]与str2[0...j]的最长公共子序列长度(后续递归) + // d) 最长公共子序列,必须以str1[i]字符结尾、也必须以str2[j]字符结尾 + // 同时可以看到,可能性d)存在的条件,一定是在str1[i] == str2[j]的情况下,才成立的 + // 所以,最长公共子序列总长度 = str1[0...i-1]与str2[0...j-1]的最长公共子序列长度(后续递归) + 1(共同的结尾) + // 综上,四种情况已经穷尽了所有可能性。四种情况中取最大即可 + // 其中b)、c)一定参与最大值的比较, + // 当str1[i] == str2[j]时,a)一定比d)小,所以d)参与 + // 当str1[i] != str2[j]时,d)压根不存在,所以a)参与 + // 但是再次注意了! + // a)是:str1[0...i-1]与str2[0...j-1]的最长公共子序列长度 + // b)是:str1[0...i]与str2[0...j-1]的最长公共子序列长度 + // c)是:str1[0...i-1]与str2[0...j]的最长公共子序列长度 + // a)中str1的范围 < b)中str1的范围,a)中str2的范围 == b)中str2的范围 + // 所以a)不用求也知道,它比不过b)啊,因为有一个样本的范围比b)小啊! + // a)中str1的范围 == c)中str1的范围,a)中str2的范围 < c)中str2的范围 + // 所以a)不用求也知道,它比不过c)啊,因为有一个样本的范围比c)小啊! + // 至此,可以知道,a)就是个垃圾,有它没它,都不影响最大值的决策 + // 所以,当str1[i] == str2[j]时,b)、c)、d)中选出最大值 + // 当str1[i] != str2[j]时,b)、c)中选出最大值 + public static int process1(char[] str1, char[] str2, int i, int j) { + if (i == 0 && j == 0) { + // str1[0..0]和str2[0..0],都只剩一个字符了 + // 那如果字符相等,公共子序列长度就是1,不相等就是0 + // 这显而易见 + return str1[i] == str2[j] ? 1 : 0; + } else if (i == 0) { + // 这里的情况为: + // str1[0...0]和str2[0...j],str1只剩1个字符了,但是str2不只一个字符 + // 因为str1只剩一个字符了,所以str1[0...0]和str2[0...j]公共子序列最多长度为1 + // 如果str1[0] == str2[j],那么此时相等已经找到了!公共子序列长度就是1,也不可能更大了 + // 如果str1[0] != str2[j],只是此时不相等而已, + // 那么str2[0...j-1]上有没有字符等于str1[0]呢?不知道,所以递归继续找 + if (str1[i] == str2[j]) { + return 1; + } else { + return process1(str1, str2, i, j - 1); + } + } else if (j == 0) { + // 和上面的else if同理 + // str1[0...i]和str2[0...0],str2只剩1个字符了,但是str1不只一个字符 + // 因为str2只剩一个字符了,所以str1[0...i]和str2[0...0]公共子序列最多长度为1 + // 如果str1[i] == str2[0],那么此时相等已经找到了!公共子序列长度就是1,也不可能更大了 + // 如果str1[i] != str2[0],只是此时不相等而已, + // 那么str1[0...i-1]上有没有字符等于str2[0]呢?不知道,所以递归继续找 + if (str1[i] == str2[j]) { + return 1; + } else { + return process1(str1, str2, i - 1, j); + } + } else { // i != 0 && j != 0 + // 这里的情况为: + // str1[0...i]和str2[0...i],str1和str2都不只一个字符 + // 看函数开始之前的注释部分 + // p1就是可能性c) + int p1 = process1(str1, str2, i - 1, j); + // p2就是可能性b) + int p2 = process1(str1, str2, i, j - 1); + // p3就是可能性d),如果可能性d)存在,即str1[i] == str2[j],那么p3就求出来,参与pk + // 如果可能性d)不存在,即str1[i] != str2[j],那么让p3等于0,然后去参与pk,反正不影响 + int p3 = str1[i] == str2[j] ? (1 + process1(str1, str2, i - 1, j - 1)) : 0; + return Math.max(p1, Math.max(p2, p3)); + } + } + + public static int longestCommonSubsequence2(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length; + int M = str2.length; + int[][] dp = new int[N][M]; + dp[0][0] = str1[0] == str2[0] ? 1 : 0; + for (int j = 1; j < M; j++) { + dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1]; + } + for (int i = 1; i < N; i++) { + dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + int p1 = dp[i - 1][j]; + int p2 = dp[i][j - 1]; + int p3 = str1[i] == str2[j] ? (1 + dp[i - 1][j - 1]) : 0; + dp[i][j] = Math.max(p1, Math.max(p2, p3)); + } + } + return dp[N - 1][M - 1]; + } + +} diff --git a/体系学习班/class20/Code01_PalindromeSubsequence.java b/体系学习班/class20/Code01_PalindromeSubsequence.java new file mode 100644 index 0000000..dd38429 --- /dev/null +++ b/体系学习班/class20/Code01_PalindromeSubsequence.java @@ -0,0 +1,121 @@ +package class20; + +// 测试链接:https://leetcode.com/problems/longest-palindromic-subsequence/ +public class Code01_PalindromeSubsequence { + + public static int lpsl1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + return f(str, 0, str.length - 1); + } + + // str[L..R]最长回文子序列长度返回 + public static int f(char[] str, int L, int R) { + if (L == R) { + return 1; + } + if (L == R - 1) { + return str[L] == str[R] ? 2 : 1; + } + int p1 = f(str, L + 1, R - 1); + int p2 = f(str, L, R - 1); + int p3 = f(str, L + 1, R); + int p4 = str[L] != str[R] ? 0 : (2 + f(str, L + 1, R - 1)); + return Math.max(Math.max(p1, p2), Math.max(p3, p4)); + } + + public static int lpsl2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + dp[N - 1][N - 1] = 1; + for (int i = 0; i < N - 1; i++) { + dp[i][i] = 1; + dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1; + } + for (int L = N - 3; L >= 0; L--) { + for (int R = L + 2; R < N; R++) { + dp[L][R] = Math.max(dp[L][R - 1], dp[L + 1][R]); + if (str[L] == str[R]) { + dp[L][R] = Math.max(dp[L][R], 2 + dp[L + 1][R - 1]); + } + } + } + return dp[0][N - 1]; + } + + public static int longestPalindromeSubseq1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + if (s.length() == 1) { + return 1; + } + char[] str = s.toCharArray(); + char[] reverse = reverse(str); + return longestCommonSubsequence(str, reverse); + } + + public static char[] reverse(char[] str) { + int N = str.length; + char[] reverse = new char[str.length]; + for (int i = 0; i < str.length; i++) { + reverse[--N] = str[i]; + } + return reverse; + } + + public static int longestCommonSubsequence(char[] str1, char[] str2) { + int N = str1.length; + int M = str2.length; + int[][] dp = new int[N][M]; + dp[0][0] = str1[0] == str2[0] ? 1 : 0; + for (int i = 1; i < N; i++) { + dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0]; + } + for (int j = 1; j < M; j++) { + dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + if (str1[i] == str2[j]) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1); + } + } + } + return dp[N - 1][M - 1]; + } + + public static int longestPalindromeSubseq2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + if (s.length() == 1) { + return 1; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + dp[N - 1][N - 1] = 1; + for (int i = 0; i < N - 1; i++) { + dp[i][i] = 1; + dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1; + } + for (int i = N - 3; i >= 0; i--) { + for (int j = i + 2; j < N; j++) { + dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]); + if (str[i] == str[j]) { + dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2); + } + } + } + return dp[0][N - 1]; + } + +} diff --git a/体系学习班/class20/Code02_HorseJump.java b/体系学习班/class20/Code02_HorseJump.java new file mode 100644 index 0000000..f96f4a1 --- /dev/null +++ b/体系学习班/class20/Code02_HorseJump.java @@ -0,0 +1,109 @@ +package class20; + +public class Code02_HorseJump { + + // 当前来到的位置是(x,y) + // 还剩下rest步需要跳 + // 跳完rest步,正好跳到a,b的方法数是多少? + // 10 * 9 + public static int jump(int a, int b, int k) { + return process(0, 0, k, a, b); + } + + public static int process(int x, int y, int rest, int a, int b) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + if (rest == 0) { + return (x == a && y == b) ? 1 : 0; + } + int ways = process(x + 2, y + 1, rest - 1, a, b); + ways += process(x + 1, y + 2, rest - 1, a, b); + ways += process(x - 1, y + 2, rest - 1, a, b); + ways += process(x - 2, y + 1, rest - 1, a, b); + ways += process(x - 2, y - 1, rest - 1, a, b); + ways += process(x - 1, y - 2, rest - 1, a, b); + ways += process(x + 1, y - 2, rest - 1, a, b); + ways += process(x + 2, y - 1, rest - 1, a, b); + return ways; + } + + public static int dp(int a, int b, int k) { + int[][][] dp = new int[10][9][k + 1]; + dp[a][b][0] = 1; + for (int rest = 1; rest <= k; rest++) { + for (int x = 0; x < 10; x++) { + for (int y = 0; y < 9; y++) { + int ways = pick(dp, x + 2, y + 1, rest - 1); + ways += pick(dp, x + 1, y + 2, rest - 1); + ways += pick(dp, x - 1, y + 2, rest - 1); + ways += pick(dp, x - 2, y + 1, rest - 1); + ways += pick(dp, x - 2, y - 1, rest - 1); + ways += pick(dp, x - 1, y - 2, rest - 1); + ways += pick(dp, x + 1, y - 2, rest - 1); + ways += pick(dp, x + 2, y - 1, rest - 1); + dp[x][y][rest] = ways; + } + } + } + return dp[0][0][k]; + } + + public static int pick(int[][][] dp, int x, int y, int rest) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + return dp[x][y][rest]; + } + + public static int ways(int a, int b, int step) { + return f(0, 0, step, a, b); + } + + public static int f(int i, int j, int step, int a, int b) { + if (i < 0 || i > 9 || j < 0 || j > 8) { + return 0; + } + if (step == 0) { + return (i == a && j == b) ? 1 : 0; + } + return f(i - 2, j + 1, step - 1, a, b) + f(i - 1, j + 2, step - 1, a, b) + f(i + 1, j + 2, step - 1, a, b) + + f(i + 2, j + 1, step - 1, a, b) + f(i + 2, j - 1, step - 1, a, b) + f(i + 1, j - 2, step - 1, a, b) + + f(i - 1, j - 2, step - 1, a, b) + f(i - 2, j - 1, step - 1, a, b); + + } + + public static int waysdp(int a, int b, int s) { + int[][][] dp = new int[10][9][s + 1]; + dp[a][b][0] = 1; + for (int step = 1; step <= s; step++) { // 按层来 + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 9; j++) { + dp[i][j][step] = getValue(dp, i - 2, j + 1, step - 1) + getValue(dp, i - 1, j + 2, step - 1) + + getValue(dp, i + 1, j + 2, step - 1) + getValue(dp, i + 2, j + 1, step - 1) + + getValue(dp, i + 2, j - 1, step - 1) + getValue(dp, i + 1, j - 2, step - 1) + + getValue(dp, i - 1, j - 2, step - 1) + getValue(dp, i - 2, j - 1, step - 1); + } + } + } + return dp[0][0][s]; + } + + // 在dp表中,得到dp[i][j][step]的值,但如果(i,j)位置越界的话,返回0; + public static int getValue(int[][][] dp, int i, int j, int step) { + if (i < 0 || i > 9 || j < 0 || j > 8) { + return 0; + } + return dp[i][j][step]; + } + + public static void main(String[] args) { + int x = 7; + int y = 7; + int step = 10; + System.out.println(ways(x, y, step)); + System.out.println(dp(x, y, step)); + + System.out.println(jump(x, y, step)); + } +} diff --git a/体系学习班/class20/Code03_Coffee.java b/体系学习班/class20/Code03_Coffee.java new file mode 100644 index 0000000..eb2b620 --- /dev/null +++ b/体系学习班/class20/Code03_Coffee.java @@ -0,0 +1,204 @@ +package class20; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.PriorityQueue; + +// 题目 +// 数组arr代表每一个咖啡机冲一杯咖啡的时间,每个咖啡机只能串行的制造咖啡。 +// 现在有n个人需要喝咖啡,只能用咖啡机来制造咖啡。 +// 认为每个人喝咖啡的时间非常短,冲好的时间即是喝完的时间。 +// 每个人喝完之后咖啡杯可以选择洗或者自然挥发干净,只有一台洗咖啡杯的机器,只能串行的洗咖啡杯。 +// 洗杯子的机器洗完一个杯子时间为a,任何一个杯子自然挥发干净的时间为b。 +// 四个参数:arr, n, a, b +// 假设时间点从0开始,返回所有人喝完咖啡并洗完咖啡杯的全部过程结束后,至少来到什么时间点。 +public class Code03_Coffee { + + // 验证的方法 + // 彻底的暴力 + // 很慢但是绝对正确 + public static int right(int[] arr, int n, int a, int b) { + int[] times = new int[arr.length]; + int[] drink = new int[n]; + return forceMake(arr, times, 0, drink, n, a, b); + } + + // 每个人暴力尝试用每一个咖啡机给自己做咖啡 + public static int forceMake(int[] arr, int[] times, int kth, int[] drink, int n, int a, int b) { + if (kth == n) { + int[] drinkSorted = Arrays.copyOf(drink, kth); + Arrays.sort(drinkSorted); + return forceWash(drinkSorted, a, b, 0, 0, 0); + } + int time = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + int work = arr[i]; + int pre = times[i]; + drink[kth] = pre + work; + times[i] = pre + work; + time = Math.min(time, forceMake(arr, times, kth + 1, drink, n, a, b)); + drink[kth] = 0; + times[i] = pre; + } + return time; + } + + public static int forceWash(int[] drinks, int a, int b, int index, int washLine, int time) { + if (index == drinks.length) { + return time; + } + // 选择一:当前index号咖啡杯,选择用洗咖啡机刷干净 + int wash = Math.max(drinks[index], washLine) + a; + int ans1 = forceWash(drinks, a, b, index + 1, wash, Math.max(wash, time)); + + // 选择二:当前index号咖啡杯,选择自然挥发 + int dry = drinks[index] + b; + int ans2 = forceWash(drinks, a, b, index + 1, washLine, Math.max(dry, time)); + return Math.min(ans1, ans2); + } + + // 以下为贪心+优良暴力 + public static class Machine { + public int timePoint; + public int workTime; + + public Machine(int t, int w) { + timePoint = t; + workTime = w; + } + } + + public static class MachineComparator implements Comparator { + + @Override + public int compare(Machine o1, Machine o2) { + return (o1.timePoint + o1.workTime) - (o2.timePoint + o2.workTime); + } + + } + + // 优良一点的暴力尝试的方法 + public static int minTime1(int[] arr, int n, int a, int b) { + PriorityQueue heap = new PriorityQueue(new MachineComparator()); + for (int i = 0; i < arr.length; i++) { + heap.add(new Machine(0, arr[i])); + } + int[] drinks = new int[n]; + for (int i = 0; i < n; i++) { + Machine cur = heap.poll(); + cur.timePoint += cur.workTime; + drinks[i] = cur.timePoint; + heap.add(cur); + } + return bestTime(drinks, a, b, 0, 0); + } + + // drinks 所有杯子可以开始洗的时间 + // wash 单杯洗干净的时间(串行) + // air 挥发干净的时间(并行) + // free 洗的机器什么时候可用 + // drinks[index.....]都变干净,最早的结束时间(返回) + public static int bestTime(int[] drinks, int wash, int air, int index, int free) { + if (index == drinks.length) { + return 0; + } + // index号杯子 决定洗 + int selfClean1 = Math.max(drinks[index], free) + wash; + int restClean1 = bestTime(drinks, wash, air, index + 1, selfClean1); + int p1 = Math.max(selfClean1, restClean1); + + // index号杯子 决定挥发 + int selfClean2 = drinks[index] + air; + int restClean2 = bestTime(drinks, wash, air, index + 1, free); + int p2 = Math.max(selfClean2, restClean2); + return Math.min(p1, p2); + } + + // 贪心+优良尝试改成动态规划 + public static int minTime2(int[] arr, int n, int a, int b) { + PriorityQueue heap = new PriorityQueue(new MachineComparator()); + for (int i = 0; i < arr.length; i++) { + heap.add(new Machine(0, arr[i])); + } + int[] drinks = new int[n]; + for (int i = 0; i < n; i++) { + Machine cur = heap.poll(); + cur.timePoint += cur.workTime; + drinks[i] = cur.timePoint; + heap.add(cur); + } + return bestTimeDp(drinks, a, b); + } + + public static int bestTimeDp(int[] drinks, int wash, int air) { + int N = drinks.length; + int maxFree = 0; + for (int i = 0; i < drinks.length; i++) { + maxFree = Math.max(maxFree, drinks[i]) + wash; + } + int[][] dp = new int[N + 1][maxFree + 1]; + for (int index = N - 1; index >= 0; index--) { + for (int free = 0; free <= maxFree; free++) { + int selfClean1 = Math.max(drinks[index], free) + wash; + if (selfClean1 > maxFree) { + break; // 因为后面的也都不用填了 + } + // index号杯子 决定洗 + int restClean1 = dp[index + 1][selfClean1]; + int p1 = Math.max(selfClean1, restClean1); + // index号杯子 决定挥发 + int selfClean2 = drinks[index] + air; + int restClean2 = dp[index + 1][free]; + int p2 = Math.max(selfClean2, restClean2); + dp[index][free] = Math.min(p1, p2); + } + } + return dp[0][0]; + } + + // for test + public static int[] randomArray(int len, int max) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * max) + 1; + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + System.out.print("arr : "); + for (int j = 0; j < arr.length; j++) { + System.out.print(arr[j] + ", "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 10; + int max = 10; + int testTime = 10; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(len, max); + int n = (int) (Math.random() * 7) + 1; + int a = (int) (Math.random() * 7) + 1; + int b = (int) (Math.random() * 10) + 1; + int ans1 = right(arr, n, a, b); + int ans2 = minTime1(arr, n, a, b); + int ans3 = minTime2(arr, n, a, b); + if (ans1 != ans2 || ans2 != ans3) { + printArray(arr); + System.out.println("n : " + n); + System.out.println("a : " + a); + System.out.println("b : " + b); + System.out.println(ans1 + " , " + ans2 + " , " + ans3); + System.out.println("==============="); + break; + } + } + System.out.println("测试结束"); + + } + +} diff --git a/体系学习班/class21/Code01_MinPathSum.java b/体系学习班/class21/Code01_MinPathSum.java new file mode 100644 index 0000000..c537438 --- /dev/null +++ b/体系学习班/class21/Code01_MinPathSum.java @@ -0,0 +1,79 @@ +package class21; + +public class Code01_MinPathSum { + + public static int minPathSum1(int[][] m) { + if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) { + return 0; + } + int row = m.length; + int col = m[0].length; + int[][] dp = new int[row][col]; + dp[0][0] = m[0][0]; + for (int i = 1; i < row; i++) { + dp[i][0] = dp[i - 1][0] + m[i][0]; + } + for (int j = 1; j < col; j++) { + dp[0][j] = dp[0][j - 1] + m[0][j]; + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + m[i][j]; + } + } + return dp[row - 1][col - 1]; + } + + public static int minPathSum2(int[][] m) { + if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) { + return 0; + } + int row = m.length; + int col = m[0].length; + int[] dp = new int[col]; + dp[0] = m[0][0]; + for (int j = 1; j < col; j++) { + dp[j] = dp[j - 1] + m[0][j]; + } + for (int i = 1; i < row; i++) { + dp[0] += m[i][0]; + for (int j = 1; j < col; j++) { + dp[j] = Math.min(dp[j - 1], dp[j]) + m[i][j]; + } + } + return dp[col - 1]; + } + + // for test + public static int[][] generateRandomMatrix(int rowSize, int colSize) { + if (rowSize < 0 || colSize < 0) { + return null; + } + int[][] result = new int[rowSize][colSize]; + for (int i = 0; i != result.length; i++) { + for (int j = 0; j != result[0].length; j++) { + result[i][j] = (int) (Math.random() * 100); + } + } + return result; + } + + // for test + public static void printMatrix(int[][] matrix) { + for (int i = 0; i != matrix.length; i++) { + for (int j = 0; j != matrix[0].length; j++) { + System.out.print(matrix[i][j] + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int rowSize = 10; + int colSize = 10; + int[][] m = generateRandomMatrix(rowSize, colSize); + System.out.println(minPathSum1(m)); + System.out.println(minPathSum2(m)); + + } +} diff --git a/体系学习班/class21/Code02_CoinsWayEveryPaperDifferent.java b/体系学习班/class21/Code02_CoinsWayEveryPaperDifferent.java new file mode 100644 index 0000000..a162b06 --- /dev/null +++ b/体系学习班/class21/Code02_CoinsWayEveryPaperDifferent.java @@ -0,0 +1,77 @@ +package class21; + +public class Code02_CoinsWayEveryPaperDifferent { + + public static int coinWays(int[] arr, int aim) { + return process(arr, 0, aim); + } + + // arr[index....] 组成正好rest这么多的钱,有几种方法 + public static int process(int[] arr, int index, int rest) { + if (rest < 0) { + return 0; + } + if (index == arr.length) { // 没钱了! + return rest == 0 ? 1 : 0; + } else { + return process(arr, index + 1, rest) + process(arr, index + 1, rest - arr[index]); + } + } + + public static int dp(int[] arr, int aim) { + if (aim == 0) { + return 1; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest] + (rest - arr[index] >= 0 ? dp[index + 1][rest - arr[index]] : 0); + } + } + return dp[0][aim]; + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = coinWays(arr, aim); + int ans2 = dp(arr, aim); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class21/Code03_CoinsWayNoLimit.java b/体系学习班/class21/Code03_CoinsWayNoLimit.java new file mode 100644 index 0000000..7759d9c --- /dev/null +++ b/体系学习班/class21/Code03_CoinsWayNoLimit.java @@ -0,0 +1,108 @@ +package class21; + +public class Code03_CoinsWayNoLimit { + + public static int coinsWay(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + return process(arr, 0, aim); + } + + // arr[index....] 所有的面值,每一个面值都可以任意选择张数,组成正好rest这么多钱,方法数多少? + public static int process(int[] arr, int index, int rest) { + if (index == arr.length) { // 没钱了 + return rest == 0 ? 1 : 0; + } + int ways = 0; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + ways += process(arr, index + 1, rest - (zhang * arr[index])); + } + return ways; + } + + public static int dp1(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + int ways = 0; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + ways += dp[index + 1][rest - (zhang * arr[index])]; + } + dp[index][rest] = ways; + } + } + return dp[0][aim]; + } + + public static int dp2(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest]; + if (rest - arr[index] >= 0) { + dp[index][rest] += dp[index][rest - arr[index]]; + } + } + } + return dp[0][aim]; + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + boolean[] has = new boolean[maxValue + 1]; + for (int i = 0; i < N; i++) { + do { + arr[i] = (int) (Math.random() * maxValue) + 1; + } while (has[arr[i]]); + has[arr[i]] = true; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 10; + int maxValue = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = coinsWay(arr, aim); + int ans2 = dp1(arr, aim); + int ans3 = dp2(arr, aim); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class21/Code04_CoinsWaySameValueSamePapper.java b/体系学习班/class21/Code04_CoinsWaySameValueSamePapper.java new file mode 100644 index 0000000..f45c919 --- /dev/null +++ b/体系学习班/class21/Code04_CoinsWaySameValueSamePapper.java @@ -0,0 +1,148 @@ +package class21; + +import java.util.HashMap; +import java.util.Map.Entry; + +public class Code04_CoinsWaySameValueSamePapper { + + public static class Info { + public int[] coins; + public int[] zhangs; + + public Info(int[] c, int[] z) { + coins = c; + zhangs = z; + } + } + + public static Info getInfo(int[] arr) { + HashMap counts = new HashMap<>(); + for (int value : arr) { + if (!counts.containsKey(value)) { + counts.put(value, 1); + } else { + counts.put(value, counts.get(value) + 1); + } + } + int N = counts.size(); + int[] coins = new int[N]; + int[] zhangs = new int[N]; + int index = 0; + for (Entry entry : counts.entrySet()) { + coins[index] = entry.getKey(); + zhangs[index++] = entry.getValue(); + } + return new Info(coins, zhangs); + } + + public static int coinsWay(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + Info info = getInfo(arr); + return process(info.coins, info.zhangs, 0, aim); + } + + // coins 面值数组,正数且去重 + // zhangs 每种面值对应的张数 + public static int process(int[] coins, int[] zhangs, int index, int rest) { + if (index == coins.length) { + return rest == 0 ? 1 : 0; + } + int ways = 0; + for (int zhang = 0; zhang * coins[index] <= rest && zhang <= zhangs[index]; zhang++) { + ways += process(coins, zhangs, index + 1, rest - (zhang * coins[index])); + } + return ways; + } + + public static int dp1(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + Info info = getInfo(arr); + int[] coins = info.coins; + int[] zhangs = info.zhangs; + int N = coins.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + int ways = 0; + for (int zhang = 0; zhang * coins[index] <= rest && zhang <= zhangs[index]; zhang++) { + ways += dp[index + 1][rest - (zhang * coins[index])]; + } + dp[index][rest] = ways; + } + } + return dp[0][aim]; + } + + public static int dp2(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + Info info = getInfo(arr); + int[] coins = info.coins; + int[] zhangs = info.zhangs; + int N = coins.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest]; + if (rest - coins[index] >= 0) { + dp[index][rest] += dp[index][rest - coins[index]]; + } + if (rest - coins[index] * (zhangs[index] + 1) >= 0) { + dp[index][rest] -= dp[index + 1][rest - coins[index] * (zhangs[index] + 1)]; + } + } + } + return dp[0][aim]; + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 10; + int maxValue = 20; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = coinsWay(arr, aim); + int ans2 = dp1(arr, aim); + int ans3 = dp2(arr, aim); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class21/Code05_BobDie.java b/体系学习班/class21/Code05_BobDie.java new file mode 100644 index 0000000..439fffe --- /dev/null +++ b/体系学习班/class21/Code05_BobDie.java @@ -0,0 +1,58 @@ +package class21; + +public class Code05_BobDie { + + public static double livePosibility1(int row, int col, int k, int N, int M) { + return (double) process(row, col, k, N, M) / Math.pow(4, k); + } + + // 目前在row,col位置,还有rest步要走,走完了如果还在棋盘中就获得1个生存点,返回总的生存点数 + public static long process(int row, int col, int rest, int N, int M) { + if (row < 0 || row == N || col < 0 || col == M) { + return 0; + } + // 还在棋盘中! + if (rest == 0) { + return 1; + } + // 还在棋盘中!还有步数要走 + long up = process(row - 1, col, rest - 1, N, M); + long down = process(row + 1, col, rest - 1, N, M); + long left = process(row, col - 1, rest - 1, N, M); + long right = process(row, col + 1, rest - 1, N, M); + return up + down + left + right; + } + + public static double livePosibility2(int row, int col, int k, int N, int M) { + long[][][] dp = new long[N][M][k + 1]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + dp[i][j][0] = 1; + } + } + for (int rest = 1; rest <= k; rest++) { + for (int r = 0; r < N; r++) { + for (int c = 0; c < M; c++) { + dp[r][c][rest] = pick(dp, N, M, r - 1, c, rest - 1); + dp[r][c][rest] += pick(dp, N, M, r + 1, c, rest - 1); + dp[r][c][rest] += pick(dp, N, M, r, c - 1, rest - 1); + dp[r][c][rest] += pick(dp, N, M, r, c + 1, rest - 1); + } + } + } + return (double) dp[row][col][k] / Math.pow(4, k); + } + + public static long pick(long[][][] dp, int N, int M, int r, int c, int rest) { + if (r < 0 || r == N || c < 0 || c == M) { + return 0; + } + return dp[r][c][rest]; + } + + public static void main(String[] args) { + System.out.println(livePosibility1(6, 6, 10, 50, 50)); + System.out.println(livePosibility2(6, 6, 10, 50, 50)); + } + +} diff --git a/体系学习班/class22/Code01_KillMonster.java b/体系学习班/class22/Code01_KillMonster.java new file mode 100644 index 0000000..77fae7b --- /dev/null +++ b/体系学习班/class22/Code01_KillMonster.java @@ -0,0 +1,100 @@ +package class22; + +public class Code01_KillMonster { + + public static double right(int N, int M, int K) { + if (N < 1 || M < 1 || K < 1) { + return 0; + } + long all = (long) Math.pow(M + 1, K); + long kill = process(K, M, N); + return (double) ((double) kill / (double) all); + } + + // 怪兽还剩hp点血 + // 每次的伤害在[0~M]范围上 + // 还有times次可以砍 + // 返回砍死的情况数! + public static long process(int times, int M, int hp) { + if (times == 0) { + return hp <= 0 ? 1 : 0; + } + if (hp <= 0) { + return (long) Math.pow(M + 1, times); + } + long ways = 0; + for (int i = 0; i <= M; i++) { + ways += process(times - 1, M, hp - i); + } + return ways; + } + + public static double dp1(int N, int M, int K) { + if (N < 1 || M < 1 || K < 1) { + return 0; + } + long all = (long) Math.pow(M + 1, K); + long[][] dp = new long[K + 1][N + 1]; + dp[0][0] = 1; + for (int times = 1; times <= K; times++) { + dp[times][0] = (long) Math.pow(M + 1, times); + for (int hp = 1; hp <= N; hp++) { + long ways = 0; + for (int i = 0; i <= M; i++) { + if (hp - i >= 0) { + ways += dp[times - 1][hp - i]; + } else { + ways += (long) Math.pow(M + 1, times - 1); + } + } + dp[times][hp] = ways; + } + } + long kill = dp[K][N]; + return (double) ((double) kill / (double) all); + } + + public static double dp2(int N, int M, int K) { + if (N < 1 || M < 1 || K < 1) { + return 0; + } + long all = (long) Math.pow(M + 1, K); + long[][] dp = new long[K + 1][N + 1]; + dp[0][0] = 1; + for (int times = 1; times <= K; times++) { + dp[times][0] = (long) Math.pow(M + 1, times); + for (int hp = 1; hp <= N; hp++) { + dp[times][hp] = dp[times][hp - 1] + dp[times - 1][hp]; + if (hp - 1 - M >= 0) { + dp[times][hp] -= dp[times - 1][hp - 1 - M]; + } else { + dp[times][hp] -= Math.pow(M + 1, times - 1); + } + } + } + long kill = dp[K][N]; + return (double) ((double) kill / (double) all); + } + + public static void main(String[] args) { + int NMax = 10; + int MMax = 10; + int KMax = 10; + int testTime = 200; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * NMax); + int M = (int) (Math.random() * MMax); + int K = (int) (Math.random() * KMax); + double ans1 = right(N, M, K); + double ans2 = dp1(N, M, K); + double ans3 = dp2(N, M, K); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class22/Code02_MinCoinsNoLimit.java b/体系学习班/class22/Code02_MinCoinsNoLimit.java new file mode 100644 index 0000000..06e4843 --- /dev/null +++ b/体系学习班/class22/Code02_MinCoinsNoLimit.java @@ -0,0 +1,121 @@ +package class22; + +public class Code02_MinCoinsNoLimit { + + public static int minCoins(int[] arr, int aim) { + return process(arr, 0, aim); + } + + // arr[index...]面值,每种面值张数自由选择, + // 搞出rest正好这么多钱,返回最小张数 + // 拿Integer.MAX_VALUE标记怎么都搞定不了 + public static int process(int[] arr, int index, int rest) { + if (index == arr.length) { + return rest == 0 ? 0 : Integer.MAX_VALUE; + } else { + int ans = Integer.MAX_VALUE; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + int next = process(arr, index + 1, rest - zhang * arr[index]); + if (next != Integer.MAX_VALUE) { + ans = Math.min(ans, zhang + next); + } + } + return ans; + } + } + + public static int dp1(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + int ans = Integer.MAX_VALUE; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + int next = dp[index + 1][rest - zhang * arr[index]]; + if (next != Integer.MAX_VALUE) { + ans = Math.min(ans, zhang + next); + } + } + dp[index][rest] = ans; + } + } + return dp[0][aim]; + } + + public static int dp2(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest]; + if (rest - arr[index] >= 0 + && dp[index][rest - arr[index]] != Integer.MAX_VALUE) { + dp[index][rest] = Math.min(dp[index][rest], dp[index][rest - arr[index]] + 1); + } + } + } + return dp[0][aim]; + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + boolean[] has = new boolean[maxValue + 1]; + for (int i = 0; i < N; i++) { + do { + arr[i] = (int) (Math.random() * maxValue) + 1; + } while (has[arr[i]]); + has[arr[i]] = true; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 30; + int testTime = 300000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * maxLen); + int[] arr = randomArray(N, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = minCoins(arr, aim); + int ans2 = dp1(arr, aim); + int ans3 = dp2(arr, aim); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + } + +} diff --git a/体系学习班/class22/Code03_SplitNumber.java b/体系学习班/class22/Code03_SplitNumber.java new file mode 100644 index 0000000..23193bc --- /dev/null +++ b/体系学习班/class22/Code03_SplitNumber.java @@ -0,0 +1,85 @@ +package class22; + +public class Code03_SplitNumber { + + // n为正数 + public static int ways(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + return process(1, n); + } + + // 上一个拆出来的数是pre + // 还剩rest需要去拆 + // 返回拆解的方法数 + public static int process(int pre, int rest) { + if (rest == 0) { + return 1; + } + if (pre > rest) { + return 0; + } + int ways = 0; + for (int first = pre; first <= rest; first++) { + ways += process(first, rest - first); + } + return ways; + } + + public static int dp1(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + int[][] dp = new int[n + 1][n + 1]; + for (int pre = 1; pre <= n; pre++) { + dp[pre][0] = 1; + dp[pre][pre] = 1; + } + for (int pre = n - 1; pre >= 1; pre--) { + for (int rest = pre + 1; rest <= n; rest++) { + int ways = 0; + for (int first = pre; first <= rest; first++) { + ways += dp[first][rest - first]; + } + dp[pre][rest] = ways; + } + } + return dp[1][n]; + } + + public static int dp2(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + int[][] dp = new int[n + 1][n + 1]; + for (int pre = 1; pre <= n; pre++) { + dp[pre][0] = 1; + dp[pre][pre] = 1; + } + for (int pre = n - 1; pre >= 1; pre--) { + for (int rest = pre + 1; rest <= n; rest++) { + dp[pre][rest] = dp[pre + 1][rest]; + dp[pre][rest] += dp[pre][rest - pre]; + } + } + return dp[1][n]; + } + + public static void main(String[] args) { + int test = 39; + System.out.println(ways(test)); + System.out.println(dp1(test)); + System.out.println(dp2(test)); + } + +} diff --git a/体系学习班/class23/Code01_SplitSumClosed.java b/体系学习班/class23/Code01_SplitSumClosed.java new file mode 100644 index 0000000..6f82c49 --- /dev/null +++ b/体系学习班/class23/Code01_SplitSumClosed.java @@ -0,0 +1,94 @@ +package class23; + +public class Code01_SplitSumClosed { + + public static int right(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int sum = 0; + for (int num : arr) { + sum += num; + } + return process(arr, 0, sum / 2); + } + + // arr[i...]可以自由选择,请返回累加和尽量接近rest,但不能超过rest的情况下,最接近的累加和是多少? + public static int process(int[] arr, int i, int rest) { + if (i == arr.length) { + return 0; + } else { // 还有数,arr[i]这个数 + // 可能性1,不使用arr[i] + int p1 = process(arr, i + 1, rest); + // 可能性2,要使用arr[i] + int p2 = 0; + if (arr[i] <= rest) { + p2 = arr[i] + process(arr, i + 1, rest - arr[i]); + } + return Math.max(p1, p2); + } + } + + public static int dp(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int sum = 0; + for (int num : arr) { + sum += num; + } + sum /= 2; + int N = arr.length; + int[][] dp = new int[N + 1][sum + 1]; + for (int i = N - 1; i >= 0; i--) { + for (int rest = 0; rest <= sum; rest++) { + // 可能性1,不使用arr[i] + int p1 = dp[i + 1][rest]; + // 可能性2,要使用arr[i] + int p2 = 0; + if (arr[i] <= rest) { + p2 = arr[i] + dp[i + 1][rest - arr[i]]; + } + dp[i][rest] = Math.max(p1, p2); + } + } + return dp[0][sum]; + } + + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * value); + } + return arr; + } + + public static void printArray(int[] arr) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 50; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * maxLen); + int[] arr = randomArray(len, maxValue); + int ans1 = right(arr); + int ans2 = dp(arr); + if (ans1 != ans2) { + printArray(arr); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class23/Code02_SplitSumClosedSizeHalf.java b/体系学习班/class23/Code02_SplitSumClosedSizeHalf.java new file mode 100644 index 0000000..0da40fb --- /dev/null +++ b/体系学习班/class23/Code02_SplitSumClosedSizeHalf.java @@ -0,0 +1,243 @@ +package class23; + +public class Code02_SplitSumClosedSizeHalf { + + public static int right(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int sum = 0; + for (int num : arr) { + sum += num; + } + if ((arr.length & 1) == 0) { + return process(arr, 0, arr.length / 2, sum / 2); + } else { + return Math.max(process(arr, 0, arr.length / 2, sum / 2), process(arr, 0, arr.length / 2 + 1, sum / 2)); + } + } + + // arr[i....]自由选择,挑选的个数一定要是picks个,累加和<=rest, 离rest最近的返回 + public static int process(int[] arr, int i, int picks, int rest) { + if (i == arr.length) { + return picks == 0 ? 0 : -1; + } else { + int p1 = process(arr, i + 1, picks, rest); + // 就是要使用arr[i]这个数 + int p2 = -1; + int next = -1; + if (arr[i] <= rest) { + next = process(arr, i + 1, picks - 1, rest - arr[i]); + } + if (next != -1) { + p2 = arr[i] + next; + } + return Math.max(p1, p2); + } + } + + public static int dp(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int sum = 0; + for (int num : arr) { + sum += num; + } + sum /= 2; + int N = arr.length; + int M = (N + 1) / 2; + int[][][] dp = new int[N + 1][M + 1][sum + 1]; + for (int i = 0; i <= N; i++) { + for (int j = 0; j <= M; j++) { + for (int k = 0; k <= sum; k++) { + dp[i][j][k] = -1; + } + } + } + for (int rest = 0; rest <= sum; rest++) { + dp[N][0][rest] = 0; + } + for (int i = N - 1; i >= 0; i--) { + for (int picks = 0; picks <= M; picks++) { + for (int rest = 0; rest <= sum; rest++) { + int p1 = dp[i + 1][picks][rest]; + // 就是要使用arr[i]这个数 + int p2 = -1; + int next = -1; + if (picks - 1 >= 0 && arr[i] <= rest) { + next = dp[i + 1][picks - 1][rest - arr[i]]; + } + if (next != -1) { + p2 = arr[i] + next; + } + dp[i][picks][rest] = Math.max(p1, p2); + } + } + } + if ((arr.length & 1) == 0) { + return dp[0][arr.length / 2][sum]; + } else { + return Math.max(dp[0][arr.length / 2][sum], dp[0][(arr.length / 2) + 1][sum]); + } + } + +// public static int right(int[] arr) { +// if (arr == null || arr.length < 2) { +// return 0; +// } +// int sum = 0; +// for (int num : arr) { +// sum += num; +// } +// return process(arr, 0, 0, sum >> 1); +// } +// +// public static int process(int[] arr, int i, int picks, int rest) { +// if (i == arr.length) { +// if ((arr.length & 1) == 0) { +// return picks == (arr.length >> 1) ? 0 : -1; +// } else { +// return (picks == (arr.length >> 1) || picks == (arr.length >> 1) + 1) ? 0 : -1; +// } +// } +// int p1 = process(arr, i + 1, picks, rest); +// int p2 = -1; +// int next2 = -1; +// if (arr[i] <= rest) { +// next2 = process(arr, i + 1, picks + 1, rest - arr[i]); +// } +// if (next2 != -1) { +// p2 = arr[i] + next2; +// } +// return Math.max(p1, p2); +// } +// +// public static int dp1(int[] arr) { +// if (arr == null || arr.length < 2) { +// return 0; +// } +// int sum = 0; +// for (int num : arr) { +// sum += num; +// } +// sum >>= 1; +// int N = arr.length; +// int M = (arr.length + 1) >> 1; +// int[][][] dp = new int[N + 1][M + 1][sum + 1]; +// for (int i = 0; i <= N; i++) { +// for (int j = 0; j <= M; j++) { +// for (int k = 0; k <= sum; k++) { +// dp[i][j][k] = -1; +// } +// } +// } +// for (int k = 0; k <= sum; k++) { +// dp[N][M][k] = 0; +// } +// if ((arr.length & 1) != 0) { +// for (int k = 0; k <= sum; k++) { +// dp[N][M - 1][k] = 0; +// } +// } +// for (int i = N - 1; i >= 0; i--) { +// for (int picks = 0; picks <= M; picks++) { +// for (int rest = 0; rest <= sum; rest++) { +// int p1 = dp[i + 1][picks][rest]; +// int p2 = -1; +// int next2 = -1; +// if (picks + 1 <= M && arr[i] <= rest) { +// next2 = dp[i + 1][picks + 1][rest - arr[i]]; +// } +// if (next2 != -1) { +// p2 = arr[i] + next2; +// } +// dp[i][picks][rest] = Math.max(p1, p2); +// } +// } +// } +// return dp[0][0][sum]; +// } + + public static int dp2(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int sum = 0; + for (int num : arr) { + sum += num; + } + sum >>= 1; + int N = arr.length; + int M = (arr.length + 1) >> 1; + int[][][] dp = new int[N][M + 1][sum + 1]; + for (int i = 0; i < N; i++) { + for (int j = 0; j <= M; j++) { + for (int k = 0; k <= sum; k++) { + dp[i][j][k] = Integer.MIN_VALUE; + } + } + } + for (int i = 0; i < N; i++) { + for (int k = 0; k <= sum; k++) { + dp[i][0][k] = 0; + } + } + for (int k = 0; k <= sum; k++) { + dp[0][1][k] = arr[0] <= k ? arr[0] : Integer.MIN_VALUE; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j <= Math.min(i + 1, M); j++) { + for (int k = 0; k <= sum; k++) { + dp[i][j][k] = dp[i - 1][j][k]; + if (k - arr[i] >= 0) { + dp[i][j][k] = Math.max(dp[i][j][k], dp[i - 1][j - 1][k - arr[i]] + arr[i]); + } + } + } + } + return Math.max(dp[N - 1][M][sum], dp[N - 1][N - M][sum]); + } + + // for test + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * value); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int maxLen = 10; + int maxValue = 50; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * maxLen); + int[] arr = randomArray(len, maxValue); + int ans1 = right(arr); + int ans2 = dp(arr); + int ans3 = dp2(arr); + if (ans1 != ans2 || ans1 != ans3) { + printArray(arr); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } + +} \ No newline at end of file diff --git a/体系学习班/class23/Code03_NQueens.java b/体系学习班/class23/Code03_NQueens.java new file mode 100644 index 0000000..a9e454a --- /dev/null +++ b/体系学习班/class23/Code03_NQueens.java @@ -0,0 +1,89 @@ +package class23; + +public class Code03_NQueens { + + public static int num1(int n) { + if (n < 1) { + return 0; + } + int[] record = new int[n]; + return process1(0, record, n); + } + + // 当前来到i行,一共是0~N-1行 + // 在i行上放皇后,所有列都尝试 + // 必须要保证跟之前所有的皇后不打架 + // int[] record record[x] = y 之前的第x行的皇后,放在了y列上 + // 返回:不关心i以上发生了什么,i.... 后续有多少合法的方法数 + public static int process1(int i, int[] record, int n) { + if (i == n) { + return 1; + } + int res = 0; + // i行的皇后,放哪一列呢?j列, + for (int j = 0; j < n; j++) { + if (isValid(record, i, j)) { + record[i] = j; + res += process1(i + 1, record, n); + } + } + return res; + } + + public static boolean isValid(int[] record, int i, int j) { + // 0..i-1 + for (int k = 0; k < i; k++) { + if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) { + return false; + } + } + return true; + } + + // 请不要超过32皇后问题 + public static int num2(int n) { + if (n < 1 || n > 32) { + return 0; + } + // 如果你是13皇后问题,limit 最右13个1,其他都是0 + int limit = n == 32 ? -1 : (1 << n) - 1; + return process2(limit, 0, 0, 0); + } + + // 7皇后问题 + // limit : 0....0 1 1 1 1 1 1 1 + // 之前皇后的列影响:colLim + // 之前皇后的左下对角线影响:leftDiaLim + // 之前皇后的右下对角线影响:rightDiaLim + public static int process2(int limit, int colLim, int leftDiaLim, int rightDiaLim) { + if (colLim == limit) { + return 1; + } + // pos中所有是1的位置,是你可以去尝试皇后的位置 + int pos = limit & (~(colLim | leftDiaLim | rightDiaLim)); + int mostRightOne = 0; + int res = 0; + while (pos != 0) { + mostRightOne = pos & (~pos + 1); + pos = pos - mostRightOne; + res += process2(limit, colLim | mostRightOne, (leftDiaLim | mostRightOne) << 1, + (rightDiaLim | mostRightOne) >>> 1); + } + return res; + } + + public static void main(String[] args) { + int n = 15; + + long start = System.currentTimeMillis(); + System.out.println(num2(n)); + long end = System.currentTimeMillis(); + System.out.println("cost time: " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + System.out.println(num1(n)); + end = System.currentTimeMillis(); + System.out.println("cost time: " + (end - start) + "ms"); + + } +} diff --git a/体系学习班/class24/Code01_SlidingWindowMaxArray.java b/体系学习班/class24/Code01_SlidingWindowMaxArray.java new file mode 100644 index 0000000..d115951 --- /dev/null +++ b/体系学习班/class24/Code01_SlidingWindowMaxArray.java @@ -0,0 +1,99 @@ +package class24; + +import java.util.LinkedList; + +public class Code01_SlidingWindowMaxArray { + + // 暴力的对数器方法 + public static int[] right(int[] arr, int w) { + if (arr == null || w < 1 || arr.length < w) { + return null; + } + int N = arr.length; + int[] res = new int[N - w + 1]; + int index = 0; + int L = 0; + int R = w - 1; + while (R < N) { + int max = arr[L]; + for (int i = L + 1; i <= R; i++) { + max = Math.max(max, arr[i]); + + } + res[index++] = max; + L++; + R++; + } + return res; + } + + public static int[] getMaxWindow(int[] arr, int w) { + if (arr == null || w < 1 || arr.length < w) { + return null; + } + // qmax 窗口最大值的更新结构 + // 放下标 + LinkedList qmax = new LinkedList(); + int[] res = new int[arr.length - w + 1]; + int index = 0; + for (int R = 0; R < arr.length; R++) { + while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]) { + qmax.pollLast(); + } + qmax.addLast(R); + if (qmax.peekFirst() == R - w) { + qmax.pollFirst(); + } + if (R >= w - 1) { + res[index++] = arr[qmax.peekFirst()]; + } + } + return res; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * (maxValue + 1)); + } + return arr; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + int testTime = 100000; + int maxSize = 100; + int maxValue = 100; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int w = (int) (Math.random() * (arr.length + 1)); + int[] ans1 = getMaxWindow(arr, w); + int[] ans2 = right(arr, w); + if (!isEqual(ans1, ans2)) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class24/Code02_AllLessNumSubArray.java b/体系学习班/class24/Code02_AllLessNumSubArray.java new file mode 100644 index 0000000..7bbd5de --- /dev/null +++ b/体系学习班/class24/Code02_AllLessNumSubArray.java @@ -0,0 +1,109 @@ +package class24; + +import java.util.LinkedList; + +public class Code02_AllLessNumSubArray { + + // 暴力的对数器方法 + public static int right(int[] arr, int sum) { + if (arr == null || arr.length == 0 || sum < 0) { + return 0; + } + int N = arr.length; + int count = 0; + for (int L = 0; L < N; L++) { + for (int R = L; R < N; R++) { + int max = arr[L]; + int min = arr[L]; + for (int i = L + 1; i <= R; i++) { + max = Math.max(max, arr[i]); + min = Math.min(min, arr[i]); + } + if (max - min <= sum) { + count++; + } + } + } + return count; + } + + public static int num(int[] arr, int sum) { + if (arr == null || arr.length == 0 || sum < 0) { + return 0; + } + int N = arr.length; + int count = 0; + LinkedList maxWindow = new LinkedList<>(); + LinkedList minWindow = new LinkedList<>(); + int R = 0; + for (int L = 0; L < N; L++) { + while (R < N) { + while (!maxWindow.isEmpty() && arr[maxWindow.peekLast()] <= arr[R]) { + maxWindow.pollLast(); + } + maxWindow.addLast(R); + while (!minWindow.isEmpty() && arr[minWindow.peekLast()] >= arr[R]) { + minWindow.pollLast(); + } + minWindow.addLast(R); + if (arr[maxWindow.peekFirst()] - arr[minWindow.peekFirst()] > sum) { + break; + } else { + R++; + } + } + count += R - L; + if (maxWindow.peekFirst() == L) { + maxWindow.pollFirst(); + } + if (minWindow.peekFirst() == L) { + minWindow.pollFirst(); + } + } + return count; + } + + // for test + public static int[] generateRandomArray(int maxLen, int maxValue) { + int len = (int) (Math.random() * (maxLen + 1)); + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * (maxValue + 1)) - (int) (Math.random() * (maxValue + 1)); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr != null) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int maxLen = 100; + int maxValue = 200; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxLen, maxValue); + int sum = (int) (Math.random() * (maxValue + 1)); + int ans1 = right(arr, sum); + int ans2 = num(arr, sum); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(sum); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + + } + +} diff --git a/体系学习班/class24/Code03_GasStation.java b/体系学习班/class24/Code03_GasStation.java new file mode 100644 index 0000000..b80906f --- /dev/null +++ b/体系学习班/class24/Code03_GasStation.java @@ -0,0 +1,53 @@ +package class24; + +import java.util.LinkedList; + +// 测试链接:https://leetcode.com/problems/gas-station +public class Code03_GasStation { + + // 这个方法的时间复杂度O(N),额外空间复杂度O(N) + public static int canCompleteCircuit(int[] gas, int[] cost) { + boolean[] good = goodArray(gas, cost); + for (int i = 0; i < gas.length; i++) { + if (good[i]) { + return i; + } + } + return -1; + } + + public static boolean[] goodArray(int[] g, int[] c) { + int N = g.length; + int M = N << 1; + int[] arr = new int[M]; + for (int i = 0; i < N; i++) { + arr[i] = g[i] - c[i]; + arr[i + N] = g[i] - c[i]; + } + for (int i = 1; i < M; i++) { + arr[i] += arr[i - 1]; + } + LinkedList w = new LinkedList<>(); + for (int i = 0; i < N; i++) { + while (!w.isEmpty() && arr[w.peekLast()] >= arr[i]) { + w.pollLast(); + } + w.addLast(i); + } + boolean[] ans = new boolean[N]; + for (int offset = 0, i = 0, j = N; j < M; offset = arr[i++], j++) { + if (arr[w.peekFirst()] - offset >= 0) { + ans[i] = true; + } + if (w.peekFirst() == i) { + w.pollFirst(); + } + while (!w.isEmpty() && arr[w.peekLast()] >= arr[j]) { + w.pollLast(); + } + w.addLast(j); + } + return ans; + } + +} diff --git a/体系学习班/class24/Code04_MinCoinsOnePaper.java b/体系学习班/class24/Code04_MinCoinsOnePaper.java new file mode 100644 index 0000000..cfda95a --- /dev/null +++ b/体系学习班/class24/Code04_MinCoinsOnePaper.java @@ -0,0 +1,250 @@ +package class24; + +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.LinkedList; + +public class Code04_MinCoinsOnePaper { + + public static int minCoins(int[] arr, int aim) { + return process(arr, 0, aim); + } + + public static int process(int[] arr, int index, int rest) { + if (rest < 0) { + return Integer.MAX_VALUE; + } + if (index == arr.length) { + return rest == 0 ? 0 : Integer.MAX_VALUE; + } else { + int p1 = process(arr, index + 1, rest); + int p2 = process(arr, index + 1, rest - arr[index]); + if (p2 != Integer.MAX_VALUE) { + p2++; + } + return Math.min(p1, p2); + } + } + + // dp1时间复杂度为:O(arr长度 * aim) + public static int dp1(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + int p1 = dp[index + 1][rest]; + int p2 = rest - arr[index] >= 0 ? dp[index + 1][rest - arr[index]] : Integer.MAX_VALUE; + if (p2 != Integer.MAX_VALUE) { + p2++; + } + dp[index][rest] = Math.min(p1, p2); + } + } + return dp[0][aim]; + } + + public static class Info { + public int[] coins; + public int[] zhangs; + + public Info(int[] c, int[] z) { + coins = c; + zhangs = z; + } + } + + public static Info getInfo(int[] arr) { + HashMap counts = new HashMap<>(); + for (int value : arr) { + if (!counts.containsKey(value)) { + counts.put(value, 1); + } else { + counts.put(value, counts.get(value) + 1); + } + } + int N = counts.size(); + int[] coins = new int[N]; + int[] zhangs = new int[N]; + int index = 0; + for (Entry entry : counts.entrySet()) { + coins[index] = entry.getKey(); + zhangs[index++] = entry.getValue(); + } + return new Info(coins, zhangs); + } + + // dp2时间复杂度为:O(arr长度) + O(货币种数 * aim * 每种货币的平均张数) + public static int dp2(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + // 得到info时间复杂度O(arr长度) + Info info = getInfo(arr); + int[] coins = info.coins; + int[] zhangs = info.zhangs; + int N = coins.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + // 这三层for循环,时间复杂度为O(货币种数 * aim * 每种货币的平均张数) + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest]; + for (int zhang = 1; zhang * coins[index] <= aim && zhang <= zhangs[index]; zhang++) { + if (rest - zhang * coins[index] >= 0 + && dp[index + 1][rest - zhang * coins[index]] != Integer.MAX_VALUE) { + dp[index][rest] = Math.min(dp[index][rest], zhang + dp[index + 1][rest - zhang * coins[index]]); + } + } + } + } + return dp[0][aim]; + } + + // dp3时间复杂度为:O(arr长度) + O(货币种数 * aim) + // 优化需要用到窗口内最小值的更新结构 + public static int dp3(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + // 得到info时间复杂度O(arr长度) + Info info = getInfo(arr); + int[] c = info.coins; + int[] z = info.zhangs; + int N = c.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + // 虽然是嵌套了很多循环,但是时间复杂度为O(货币种数 * aim) + // 因为用了窗口内最小值的更新结构 + for (int i = N - 1; i >= 0; i--) { + for (int mod = 0; mod < Math.min(aim + 1, c[i]); mod++) { + // 当前面值 X + // mod mod + x mod + 2*x mod + 3 * x + LinkedList w = new LinkedList<>(); + w.add(mod); + dp[i][mod] = dp[i + 1][mod]; + for (int r = mod + c[i]; r <= aim; r += c[i]) { + while (!w.isEmpty() && (dp[i + 1][w.peekLast()] == Integer.MAX_VALUE + || dp[i + 1][w.peekLast()] + compensate(w.peekLast(), r, c[i]) >= dp[i + 1][r])) { + w.pollLast(); + } + w.addLast(r); + int overdue = r - c[i] * (z[i] + 1); + if (w.peekFirst() == overdue) { + w.pollFirst(); + } + dp[i][r] = dp[i + 1][w.peekFirst()] + compensate(w.peekFirst(), r, c[i]); + } + } + } + return dp[0][aim]; + } + + public static int compensate(int pre, int cur, int coin) { + return (cur - pre) / coin; + } + + // 为了测试 + public static int[] randomArray(int N, int maxValue) { + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 30; + int testTime = 300000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * maxLen); + int[] arr = randomArray(N, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = minCoins(arr, aim); + int ans2 = dp1(arr, aim); + int ans3 = dp2(arr, aim); + int ans4 = dp3(arr, aim); + if (ans1 != ans2 || ans3 != ans4 || ans1 != ans3) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println(ans4); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("=========="); + + int aim = 0; + int[] arr = null; + long start; + long end; + int ans2; + int ans3; + + System.out.println("性能测试开始"); + maxLen = 30000; + maxValue = 20; + aim = 60000; + arr = randomArray(maxLen, maxValue); + + start = System.currentTimeMillis(); + ans2 = dp2(arr, aim); + end = System.currentTimeMillis(); + System.out.println("dp2答案 : " + ans2 + ", dp2运行时间 : " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + ans3 = dp3(arr, aim); + end = System.currentTimeMillis(); + System.out.println("dp3答案 : " + ans3 + ", dp3运行时间 : " + (end - start) + " ms"); + System.out.println("性能测试结束"); + + System.out.println("==========="); + + System.out.println("货币大量重复出现情况下,"); + System.out.println("大数据量测试dp3开始"); + maxLen = 20000000; + aim = 10000; + maxValue = 10000; + arr = randomArray(maxLen, maxValue); + start = System.currentTimeMillis(); + ans3 = dp3(arr, aim); + end = System.currentTimeMillis(); + System.out.println("dp3运行时间 : " + (end - start) + " ms"); + System.out.println("大数据量测试dp3结束"); + + System.out.println("==========="); + + System.out.println("当货币很少出现重复,dp2比dp3有常数时间优势"); + System.out.println("当货币大量出现重复,dp3时间复杂度明显优于dp2"); + System.out.println("dp3的优化用到了窗口内最小值的更新结构"); + } + +} diff --git a/体系学习班/class25/Code01_MonotonousStack.java b/体系学习班/class25/Code01_MonotonousStack.java new file mode 100644 index 0000000..b823a30 --- /dev/null +++ b/体系学习班/class25/Code01_MonotonousStack.java @@ -0,0 +1,165 @@ +package class25; + +import java.util.List; +import java.util.ArrayList; +import java.util.Stack; + +public class Code01_MonotonousStack { + + // arr = [ 3, 1, 2, 3] + // 0 1 2 3 + // [ + // 0 : [-1, 1] + // 1 : [-1, -1] + // 2 : [ 1, -1] + // 3 : [ 2, -1] + // ] + public static int[][] getNearLessNoRepeat(int[] arr) { + int[][] res = new int[arr.length][2]; + // 只存位置! + Stack stack = new Stack<>(); + for (int i = 0; i < arr.length; i++) { // 当遍历到i位置的数,arr[i] + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + int j = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); + res[j][0] = leftLessIndex; + res[j][1] = i; + } + stack.push(i); + } + while (!stack.isEmpty()) { + int j = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); + res[j][0] = leftLessIndex; + res[j][1] = -1; + } + return res; + } + + public static int[][] getNearLess(int[] arr) { + int[][] res = new int[arr.length][2]; + Stack> stack = new Stack<>(); + for (int i = 0; i < arr.length; i++) { // i -> arr[i] 进栈 + while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) { + List popIs = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1); + for (Integer popi : popIs) { + res[popi][0] = leftLessIndex; + res[popi][1] = i; + } + } + if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) { + stack.peek().add(Integer.valueOf(i)); + } else { + ArrayList list = new ArrayList<>(); + list.add(i); + stack.push(list); + } + } + while (!stack.isEmpty()) { + List popIs = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1); + for (Integer popi : popIs) { + res[popi][0] = leftLessIndex; + res[popi][1] = -1; + } + } + return res; + } + + // for test + public static int[] getRandomArrayNoRepeat(int size) { + int[] arr = new int[(int) (Math.random() * size) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + for (int i = 0; i < arr.length; i++) { + int swapIndex = (int) (Math.random() * arr.length); + int tmp = arr[swapIndex]; + arr[swapIndex] = arr[i]; + arr[i] = tmp; + } + return arr; + } + + // for test + public static int[] getRandomArray(int size, int max) { + int[] arr = new int[(int) (Math.random() * size) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * max) - (int) (Math.random() * max); + } + return arr; + } + + // for test + public static int[][] rightWay(int[] arr) { + int[][] res = new int[arr.length][2]; + for (int i = 0; i < arr.length; i++) { + int leftLessIndex = -1; + int rightLessIndex = -1; + int cur = i - 1; + while (cur >= 0) { + if (arr[cur] < arr[i]) { + leftLessIndex = cur; + break; + } + cur--; + } + cur = i + 1; + while (cur < arr.length) { + if (arr[cur] < arr[i]) { + rightLessIndex = cur; + break; + } + cur++; + } + res[i][0] = leftLessIndex; + res[i][1] = rightLessIndex; + } + return res; + } + + // for test + public static boolean isEqual(int[][] res1, int[][] res2) { + if (res1.length != res2.length) { + return false; + } + for (int i = 0; i < res1.length; i++) { + if (res1[i][0] != res2[i][0] || res1[i][1] != res2[i][1]) { + return false; + } + } + + return true; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int size = 10; + int max = 20; + int testTimes = 2000000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int[] arr1 = getRandomArrayNoRepeat(size); + int[] arr2 = getRandomArray(size, max); + if (!isEqual(getNearLessNoRepeat(arr1), rightWay(arr1))) { + System.out.println("Oops!"); + printArray(arr1); + break; + } + if (!isEqual(getNearLess(arr2), rightWay(arr2))) { + System.out.println("Oops!"); + printArray(arr2); + break; + } + } + System.out.println("测试结束"); + } +} diff --git a/体系学习班/class25/Code01_MonotonousStackForNowcoder.java b/体系学习班/class25/Code01_MonotonousStackForNowcoder.java new file mode 100644 index 0000000..501b65b --- /dev/null +++ b/体系学习班/class25/Code01_MonotonousStackForNowcoder.java @@ -0,0 +1,71 @@ +package class25; + +// 测试链接 : https://www.nowcoder.com/practice/2a2c00e7a88a498693568cef63a4b7bb +// 如果在牛客上做题,可以用如下的方式来做 +// 提交如下的代码,并把主类名改成"Main" +// 可以直接通过 +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class Code01_MonotonousStackForNowcoder { + + public static int[] arr = new int[1000000]; + public static int[][] ans = new int[1000000][2]; + // stack1 : 相等值的位置也放 + // stack2 : 只放不相等值的最后一个位置 + // 比如 : arr = { 3, 3, 3, 4, 4, 6, 6, 6} + // 位置 0 1 2 3 4 5 6 7 + // 如果位置依次压栈, + // stack1中的记录是(位置) : 0 1 2 3 4 5 6 7 + // stack1中的记录是(位置) : 2 4 7 + public static int[] stack1 = new int[1000000]; + public static int[] stack2 = new int[1000000]; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + int n = Integer.parseInt(br.readLine()); + String[] strs = br.readLine().split(" "); + for (int i = 0; i < n; i++) { + arr[i] = Integer.parseInt(strs[i]); + } + getNearLess(n); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < n; i++) { + builder.append(ans[i][0] + " " + ans[i][1] + "\n"); + } + System.out.println(builder.toString()); + } + + public static void getNearLess(int n) { + int stackSize1 = 0; + int stackSize2 = 0; + for (int i = 0; i < n; i++) { + while (stackSize1 > 0 && arr[stack1[stackSize1 - 1]] > arr[i]) { + int curIndex = stack1[--stackSize1]; + int left = stackSize2 < 2 ? -1 : stack2[stackSize2 - 2]; + ans[curIndex][0] = left; + ans[curIndex][1] = i; + if (stackSize1 == 0 || arr[stack1[stackSize1 - 1]] != arr[curIndex]) { + stackSize2--; + } + } + if (stackSize1 != 0 && arr[stack1[stackSize1 - 1]] == arr[i]) { + stack2[stackSize2 - 1] = i; + } else { + stack2[stackSize2++] = i; + } + stack1[stackSize1++] = i; + } + while (stackSize1 != 0) { + int curIndex = stack1[--stackSize1]; + int left = stackSize2 < 2 ? -1 : stack2[stackSize2 - 2]; + ans[curIndex][0] = left; + ans[curIndex][1] = -1; + if (stackSize1 == 0 || arr[stack1[stackSize1 - 1]] != arr[curIndex]) { + stackSize2--; + } + } + } + +} diff --git a/体系学习班/class25/Code02_AllTimesMinToMax.java b/体系学习班/class25/Code02_AllTimesMinToMax.java new file mode 100644 index 0000000..2f26ac1 --- /dev/null +++ b/体系学习班/class25/Code02_AllTimesMinToMax.java @@ -0,0 +1,98 @@ +package class25; + +import java.util.Stack; + +public class Code02_AllTimesMinToMax { + + public static int max1(int[] arr) { + int max = Integer.MIN_VALUE; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + int minNum = Integer.MAX_VALUE; + int sum = 0; + for (int k = i; k <= j; k++) { + sum += arr[k]; + minNum = Math.min(minNum, arr[k]); + } + max = Math.max(max, minNum * sum); + } + } + return max; + } + + public static int max2(int[] arr) { + int size = arr.length; + int[] sums = new int[size]; + sums[0] = arr[0]; + for (int i = 1; i < size; i++) { + sums[i] = sums[i - 1] + arr[i]; + } + int max = Integer.MIN_VALUE; + Stack stack = new Stack(); + for (int i = 0; i < size; i++) { + while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) { + int j = stack.pop(); + max = Math.max(max, (stack.isEmpty() ? sums[i - 1] : (sums[i - 1] - sums[stack.peek()])) * arr[j]); + } + stack.push(i); + } + while (!stack.isEmpty()) { + int j = stack.pop(); + max = Math.max(max, (stack.isEmpty() ? sums[size - 1] : (sums[size - 1] - sums[stack.peek()])) * arr[j]); + } + return max; + } + + public static int[] gerenareRondomArray() { + int[] arr = new int[(int) (Math.random() * 20) + 10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * 101); + } + return arr; + } + + public static void main(String[] args) { + int testTimes = 2000000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + int[] arr = gerenareRondomArray(); + if (max1(arr) != max2(arr)) { + System.out.println("FUCK!"); + break; + } + } + System.out.println("test finish"); + } + + // 本题可以在leetcode上找到原题 + // 测试链接 : https://leetcode.com/problems/maximum-subarray-min-product/ + // 注意测试题目数量大,要取模,但是思路和课上讲的是完全一样的 + // 注意溢出的处理即可,也就是用long类型来表示累加和 + // 还有优化就是,你可以用自己手写的数组栈,来替代系统实现的栈,也会快很多 + public static int maxSumMinProduct(int[] arr) { + int size = arr.length; + long[] sums = new long[size]; + sums[0] = arr[0]; + for (int i = 1; i < size; i++) { + sums[i] = sums[i - 1] + arr[i]; + } + long max = Long.MIN_VALUE; + int[] stack = new int[size]; + int stackSize = 0; + for (int i = 0; i < size; i++) { + while (stackSize != 0 && arr[stack[stackSize - 1]] >= arr[i]) { + int j = stack[--stackSize]; + max = Math.max(max, + (stackSize == 0 ? sums[i - 1] : (sums[i - 1] - sums[stack[stackSize - 1]])) * arr[j]); + } + stack[stackSize++] = i; + } + while (stackSize != 0) { + int j = stack[--stackSize]; + max = Math.max(max, + (stackSize == 0 ? sums[size - 1] : (sums[size - 1] - sums[stack[stackSize - 1]])) * arr[j]); + } + return (int) (max % 1000000007); + } + +} diff --git a/体系学习班/class25/Code03_LargestRectangleInHistogram.java b/体系学习班/class25/Code03_LargestRectangleInHistogram.java new file mode 100644 index 0000000..d2d1ce5 --- /dev/null +++ b/体系学习班/class25/Code03_LargestRectangleInHistogram.java @@ -0,0 +1,58 @@ +package class25; + +import java.util.Stack; + +// 测试链接:https://leetcode.com/problems/largest-rectangle-in-histogram +public class Code03_LargestRectangleInHistogram { + + public static int largestRectangleArea1(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int maxArea = 0; + Stack stack = new Stack(); + for (int i = 0; i < height.length; i++) { + while (!stack.isEmpty() && height[i] <= height[stack.peek()]) { + int j = stack.pop(); + int k = stack.isEmpty() ? -1 : stack.peek(); + int curArea = (i - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + stack.push(i); + } + while (!stack.isEmpty()) { + int j = stack.pop(); + int k = stack.isEmpty() ? -1 : stack.peek(); + int curArea = (height.length - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + return maxArea; + } + + public static int largestRectangleArea2(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int N = height.length; + int[] stack = new int[N]; + int si = -1; + int maxArea = 0; + for (int i = 0; i < height.length; i++) { + while (si != -1 && height[i] <= height[stack[si]]) { + int j = stack[si--]; + int k = si == -1 ? -1 : stack[si]; + int curArea = (i - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + stack[++si] = i; + } + while (si != -1) { + int j = stack[si--]; + int k = si == -1 ? -1 : stack[si]; + int curArea = (height.length - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + return maxArea; + } + +} diff --git a/体系学习班/class25/Code04_MaximalRectangle.java b/体系学习班/class25/Code04_MaximalRectangle.java new file mode 100644 index 0000000..e2b9d37 --- /dev/null +++ b/体系学习班/class25/Code04_MaximalRectangle.java @@ -0,0 +1,48 @@ +package class25; + +import java.util.Stack; + +// 测试链接:https://leetcode.com/problems/maximal-rectangle/ +public class Code04_MaximalRectangle { + + public static int maximalRectangle(char[][] map) { + if (map == null || map.length == 0 || map[0].length == 0) { + return 0; + } + int maxArea = 0; + int[] height = new int[map[0].length]; + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + height[j] = map[i][j] == '0' ? 0 : height[j] + 1; + } + maxArea = Math.max(maxRecFromBottom(height), maxArea); + } + return maxArea; + } + + // height是正方图数组 + public static int maxRecFromBottom(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int maxArea = 0; + Stack stack = new Stack(); + for (int i = 0; i < height.length; i++) { + while (!stack.isEmpty() && height[i] <= height[stack.peek()]) { + int j = stack.pop(); + int k = stack.isEmpty() ? -1 : stack.peek(); + int curArea = (i - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + stack.push(i); + } + while (!stack.isEmpty()) { + int j = stack.pop(); + int k = stack.isEmpty() ? -1 : stack.peek(); + int curArea = (height.length - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + return maxArea; + } + +} diff --git a/体系学习班/class25/Code05_CountSubmatricesWithAllOnes.java b/体系学习班/class25/Code05_CountSubmatricesWithAllOnes.java new file mode 100644 index 0000000..f24d02e --- /dev/null +++ b/体系学习班/class25/Code05_CountSubmatricesWithAllOnes.java @@ -0,0 +1,81 @@ +package class25; + +// 测试链接:https://leetcode.com/problems/count-submatrices-with-all-ones +public class Code05_CountSubmatricesWithAllOnes { + + public static int numSubmat(int[][] mat) { + if (mat == null || mat.length == 0 || mat[0].length == 0) { + return 0; + } + int nums = 0; + int[] height = new int[mat[0].length]; + for (int i = 0; i < mat.length; i++) { + for (int j = 0; j < mat[0].length; j++) { + height[j] = mat[i][j] == 0 ? 0 : height[j] + 1; + } + nums += countFromBottom(height); + } + return nums; + + } + + // 比如 + // 1 + // 1 + // 1 1 + // 1 1 1 + // 1 1 1 + // 1 1 1 + // + // 2 .... 6 .... 9 + // 如上图,假设在6位置,1的高度为6 + // 在6位置的左边,离6位置最近、且小于高度6的位置是2,2位置的高度是3 + // 在6位置的右边,离6位置最近、且小于高度6的位置是9,9位置的高度是4 + // 此时我们求什么? + // 1) 求在3~8范围上,必须以高度6作为高的矩形,有几个? + // 2) 求在3~8范围上,必须以高度5作为高的矩形,有几个? + // 也就是说,<=4的高度,一律不求 + // 那么,1) 求必须以位置6的高度6作为高的矩形,有几个? + // 3..3 3..4 3..5 3..6 3..7 3..8 + // 4..4 4..5 4..6 4..7 4..8 + // 5..5 5..6 5..7 5..8 + // 6..6 6..7 6..8 + // 7..7 7..8 + // 8..8 + // 这么多!= 21 = (9 - 2 - 1) * (9 - 2) / 2 + // 这就是任何一个数字从栈里弹出的时候,计算矩形数量的方式 + public static int countFromBottom(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int nums = 0; + int[] stack = new int[height.length]; + int si = -1; + for (int i = 0; i < height.length; i++) { + while (si != -1 && height[stack[si]] >= height[i]) { + int cur = stack[si--]; + if (height[cur] > height[i]) { + int left = si == -1 ? -1 : stack[si]; + int n = i - left - 1; + int down = Math.max(left == -1 ? 0 : height[left], height[i]); + nums += (height[cur] - down) * num(n); + } + + } + stack[++si] = i; + } + while (si != -1) { + int cur = stack[si--]; + int left = si == -1 ? -1 : stack[si]; + int n = height.length - left - 1; + int down = left == -1 ? 0 : height[left]; + nums += (height[cur] - down) * num(n); + } + return nums; + } + + public static int num(int n) { + return ((n * (1 + n)) >> 1); + } + +} diff --git a/体系学习班/class26/Code01_SumOfSubarrayMinimums.java b/体系学习班/class26/Code01_SumOfSubarrayMinimums.java new file mode 100644 index 0000000..343247b --- /dev/null +++ b/体系学习班/class26/Code01_SumOfSubarrayMinimums.java @@ -0,0 +1,156 @@ +package class26; + +// 测试链接:https://leetcode.com/problems/sum-of-subarray-minimums/ +// subArrayMinSum1是暴力解 +// subArrayMinSum2是最优解的思路 +// sumSubarrayMins是最优解思路下的单调栈优化 +// Leetcode上不要提交subArrayMinSum1、subArrayMinSum2方法,因为没有考虑取摸 +// Leetcode上只提交sumSubarrayMins方法,时间复杂度O(N),可以直接通过 +public class Code01_SumOfSubarrayMinimums { + + public static int subArrayMinSum1(int[] arr) { + int ans = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + int min = arr[i]; + for (int k = i + 1; k <= j; k++) { + min = Math.min(min, arr[k]); + } + ans += min; + } + } + return ans; + } + + // 没有用单调栈 + public static int subArrayMinSum2(int[] arr) { + // left[i] = x : arr[i]左边,离arr[i]最近,<=arr[i],位置在x + int[] left = leftNearLessEqual2(arr); + // right[i] = y : arr[i]右边,离arr[i]最近,< arr[i],的数,位置在y + int[] right = rightNearLess2(arr); + int ans = 0; + for (int i = 0; i < arr.length; i++) { + int start = i - left[i]; + int end = right[i] - i; + ans += start * end * arr[i]; + } + return ans; + } + + public static int[] leftNearLessEqual2(int[] arr) { + int N = arr.length; + int[] left = new int[N]; + for (int i = 0; i < N; i++) { + int ans = -1; + for (int j = i - 1; j >= 0; j--) { + if (arr[j] <= arr[i]) { + ans = j; + break; + } + } + left[i] = ans; + } + return left; + } + + public static int[] rightNearLess2(int[] arr) { + int N = arr.length; + int[] right = new int[N]; + for (int i = 0; i < N; i++) { + int ans = N; + for (int j = i + 1; j < N; j++) { + if (arr[i] > arr[j]) { + ans = j; + break; + } + } + right[i] = ans; + } + return right; + } + + public static int sumSubarrayMins(int[] arr) { + int[] stack = new int[arr.length]; + int[] left = nearLessEqualLeft(arr, stack); + int[] right = nearLessRight(arr, stack); + long ans = 0; + for (int i = 0; i < arr.length; i++) { + long start = i - left[i]; + long end = right[i] - i; + ans += start * end * (long) arr[i]; + ans %= 1000000007; + } + return (int) ans; + } + + public static int[] nearLessEqualLeft(int[] arr, int[] stack) { + int N = arr.length; + int[] left = new int[N]; + int size = 0; + for (int i = N - 1; i >= 0; i--) { + while (size != 0 && arr[i] <= arr[stack[size - 1]]) { + left[stack[--size]] = i; + } + stack[size++] = i; + } + while (size != 0) { + left[stack[--size]] = -1; + } + return left; + } + + public static int[] nearLessRight(int[] arr, int[] stack) { + int N = arr.length; + int[] right = new int[N]; + int size = 0; + for (int i = 0; i < N; i++) { + while (size != 0 && arr[stack[size - 1]] > arr[i]) { + right[stack[--size]] = i; + } + stack[size++] = i; + } + while (size != 0) { + right[stack[--size]] = N; + } + return right; + } + + public static int[] randomArray(int len, int maxValue) { + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * maxValue) + 1; + } + return ans; + } + + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int maxLen = 100; + int maxValue = 50; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * maxLen); + int[] arr = randomArray(len, maxValue); + int ans1 = subArrayMinSum1(arr); + int ans2 = subArrayMinSum2(arr); + int ans3 = sumSubarrayMins(arr); + if (ans1 != ans2 || ans1 != ans3) { + printArray(arr); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class26/Code02_FibonacciProblem.java b/体系学习班/class26/Code02_FibonacciProblem.java new file mode 100644 index 0000000..7f50420 --- /dev/null +++ b/体系学习班/class26/Code02_FibonacciProblem.java @@ -0,0 +1,189 @@ +package class26; + +public class Code02_FibonacciProblem { + + public static int f1(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return 1; + } + return f1(n - 1) + f1(n - 2); + } + + public static int f2(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return 1; + } + int res = 1; + int pre = 1; + int tmp = 0; + for (int i = 3; i <= n; i++) { + tmp = res; + res = res + pre; + pre = tmp; + } + return res; + } + + // O(logN) + public static int f3(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return 1; + } + // [ 1 ,1 ] + // [ 1, 0 ] + int[][] base = { + { 1, 1 }, + { 1, 0 } + }; + int[][] res = matrixPower(base, n - 2); + return res[0][0] + res[1][0]; + } + + public static int[][] matrixPower(int[][] m, int p) { + int[][] res = new int[m.length][m[0].length]; + for (int i = 0; i < res.length; i++) { + res[i][i] = 1; + } + // res = 矩阵中的1 + int[][] t = m;// 矩阵1次方 + for (; p != 0; p >>= 1) { + if ((p & 1) != 0) { + res = product(res, t); + } + t = product(t, t); + } + return res; + } + + // 两个矩阵乘完之后的结果返回 + public static int[][] product(int[][] a, int[][] b) { + int n = a.length; + int m = b[0].length; + int k = a[0].length; // a的列数同时也是b的行数 + int[][] ans = new int[n][m]; + for(int i = 0 ; i < n; i++) { + for(int j = 0 ; j < m;j++) { + for(int c = 0; c < k; c++) { + ans[i][j] += a[i][c] * b[c][j]; + } + } + } + return ans; + } + + public static int s1(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return n; + } + return s1(n - 1) + s1(n - 2); + } + + public static int s2(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return n; + } + int res = 2; + int pre = 1; + int tmp = 0; + for (int i = 3; i <= n; i++) { + tmp = res; + res = res + pre; + pre = tmp; + } + return res; + } + + public static int s3(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return n; + } + int[][] base = { { 1, 1 }, { 1, 0 } }; + int[][] res = matrixPower(base, n - 2); + return 2 * res[0][0] + res[1][0]; + } + + public static int c1(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2 || n == 3) { + return n; + } + return c1(n - 1) + c1(n - 3); + } + + public static int c2(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2 || n == 3) { + return n; + } + int res = 3; + int pre = 2; + int prepre = 1; + int tmp1 = 0; + int tmp2 = 0; + for (int i = 4; i <= n; i++) { + tmp1 = res; + tmp2 = pre; + res = res + prepre; + pre = tmp1; + prepre = tmp2; + } + return res; + } + + public static int c3(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2 || n == 3) { + return n; + } + int[][] base = { + { 1, 1, 0 }, + { 0, 0, 1 }, + { 1, 0, 0 } }; + int[][] res = matrixPower(base, n - 3); + return 3 * res[0][0] + 2 * res[1][0] + res[2][0]; + } + + public static void main(String[] args) { + int n = 19; + System.out.println(f1(n)); + System.out.println(f2(n)); + System.out.println(f3(n)); + System.out.println("==="); + + System.out.println(s1(n)); + System.out.println(s2(n)); + System.out.println(s3(n)); + System.out.println("==="); + + System.out.println(c1(n)); + System.out.println(c2(n)); + System.out.println(c3(n)); + System.out.println("==="); + + } + +} diff --git a/体系学习班/class26/Code03_ZeroLeftOneStringNumber.java b/体系学习班/class26/Code03_ZeroLeftOneStringNumber.java new file mode 100644 index 0000000..8cc3ce2 --- /dev/null +++ b/体系学习班/class26/Code03_ZeroLeftOneStringNumber.java @@ -0,0 +1,113 @@ +package class26; + +public class Code03_ZeroLeftOneStringNumber { + + public static int getNum1(int n) { + if (n < 1) { + return 0; + } + return process(1, n); + } + + public static int process(int i, int n) { + if (i == n - 1) { + return 2; + } + if (i == n) { + return 1; + } + return process(i + 1, n) + process(i + 2, n); + } + + public static int getNum2(int n) { + if (n < 1) { + return 0; + } + if (n == 1) { + return 1; + } + int pre = 1; + int cur = 1; + int tmp = 0; + for (int i = 2; i < n + 1; i++) { + tmp = cur; + cur += pre; + pre = tmp; + } + return cur; + } + + public static int getNum3(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return n; + } + int[][] base = { { 1, 1 }, { 1, 0 } }; + int[][] res = matrixPower(base, n - 2); + return 2 * res[0][0] + res[1][0]; + } + + + + + + + public static int fi(int n) { + if (n < 1) { + return 0; + } + if (n == 1 || n == 2) { + return 1; + } + int[][] base = { { 1, 1 }, + { 1, 0 } }; + int[][] res = matrixPower(base, n - 2); + return res[0][0] + res[1][0]; + } + + + + + public static int[][] matrixPower(int[][] m, int p) { + int[][] res = new int[m.length][m[0].length]; + for (int i = 0; i < res.length; i++) { + res[i][i] = 1; + } + int[][] tmp = m; + for (; p != 0; p >>= 1) { + if ((p & 1) != 0) { + res = product(res, tmp); + } + tmp = product(tmp, tmp); + } + return res; + } + + // 两个矩阵乘完之后的结果返回 + public static int[][] product(int[][] a, int[][] b) { + int n = a.length; + int m = b[0].length; + int k = a[0].length; // a的列数同时也是b的行数 + int[][] ans = new int[n][m]; + for(int i = 0 ; i < n; i++) { + for(int j = 0 ; j < m;j++) { + for(int c = 0; c < k; c++) { + ans[i][j] += a[i][c] * b[c][j]; + } + } + } + return ans; + } + + public static void main(String[] args) { + for (int i = 0; i != 20; i++) { + System.out.println(getNum1(i)); + System.out.println(getNum2(i)); + System.out.println(getNum3(i)); + System.out.println("==================="); + } + + } +} diff --git a/体系学习班/class27/Code01_KMP.java b/体系学习班/class27/Code01_KMP.java new file mode 100644 index 0000000..68a51de --- /dev/null +++ b/体系学习班/class27/Code01_KMP.java @@ -0,0 +1,75 @@ +package class27; + +public class Code01_KMP { + + public static int getIndexOf(String s1, String s2) { + if (s1 == null || s2 == null || s2.length() < 1 || s1.length() < s2.length()) { + return -1; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int x = 0; + int y = 0; + // O(M) m <= n + int[] next = getNextArray(str2); + // O(N) + while (x < str1.length && y < str2.length) { + if (str1[x] == str2[y]) { + x++; + y++; + } else if (next[y] == -1) { // y == 0 + x++; + } else { + y = next[y]; + } + } + return y == str2.length ? x - y : -1; + } + + public static int[] getNextArray(char[] str2) { + if (str2.length == 1) { + return new int[] { -1 }; + } + int[] next = new int[str2.length]; + next[0] = -1; + next[1] = 0; + int i = 2; // 目前在哪个位置上求next数组的值 + int cn = 0; // 当前是哪个位置的值再和i-1位置的字符比较 + while (i < next.length) { + if (str2[i - 1] == str2[cn]) { // 配成功的时候 + next[i++] = ++cn; + } else if (cn > 0) { + cn = next[cn]; + } else { + next[i++] = 0; + } + } + return next; + } + + // for test + public static String getRandomString(int possibilities, int size) { + char[] ans = new char[(int) (Math.random() * size) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (char) ((int) (Math.random() * possibilities) + 'a'); + } + return String.valueOf(ans); + } + + public static void main(String[] args) { + int possibilities = 5; + int strSize = 20; + int matchSize = 5; + int testTimes = 5000000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + String str = getRandomString(possibilities, strSize); + String match = getRandomString(possibilities, matchSize); + if (getIndexOf(str, match) != str.indexOf(match)) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class27/Code02_TreeEqual.java b/体系学习班/class27/Code02_TreeEqual.java new file mode 100644 index 0000000..657373a --- /dev/null +++ b/体系学习班/class27/Code02_TreeEqual.java @@ -0,0 +1,172 @@ +package class27; + +import java.util.ArrayList; + +public class Code02_TreeEqual { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + public static boolean containsTree1(Node big, Node small) { + if (small == null) { + return true; + } + if (big == null) { + return false; + } + if (isSameValueStructure(big, small)) { + return true; + } + return containsTree1(big.left, small) || containsTree1(big.right, small); + } + + public static boolean isSameValueStructure(Node head1, Node head2) { + if (head1 == null && head2 != null) { + return false; + } + if (head1 != null && head2 == null) { + return false; + } + if (head1 == null && head2 == null) { + return true; + } + if (head1.value != head2.value) { + return false; + } + return isSameValueStructure(head1.left, head2.left) + && isSameValueStructure(head1.right, head2.right); + } + + public static boolean containsTree2(Node big, Node small) { + if (small == null) { + return true; + } + if (big == null) { + return false; + } + ArrayList b = preSerial(big); + ArrayList s = preSerial(small); + String[] str = new String[b.size()]; + for (int i = 0; i < str.length; i++) { + str[i] = b.get(i); + } + + String[] match = new String[s.size()]; + for (int i = 0; i < match.length; i++) { + match[i] = s.get(i); + } + return getIndexOf(str, match) != -1; + } + + public static ArrayList preSerial(Node head) { + ArrayList ans = new ArrayList<>(); + pres(head, ans); + return ans; + } + + public static void pres(Node head, ArrayList ans) { + if (head == null) { + ans.add(null); + } else { + ans.add(String.valueOf(head.value)); + pres(head.left, ans); + pres(head.right, ans); + } + } + + public static int getIndexOf(String[] str1, String[] str2) { + if (str1 == null || str2 == null || str1.length < 1 || str1.length < str2.length) { + return -1; + } + int x = 0; + int y = 0; + int[] next = getNextArray(str2); + while (x < str1.length && y < str2.length) { + if (isEqual(str1[x], str2[y])) { + x++; + y++; + } else if (next[y] == -1) { + x++; + } else { + y = next[y]; + } + } + return y == str2.length ? x - y : -1; + } + + public static int[] getNextArray(String[] ms) { + if (ms.length == 1) { + return new int[] { -1 }; + } + int[] next = new int[ms.length]; + next[0] = -1; + next[1] = 0; + int i = 2; + int cn = 0; + while (i < next.length) { + if (isEqual(ms[i - 1], ms[cn])) { + next[i++] = ++cn; + } else if (cn > 0) { + cn = next[cn]; + } else { + next[i++] = 0; + } + } + return next; + } + + public static boolean isEqual(String a, String b) { + if (a == null && b == null) { + return true; + } else { + if (a == null || b == null) { + return false; + } else { + return a.equals(b); + } + } + } + + // for test + public static Node generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static Node generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + Node head = new Node((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int bigTreeLevel = 7; + int smallTreeLevel = 4; + int nodeMaxValue = 5; + int testTimes = 100000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + Node big = generateRandomBST(bigTreeLevel, nodeMaxValue); + Node small = generateRandomBST(smallTreeLevel, nodeMaxValue); + boolean ans1 = containsTree1(big, small); + boolean ans2 = containsTree2(big, small); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish!"); + + } + +} diff --git a/体系学习班/class27/Code03_IsRotation.java b/体系学习班/class27/Code03_IsRotation.java new file mode 100644 index 0000000..605cb81 --- /dev/null +++ b/体系学习班/class27/Code03_IsRotation.java @@ -0,0 +1,64 @@ +package class27; + +public class Code03_IsRotation { + + public static boolean isRotation(String a, String b) { + if (a == null || b == null || a.length() != b.length()) { + return false; + } + String b2 = b + b; + return getIndexOf(b2, a) != -1; + } + + // KMP Algorithm + public static int getIndexOf(String s, String m) { + if (s.length() < m.length()) { + return -1; + } + char[] ss = s.toCharArray(); + char[] ms = m.toCharArray(); + int si = 0; + int mi = 0; + int[] next = getNextArray(ms); + while (si < ss.length && mi < ms.length) { + if (ss[si] == ms[mi]) { + si++; + mi++; + } else if (next[mi] == -1) { + si++; + } else { + mi = next[mi]; + } + } + return mi == ms.length ? si - mi : -1; + } + + public static int[] getNextArray(char[] ms) { + if (ms.length == 1) { + return new int[] { -1 }; + } + int[] next = new int[ms.length]; + next[0] = -1; + next[1] = 0; + int pos = 2; + int cn = 0; + while (pos < next.length) { + if (ms[pos - 1] == ms[cn]) { + next[pos++] = ++cn; + } else if (cn > 0) { + cn = next[cn]; + } else { + next[pos++] = 0; + } + } + return next; + } + + public static void main(String[] args) { + String str1 = "yunzuocheng"; + String str2 = "zuochengyun"; + System.out.println(isRotation(str1, str2)); + + } + +} diff --git a/体系学习班/class28/Code01_Manacher.java b/体系学习班/class28/Code01_Manacher.java new file mode 100644 index 0000000..c75975f --- /dev/null +++ b/体系学习班/class28/Code01_Manacher.java @@ -0,0 +1,90 @@ +package class28; + +public class Code01_Manacher { + + public static int manacher(String s) { + if (s == null || s.length() == 0) { + return 0; + } + // "12132" -> "#1#2#1#3#2#" + char[] str = manacherString(s); + // 回文半径的大小 + int[] pArr = new int[str.length]; + int C = -1; + // 讲述中:R代表最右的扩成功的位置 + // coding:最右的扩成功位置的,再下一个位置 + int R = -1; + int max = Integer.MIN_VALUE; + for (int i = 0; i < str.length; i++) { // 0 1 2 + // R第一个违规的位置,i>= R + // i位置扩出来的答案,i位置扩的区域,至少是多大。 + pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1; + while (i + pArr[i] < str.length && i - pArr[i] > -1) { + if (str[i + pArr[i]] == str[i - pArr[i]]) + pArr[i]++; + else { + break; + } + } + if (i + pArr[i] > R) { + R = i + pArr[i]; + C = i; + } + max = Math.max(max, pArr[i]); + } + return max - 1; + } + + public static char[] manacherString(String str) { + char[] charArr = str.toCharArray(); + char[] res = new char[str.length() * 2 + 1]; + int index = 0; + for (int i = 0; i != res.length; i++) { + res[i] = (i & 1) == 0 ? '#' : charArr[index++]; + } + return res; + } + + // for test + public static int right(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = manacherString(s); + int max = 0; + for (int i = 0; i < str.length; i++) { + int L = i - 1; + int R = i + 1; + while (L >= 0 && R < str.length && str[L] == str[R]) { + L--; + R++; + } + max = Math.max(max, R - L - 1); + } + return max / 2; + } + + // for test + public static String getRandomString(int possibilities, int size) { + char[] ans = new char[(int) (Math.random() * size) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (char) ((int) (Math.random() * possibilities) + 'a'); + } + return String.valueOf(ans); + } + + public static void main(String[] args) { + int possibilities = 5; + int strSize = 20; + int testTimes = 5000000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + String str = getRandomString(possibilities, strSize); + if (manacher(str) != right(str)) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class28/Code02_AddShortestEnd.java b/体系学习班/class28/Code02_AddShortestEnd.java new file mode 100644 index 0000000..00d961c --- /dev/null +++ b/体系学习班/class28/Code02_AddShortestEnd.java @@ -0,0 +1,54 @@ +package class28; + +public class Code02_AddShortestEnd { + + public static String shortestEnd(String s) { + if (s == null || s.length() == 0) { + return null; + } + char[] str = manacherString(s); + int[] pArr = new int[str.length]; + int C = -1; + int R = -1; + int maxContainsEnd = -1; + for (int i = 0; i != str.length; i++) { + pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1; + while (i + pArr[i] < str.length && i - pArr[i] > -1) { + if (str[i + pArr[i]] == str[i - pArr[i]]) + pArr[i]++; + else { + break; + } + } + if (i + pArr[i] > R) { + R = i + pArr[i]; + C = i; + } + if (R == str.length) { + maxContainsEnd = pArr[i]; + break; + } + } + char[] res = new char[s.length() - maxContainsEnd + 1]; + for (int i = 0; i < res.length; i++) { + res[res.length - 1 - i] = str[i * 2 + 1]; + } + return String.valueOf(res); + } + + public static char[] manacherString(String str) { + char[] charArr = str.toCharArray(); + char[] res = new char[str.length() * 2 + 1]; + int index = 0; + for (int i = 0; i != res.length; i++) { + res[i] = (i & 1) == 0 ? '#' : charArr[index++]; + } + return res; + } + + public static void main(String[] args) { + String str1 = "abcd123321"; + System.out.println(shortestEnd(str1)); + } + +} diff --git a/体系学习班/class29/Code01_FindMinKth.java b/体系学习班/class29/Code01_FindMinKth.java new file mode 100644 index 0000000..e317550 --- /dev/null +++ b/体系学习班/class29/Code01_FindMinKth.java @@ -0,0 +1,175 @@ +package class29; + +import java.util.Comparator; +import java.util.PriorityQueue; + +public class Code01_FindMinKth { + + public static class MaxHeapComparator implements Comparator { + + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + + } + + // 利用大根堆,时间复杂度O(N*logK) + public static int minKth1(int[] arr, int k) { + PriorityQueue maxHeap = new PriorityQueue<>(new MaxHeapComparator()); + for (int i = 0; i < k; i++) { + maxHeap.add(arr[i]); + } + for (int i = k; i < arr.length; i++) { + if (arr[i] < maxHeap.peek()) { + maxHeap.poll(); + maxHeap.add(arr[i]); + } + } + return maxHeap.peek(); + } + + // 改写快排,时间复杂度O(N) + // k >= 1 + public static int minKth2(int[] array, int k) { + int[] arr = copyArray(array); + return process2(arr, 0, arr.length - 1, k - 1); + } + + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i != ans.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // arr 第k小的数 + // process2(arr, 0, N-1, k-1) + // arr[L..R] 范围上,如果排序的话(不是真的去排序),找位于index的数 + // index [L..R] + public static int process2(int[] arr, int L, int R, int index) { + if (L == R) { // L = =R ==INDEX + return arr[L]; + } + // 不止一个数 L + [0, R -L] + int pivot = arr[L + (int) (Math.random() * (R - L + 1))]; + int[] range = partition(arr, L, R, pivot); + if (index >= range[0] && index <= range[1]) { + return arr[index]; + } else if (index < range[0]) { + return process2(arr, L, range[0] - 1, index); + } else { + return process2(arr, range[1] + 1, R, index); + } + } + + public static int[] partition(int[] arr, int L, int R, int pivot) { + int less = L - 1; + int more = R + 1; + int cur = L; + while (cur < more) { + if (arr[cur] < pivot) { + swap(arr, ++less, cur++); + } else if (arr[cur] > pivot) { + swap(arr, cur, --more); + } else { + cur++; + } + } + return new int[] { less + 1, more - 1 }; + } + + public static void swap(int[] arr, int i1, int i2) { + int tmp = arr[i1]; + arr[i1] = arr[i2]; + arr[i2] = tmp; + } + + // 利用bfprt算法,时间复杂度O(N) + public static int minKth3(int[] array, int k) { + int[] arr = copyArray(array); + return bfprt(arr, 0, arr.length - 1, k - 1); + } + + // arr[L..R] 如果排序的话,位于index位置的数,是什么,返回 + public static int bfprt(int[] arr, int L, int R, int index) { + if (L == R) { + return arr[L]; + } + // L...R 每五个数一组 + // 每一个小组内部排好序 + // 小组的中位数组成新数组 + // 这个新数组的中位数返回 + int pivot = medianOfMedians(arr, L, R); + int[] range = partition(arr, L, R, pivot); + if (index >= range[0] && index <= range[1]) { + return arr[index]; + } else if (index < range[0]) { + return bfprt(arr, L, range[0] - 1, index); + } else { + return bfprt(arr, range[1] + 1, R, index); + } + } + + // arr[L...R] 五个数一组 + // 每个小组内部排序 + // 每个小组中位数领出来,组成marr + // marr中的中位数,返回 + public static int medianOfMedians(int[] arr, int L, int R) { + int size = R - L + 1; + int offset = size % 5 == 0 ? 0 : 1; + int[] mArr = new int[size / 5 + offset]; + for (int team = 0; team < mArr.length; team++) { + int teamFirst = L + team * 5; + // L ... L + 4 + // L +5 ... L +9 + // L +10....L+14 + mArr[team] = getMedian(arr, teamFirst, Math.min(R, teamFirst + 4)); + } + // marr中,找到中位数 + // marr(0, marr.len - 1, mArr.length / 2 ) + return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2); + } + + public static int getMedian(int[] arr, int L, int R) { + insertionSort(arr, L, R); + return arr[(L + R) / 2]; + } + + public static void insertionSort(int[] arr, int L, int R) { + for (int i = L + 1; i <= R; i++) { + for (int j = i - 1; j >= L && arr[j] > arr[j + 1]; j--) { + swap(arr, j, j + 1); + } + } + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) (Math.random() * maxSize) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * (maxValue + 1)); + } + return arr; + } + + public static void main(String[] args) { + int testTime = 1000000; + int maxSize = 100; + int maxValue = 100; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int k = (int) (Math.random() * arr.length) + 1; + int ans1 = minKth1(arr, k); + int ans2 = minKth2(arr, k); + int ans3 = minKth3(arr, k); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class29/Code02_MaxTopK.java b/体系学习班/class29/Code02_MaxTopK.java new file mode 100644 index 0000000..b300171 --- /dev/null +++ b/体系学习班/class29/Code02_MaxTopK.java @@ -0,0 +1,223 @@ +package class29; + +import java.util.Arrays; + +public class Code02_MaxTopK { + + // 时间复杂度O(N*logN) + // 排序+收集 + public static int[] maxTopK1(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + int N = arr.length; + k = Math.min(N, k); + Arrays.sort(arr); + int[] ans = new int[k]; + for (int i = N - 1, j = 0; j < k; i--, j++) { + ans[j] = arr[i]; + } + return ans; + } + + // 方法二,时间复杂度O(N + K*logN) + // 解释:堆 + public static int[] maxTopK2(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + int N = arr.length; + k = Math.min(N, k); + // 从底向上建堆,时间复杂度O(N) + for (int i = N - 1; i >= 0; i--) { + heapify(arr, i, N); + } + // 只把前K个数放在arr末尾,然后收集,O(K*logN) + int heapSize = N; + swap(arr, 0, --heapSize); + int count = 1; + while (heapSize > 0 && count < k) { + heapify(arr, 0, heapSize); + swap(arr, 0, --heapSize); + count++; + } + int[] ans = new int[k]; + for (int i = N - 1, j = 0; j < k; i--, j++) { + ans[j] = arr[i]; + } + return ans; + } + + public static void heapInsert(int[] arr, int index) { + while (arr[index] > arr[(index - 1) / 2]) { + swap(arr, index, (index - 1) / 2); + index = (index - 1) / 2; + } + } + + public static void heapify(int[] arr, int index, int heapSize) { + int left = index * 2 + 1; + while (left < heapSize) { + int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left; + largest = arr[largest] > arr[index] ? largest : index; + if (largest == index) { + break; + } + swap(arr, largest, index); + index = largest; + left = index * 2 + 1; + } + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 方法三,时间复杂度O(n + k * logk) + public static int[] maxTopK3(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + int N = arr.length; + k = Math.min(N, k); + // O(N) + int num = minKth(arr, N - k); + int[] ans = new int[k]; + int index = 0; + for (int i = 0; i < N; i++) { + if (arr[i] > num) { + ans[index++] = arr[i]; + } + } + for (; index < k; index++) { + ans[index] = num; + } + // O(k*logk) + Arrays.sort(ans); + for (int L = 0, R = k - 1; L < R; L++, R--) { + swap(ans, L, R); + } + return ans; + } + + // 时间复杂度O(N) + public static int minKth(int[] arr, int index) { + int L = 0; + int R = arr.length - 1; + int pivot = 0; + int[] range = null; + while (L < R) { + pivot = arr[L + (int) (Math.random() * (R - L + 1))]; + range = partition(arr, L, R, pivot); + if (index < range[0]) { + R = range[0] - 1; + } else if (index > range[1]) { + L = range[1] + 1; + } else { + return pivot; + } + } + return arr[L]; + } + + public static int[] partition(int[] arr, int L, int R, int pivot) { + int less = L - 1; + int more = R + 1; + int cur = L; + while (cur < more) { + if (arr[cur] < pivot) { + swap(arr, ++less, cur++); + } else if (arr[cur] > pivot) { + swap(arr, cur, --more); + } else { + cur++; + } + } + return new int[] { less + 1, more - 1 }; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + // [-? , +?] + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 生成随机数组测试 + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean pass = true; + System.out.println("测试开始,没有打印出错信息说明测试通过"); + for (int i = 0; i < testTime; i++) { + int k = (int) (Math.random() * maxSize) + 1; + int[] arr = generateRandomArray(maxSize, maxValue); + + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int[] arr3 = copyArray(arr); + + int[] ans1 = maxTopK1(arr1, k); + int[] ans2 = maxTopK2(arr2, k); + int[] ans3 = maxTopK3(arr3, k); + if (!isEqual(ans1, ans2) || !isEqual(ans1, ans3)) { + pass = false; + System.out.println("出错了!"); + printArray(ans1); + printArray(ans2); + printArray(ans3); + break; + } + } + System.out.println("测试结束了,测试了" + testTime + "组,是否所有测试用例都通过?" + (pass ? "是" : "否")); + } + +} diff --git a/体系学习班/class29/Code03_ReservoirSampling.java b/体系学习班/class29/Code03_ReservoirSampling.java new file mode 100644 index 0000000..c2ce9e3 --- /dev/null +++ b/体系学习班/class29/Code03_ReservoirSampling.java @@ -0,0 +1,94 @@ +package class29; + +public class Code03_ReservoirSampling { + + public static class RandomBox { + private int[] bag; + private int N; + private int count; + + public RandomBox(int capacity) { + bag = new int[capacity]; + N = capacity; + count = 0; + } + + private int rand(int max) { + return (int) (Math.random() * max) + 1; + } + + public void add(int num) { + count++; + if (count <= N) { + bag[count - 1] = num; + } else { + if (rand(count) <= N) { + bag[rand(N) - 1] = num; + } + } + } + + public int[] choices() { + int[] ans = new int[N]; + for (int i = 0; i < N; i++) { + ans[i] = bag[i]; + } + return ans; + } + + } + + // 请等概率返回1~i中的一个数字 + public static int random(int i) { + return (int) (Math.random() * i) + 1; + } + + public static void main(String[] args) { + System.out.println("hello"); + int test = 10000; + int ballNum = 17; + int[] count = new int[ballNum + 1]; + for (int i = 0; i < test; i++) { + int[] bag = new int[10]; + int bagi = 0; + for (int num = 1; num <= ballNum; num++) { + if (num <= 10) { + bag[bagi++] = num; + } else { // num > 10 + if (random(num) <= 10) { // 一定要把num球入袋子 + bagi = (int) (Math.random() * 10); + bag[bagi] = num; + } + } + + } + for (int num : bag) { + count[num]++; + } + } + for (int i = 0; i <= ballNum; i++) { + System.out.println(count[i]); + } + + System.out.println("hello"); + int all = 100; + int choose = 10; + int testTimes = 50000; + int[] counts = new int[all + 1]; + for (int i = 0; i < testTimes; i++) { + RandomBox box = new RandomBox(choose); + for (int num = 1; num <= all; num++) { + box.add(num); + } + int[] ans = box.choices(); + for (int j = 0; j < ans.length; j++) { + counts[ans[j]]++; + } + } + + for (int i = 0; i < counts.length; i++) { + System.out.println(i + " times : " + counts[i]); + } + + } +} diff --git a/体系学习班/class30/Code01_MorrisTraversal.java b/体系学习班/class30/Code01_MorrisTraversal.java new file mode 100644 index 0000000..2e3a4a4 --- /dev/null +++ b/体系学习班/class30/Code01_MorrisTraversal.java @@ -0,0 +1,230 @@ +package class30; + +public class Code01_MorrisTraversal { + + public static class Node { + public int value; + Node left; + Node right; + + public Node(int data) { + this.value = data; + } + } + + public static void process(Node root) { + if (root == null) { + return; + } + // 1 + process(root.left); + // 2 + process(root.right); + // 3 + } + + public static void morris(Node head) { + if (head == null) { + return; + } + Node cur = head; + Node mostRight = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } + cur = cur.right; + } + } + + public static void morrisPre(Node head) { + if (head == null) { + return; + } + Node cur = head; + Node mostRight = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + System.out.print(cur.value + " "); + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } else { + System.out.print(cur.value + " "); + } + cur = cur.right; + } + System.out.println(); + } + + public static void morrisIn(Node head) { + if (head == null) { + return; + } + Node cur = head; + Node mostRight = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } + System.out.print(cur.value + " "); + cur = cur.right; + } + System.out.println(); + } + + public static void morrisPos(Node head) { + if (head == null) { + return; + } + Node cur = head; + Node mostRight = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + printEdge(cur.left); + } + } + cur = cur.right; + } + printEdge(head); + System.out.println(); + } + + public static void printEdge(Node head) { + Node tail = reverseEdge(head); + Node cur = tail; + while (cur != null) { + System.out.print(cur.value + " "); + cur = cur.right; + } + reverseEdge(tail); + } + + public static Node reverseEdge(Node from) { + Node pre = null; + Node next = null; + while (from != null) { + next = from.right; + from.right = pre; + pre = from; + from = next; + } + return pre; + } + + // for test -- print tree + public static void printTree(Node head) { + System.out.println("Binary Tree:"); + printInOrder(head, 0, "H", 17); + System.out.println(); + } + + public static void printInOrder(Node head, int height, String to, int len) { + if (head == null) { + return; + } + printInOrder(head.right, height + 1, "v", len); + String val = to + head.value + to; + int lenM = val.length(); + int lenL = (len - lenM) / 2; + int lenR = len - lenM - lenL; + val = getSpace(lenL) + val + getSpace(lenR); + System.out.println(getSpace(height * len) + val); + printInOrder(head.left, height + 1, "^", len); + } + + public static String getSpace(int num) { + String space = " "; + StringBuffer buf = new StringBuffer(""); + for (int i = 0; i < num; i++) { + buf.append(space); + } + return buf.toString(); + } + + public static boolean isBST(Node head) { + if (head == null) { + return true; + } + Node cur = head; + Node mostRight = null; + Integer pre = null; + boolean ans = true; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } + if (pre != null && pre >= cur.value) { + ans = false; + } + pre = cur.value; + cur = cur.right; + } + return ans; + } + + public static void main(String[] args) { + Node head = new Node(4); + head.left = new Node(2); + head.right = new Node(6); + head.left.left = new Node(1); + head.left.right = new Node(3); + head.right.left = new Node(5); + head.right.right = new Node(7); + printTree(head); + morrisIn(head); + morrisPre(head); + morrisPos(head); + printTree(head); + + } + +} diff --git a/体系学习班/class30/Code02_MinDepth.java b/体系学习班/class30/Code02_MinDepth.java new file mode 100644 index 0000000..fa2160c --- /dev/null +++ b/体系学习班/class30/Code02_MinDepth.java @@ -0,0 +1,88 @@ +package class30; + +// 本题测试链接 : https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ +public class Code02_MinDepth { + + // 不提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int x) { + val = x; + } + } + + // 下面的方法是一般解 + public static int minDepth1(TreeNode head) { + if (head == null) { + return 0; + } + return p(head); + } + + // 返回x为头的树,最小深度是多少 + public static int p(TreeNode x) { + if (x.left == null && x.right == null) { + return 1; + } + // 左右子树起码有一个不为空 + int leftH = Integer.MAX_VALUE; + if (x.left != null) { + leftH = p(x.left); + } + int rightH = Integer.MAX_VALUE; + if (x.right != null) { + rightH = p(x.right); + } + return 1 + Math.min(leftH, rightH); + } + + // 下面的方法是morris遍历的解 + public static int minDepth2(TreeNode head) { + if (head == null) { + return 0; + } + TreeNode cur = head; + TreeNode mostRight = null; + int curLevel = 0; + int minHeight = Integer.MAX_VALUE; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + int rightBoardSize = 1; + while (mostRight.right != null && mostRight.right != cur) { + rightBoardSize++; + mostRight = mostRight.right; + } + if (mostRight.right == null) { // 第一次到达 + curLevel++; + mostRight.right = cur; + cur = cur.left; + continue; + } else { // 第二次到达 + if (mostRight.left == null) { + minHeight = Math.min(minHeight, curLevel); + } + curLevel -= rightBoardSize; + mostRight.right = null; + } + } else { // 只有一次到达 + curLevel++; + } + cur = cur.right; + } + int finalRight = 1; + cur = head; + while (cur.right != null) { + finalRight++; + cur = cur.right; + } + if (cur.left == null && cur.right == null) { + minHeight = Math.min(minHeight, finalRight); + } + return minHeight; + } + +} diff --git a/体系学习班/class31/Code01_SegmentTree.java b/体系学习班/class31/Code01_SegmentTree.java new file mode 100644 index 0000000..282c20a --- /dev/null +++ b/体系学习班/class31/Code01_SegmentTree.java @@ -0,0 +1,245 @@ +package class31; + +public class Code01_SegmentTree { + + public static class SegmentTree { + // arr[]为原序列的信息从0开始,但在arr里是从1开始的 + // sum[]模拟线段树维护区间和 + // lazy[]为累加和懒惰标记 + // change[]为更新的值 + // update[]为更新慵懒标记 + private int MAXN; + private int[] arr; + private int[] sum; + private int[] lazy; + private int[] change; + private boolean[] update; + + public SegmentTree(int[] origin) { + MAXN = origin.length + 1; + arr = new int[MAXN]; // arr[0] 不用 从1开始使用 + for (int i = 1; i < MAXN; i++) { + arr[i] = origin[i - 1]; + } + sum = new int[MAXN << 2]; // 用来支持脑补概念中,某一个范围的累加和信息 + lazy = new int[MAXN << 2]; // 用来支持脑补概念中,某一个范围沒有往下傳遞的纍加任務 + change = new int[MAXN << 2]; // 用来支持脑补概念中,某一个范围有没有更新操作的任务 + update = new boolean[MAXN << 2]; // 用来支持脑补概念中,某一个范围更新任务,更新成了什么 + } + + private void pushUp(int rt) { + sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; + } + + // 之前的,所有懒增加,和懒更新,从父范围,发给左右两个子范围 + // 分发策略是什么 + // ln表示左子树元素结点个数,rn表示右子树结点个数 + private void pushDown(int rt, int ln, int rn) { + if (update[rt]) { + update[rt << 1] = true; + update[rt << 1 | 1] = true; + change[rt << 1] = change[rt]; + change[rt << 1 | 1] = change[rt]; + lazy[rt << 1] = 0; + lazy[rt << 1 | 1] = 0; + sum[rt << 1] = change[rt] * ln; + sum[rt << 1 | 1] = change[rt] * rn; + update[rt] = false; + } + if (lazy[rt] != 0) { + lazy[rt << 1] += lazy[rt]; + sum[rt << 1] += lazy[rt] * ln; + lazy[rt << 1 | 1] += lazy[rt]; + sum[rt << 1 | 1] += lazy[rt] * rn; + lazy[rt] = 0; + } + } + + // 在初始化阶段,先把sum数组,填好 + // 在arr[l~r]范围上,去build,1~N, + // rt : 这个范围在sum中的下标 + public void build(int l, int r, int rt) { + if (l == r) { + sum[rt] = arr[l]; + return; + } + int mid = (l + r) >> 1; + build(l, mid, rt << 1); + build(mid + 1, r, rt << 1 | 1); + pushUp(rt); + } + + + // L~R 所有的值变成C + // l~r rt + public void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + update[rt] = true; + change[rt] = C; + sum[rt] = C * (r - l + 1); + lazy[rt] = 0; + return; + } + // 当前任务躲不掉,无法懒更新,要往下发 + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + // L~R, C 任务! + // rt,l~r + public void add(int L, int R, int C, int l, int r, int rt) { + // 任务如果把此时的范围全包了! + if (L <= l && r <= R) { + sum[rt] += C * (r - l + 1); + lazy[rt] += C; + return; + } + // 任务没有把你全包! + // l r mid = (l+r)/2 + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + // L~R + if (L <= mid) { + add(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + add(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + // 1~6 累加和是多少? 1~8 rt + public long query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return sum[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + long ans = 0; + if (L <= mid) { + ans += query(L, R, l, mid, rt << 1); + } + if (R > mid) { + ans += query(L, R, mid + 1, r, rt << 1 | 1); + } + return ans; + } + + } + + public static class Right { + public int[] arr; + + public Right(int[] origin) { + arr = new int[origin.length + 1]; + for (int i = 0; i < origin.length; i++) { + arr[i + 1] = origin[i]; + } + } + + public void update(int L, int R, int C) { + for (int i = L; i <= R; i++) { + arr[i] = C; + } + } + + public void add(int L, int R, int C) { + for (int i = L; i <= R; i++) { + arr[i] += C; + } + } + + public long query(int L, int R) { + long ans = 0; + for (int i = L; i <= R; i++) { + ans += arr[i]; + } + return ans; + } + + } + + public static int[] genarateRandomArray(int len, int max) { + int size = (int) (Math.random() * len) + 1; + int[] origin = new int[size]; + for (int i = 0; i < size; i++) { + origin[i] = (int) (Math.random() * max) - (int) (Math.random() * max); + } + return origin; + } + + public static boolean test() { + int len = 100; + int max = 1000; + int testTimes = 5000; + int addOrUpdateTimes = 1000; + int queryTimes = 500; + for (int i = 0; i < testTimes; i++) { + int[] origin = genarateRandomArray(len, max); + SegmentTree seg = new SegmentTree(origin); + int S = 1; + int N = origin.length; + int root = 1; + seg.build(S, N, root); + Right rig = new Right(origin); + for (int j = 0; j < addOrUpdateTimes; j++) { + int num1 = (int) (Math.random() * N) + 1; + int num2 = (int) (Math.random() * N) + 1; + int L = Math.min(num1, num2); + int R = Math.max(num1, num2); + int C = (int) (Math.random() * max) - (int) (Math.random() * max); + if (Math.random() < 0.5) { + seg.add(L, R, C, S, N, root); + rig.add(L, R, C); + } else { + seg.update(L, R, C, S, N, root); + rig.update(L, R, C); + } + } + for (int k = 0; k < queryTimes; k++) { + int num1 = (int) (Math.random() * N) + 1; + int num2 = (int) (Math.random() * N) + 1; + int L = Math.min(num1, num2); + int R = Math.max(num1, num2); + long ans1 = seg.query(L, R, S, N, root); + long ans2 = rig.query(L, R); + if (ans1 != ans2) { + return false; + } + } + } + return true; + } + + public static void main(String[] args) { + int[] origin = { 2, 1, 1, 2, 3, 4, 5 }; + SegmentTree seg = new SegmentTree(origin); + int S = 1; // 整个区间的开始位置,规定从1开始,不从0开始 -> 固定 + int N = origin.length; // 整个区间的结束位置,规定能到N,不是N-1 -> 固定 + int root = 1; // 整棵树的头节点位置,规定是1,不是0 -> 固定 + int L = 2; // 操作区间的开始位置 -> 可变 + int R = 5; // 操作区间的结束位置 -> 可变 + int C = 4; // 要加的数字或者要更新的数字 -> 可变 + // 区间生成,必须在[S,N]整个范围上build + seg.build(S, N, root); + // 区间修改,可以改变L、R和C的值,其他值不可改变 + seg.add(L, R, C, S, N, root); + // 区间更新,可以改变L、R和C的值,其他值不可改变 + seg.update(L, R, C, S, N, root); + // 区间查询,可以改变L和R的值,其他值不可改变 + long sum = seg.query(L, R, S, N, root); + System.out.println(sum); + + System.out.println("对数器测试开始..."); + System.out.println("测试结果 : " + (test() ? "通过" : "未通过")); + + } + +} diff --git a/体系学习班/class31/Code02_FallingSquares.java b/体系学习班/class31/Code02_FallingSquares.java new file mode 100644 index 0000000..2f3b2a2 --- /dev/null +++ b/体系学习班/class31/Code02_FallingSquares.java @@ -0,0 +1,109 @@ +package class31; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.TreeSet; + +public class Code02_FallingSquares { + + public static class SegmentTree { + private int[] max; + private int[] change; + private boolean[] update; + + public SegmentTree(int size) { + int N = size + 1; + max = new int[N << 2]; + + change = new int[N << 2]; + update = new boolean[N << 2]; + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + // ln表示左子树元素结点个数,rn表示右子树结点个数 + private void pushDown(int rt, int ln, int rn) { + if (update[rt]) { + update[rt << 1] = true; + update[rt << 1 | 1] = true; + change[rt << 1] = change[rt]; + change[rt << 1 | 1] = change[rt]; + max[rt << 1] = change[rt]; + max[rt << 1 | 1] = change[rt]; + update[rt] = false; + } + } + + public void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + update[rt] = true; + change[rt] = C; + max[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return max[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int left = 0; + int right = 0; + if (L <= mid) { + left = query(L, R, l, mid, rt << 1); + } + if (R > mid) { + right = query(L, R, mid + 1, r, rt << 1 | 1); + } + return Math.max(left, right); + } + + } + + public HashMap index(int[][] positions) { + TreeSet pos = new TreeSet<>(); + for (int[] arr : positions) { + pos.add(arr[0]); + pos.add(arr[0] + arr[1] - 1); + } + HashMap map = new HashMap<>(); + int count = 0; + for (Integer index : pos) { + map.put(index, ++count); + } + return map; + } + + public List fallingSquares(int[][] positions) { + HashMap map = index(positions); + int N = map.size(); + SegmentTree segmentTree = new SegmentTree(N); + int max = 0; + List res = new ArrayList<>(); + // 每落一个正方形,收集一下,所有东西组成的图像,最高高度是什么 + for (int[] arr : positions) { + int L = map.get(arr[0]); + int R = map.get(arr[0] + arr[1] - 1); + int height = segmentTree.query(L, R, 1, N, 1) + arr[1]; + max = Math.max(max, height); + res.add(max); + segmentTree.update(L, R, height, 1, N, 1); + } + return res; + } + +} diff --git a/体系学习班/class32/Code01_IndexTree.java b/体系学习班/class32/Code01_IndexTree.java new file mode 100644 index 0000000..4c4bdab --- /dev/null +++ b/体系学习班/class32/Code01_IndexTree.java @@ -0,0 +1,83 @@ +package class32; + +public class Code01_IndexTree { + + // 下标从1开始! + public static class IndexTree { + + private int[] tree; + private int N; + + // 0位置弃而不用! + public IndexTree(int size) { + N = size; + tree = new int[N + 1]; + } + + // 1~index 累加和是多少? + public int sum(int index) { + int ret = 0; + while (index > 0) { + ret += tree[index]; + index -= index & -index; + } + return ret; + } + + // index & -index : 提取出index最右侧的1出来 + // index : 0011001000 + // index & -index : 0000001000 + public void add(int index, int d) { + while (index <= N) { + tree[index] += d; + index += index & -index; + } + } + } + + public static class Right { + private int[] nums; + private int N; + + public Right(int size) { + N = size + 1; + nums = new int[N + 1]; + } + + public int sum(int index) { + int ret = 0; + for (int i = 1; i <= index; i++) { + ret += nums[i]; + } + return ret; + } + + public void add(int index, int d) { + nums[index] += d; + } + + } + + public static void main(String[] args) { + int N = 100; + int V = 100; + int testTime = 2000000; + IndexTree tree = new IndexTree(N); + Right test = new Right(N); + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int index = (int) (Math.random() * N) + 1; + if (Math.random() <= 0.5) { + int add = (int) (Math.random() * V); + tree.add(index, add); + test.add(index, add); + } else { + if (tree.sum(index) != test.sum(index)) { + System.out.println("Oops!"); + } + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class32/Code02_IndexTree2D.java b/体系学习班/class32/Code02_IndexTree2D.java new file mode 100644 index 0000000..12bb6e2 --- /dev/null +++ b/体系学习班/class32/Code02_IndexTree2D.java @@ -0,0 +1,57 @@ +package class32; + +// 测试链接:https://leetcode.com/problems/range-sum-query-2d-mutable +// 但这个题是付费题目 +// 提交时把类名、构造函数名从Code02_IndexTree2D改成NumMatrix +public class Code02_IndexTree2D { + private int[][] tree; + private int[][] nums; + private int N; + private int M; + + public Code02_IndexTree2D(int[][] matrix) { + if (matrix.length == 0 || matrix[0].length == 0) { + return; + } + N = matrix.length; + M = matrix[0].length; + tree = new int[N + 1][M + 1]; + nums = new int[N][M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + update(i, j, matrix[i][j]); + } + } + } + + private int sum(int row, int col) { + int sum = 0; + for (int i = row + 1; i > 0; i -= i & (-i)) { + for (int j = col + 1; j > 0; j -= j & (-j)) { + sum += tree[i][j]; + } + } + return sum; + } + + public void update(int row, int col, int val) { + if (N == 0 || M == 0) { + return; + } + int add = val - nums[row][col]; + nums[row][col] = val; + for (int i = row + 1; i <= N; i += i & (-i)) { + for (int j = col + 1; j <= M; j += j & (-j)) { + tree[i][j] += add; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + if (N == 0 || M == 0) { + return 0; + } + return sum(row2, col2) + sum(row1 - 1, col1 - 1) - sum(row1 - 1, col2) - sum(row2, col1 - 1); + } + +} diff --git a/体系学习班/class32/Code03_AC1.java b/体系学习班/class32/Code03_AC1.java new file mode 100644 index 0000000..a6874e5 --- /dev/null +++ b/体系学习班/class32/Code03_AC1.java @@ -0,0 +1,105 @@ +package class32; + +import java.util.LinkedList; +import java.util.Queue; + +public class Code03_AC1 { + + public static class Node { + public int end; // 有多少个字符串以该节点结尾 + public Node fail; + public Node[] nexts; + + public Node() { + end = 0; + fail = null; + nexts = new Node[26]; + } + } + + public static class ACAutomation { + private Node root; + + public ACAutomation() { + root = new Node(); + } + + // 你有多少个匹配串,就调用多少次insert + public void insert(String s) { + char[] str = s.toCharArray(); + Node cur = root; + int index = 0; + for (int i = 0; i < str.length; i++) { + index = str[i] - 'a'; + if (cur.nexts[index] == null) { + Node next = new Node(); + cur.nexts[index] = next; + } + cur = cur.nexts[index]; + } + cur.end++; + } + + public void build() { + Queue queue = new LinkedList<>(); + queue.add(root); + Node cur = null; + Node cfail = null; + while (!queue.isEmpty()) { + cur = queue.poll(); // 父 + for (int i = 0; i < 26; i++) { // 下级所有的路 + if (cur.nexts[i] != null) { // 该路下有子节点 + cur.nexts[i].fail = root; // 初始时先设置一个值 + cfail = cur.fail; + while (cfail != null) { // cur不是头节点 + if (cfail.nexts[i] != null) { + cur.nexts[i].fail = cfail.nexts[i]; + break; + } + cfail = cfail.fail; + } + queue.add(cur.nexts[i]); + } + } + } + } + + public int containNum(String content) { + char[] str = content.toCharArray(); + Node cur = root; + Node follow = null; + int index = 0; + int ans = 0; + for (int i = 0; i < str.length; i++) { + index = str[i] - 'a'; + while (cur.nexts[index] == null && cur != root) { + cur = cur.fail; + } + cur = cur.nexts[index] != null ? cur.nexts[index] : root; + follow = cur; + while (follow != root) { + if (follow.end == -1) { + break; + } + { // 不同的需求,在这一段{ }之间修改 + ans += follow.end; + follow.end = -1; + } // 不同的需求,在这一段{ }之间修改 + follow = follow.fail; + } + } + return ans; + } + + } + + public static void main(String[] args) { + ACAutomation ac = new ACAutomation(); + ac.insert("dhe"); + ac.insert("he"); + ac.insert("c"); + ac.build(); + System.out.println(ac.containNum("cdhe")); + } + +} diff --git a/体系学习班/class32/Code04_AC2.java b/体系学习班/class32/Code04_AC2.java new file mode 100644 index 0000000..e684de3 --- /dev/null +++ b/体系学习班/class32/Code04_AC2.java @@ -0,0 +1,125 @@ +package class32; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class Code04_AC2 { + + // 前缀树的节点 + public static class Node { + // 如果一个node,end为空,不是结尾 + // 如果end不为空,表示这个点是某个字符串的结尾,end的值就是这个字符串 + public String end; + // 只有在上面的end变量不为空的时候,endUse才有意义 + // 表示,这个字符串之前有没有加入过答案 + public boolean endUse; + public Node fail; + public Node[] nexts; + + public Node() { + endUse = false; + end = null; + fail = null; + nexts = new Node[26]; + } + } + + public static class ACAutomation { + private Node root; + + public ACAutomation() { + root = new Node(); + } + + public void insert(String s) { + char[] str = s.toCharArray(); + Node cur = root; + int index = 0; + for (int i = 0; i < str.length; i++) { + index = str[i] - 'a'; + if (cur.nexts[index] == null) { + cur.nexts[index] = new Node(); + } + cur = cur.nexts[index]; + } + cur.end = s; + } + + public void build() { + Queue queue = new LinkedList<>(); + queue.add(root); + Node cur = null; + Node cfail = null; + while (!queue.isEmpty()) { + // 某个父亲,cur + cur = queue.poll(); + for (int i = 0; i < 26; i++) { // 所有的路 + // cur -> 父亲 i号儿子,必须把i号儿子的fail指针设置好! + if (cur.nexts[i] != null) { // 如果真的有i号儿子 + cur.nexts[i].fail = root; + cfail = cur.fail; + while (cfail != null) { + if (cfail.nexts[i] != null) { + cur.nexts[i].fail = cfail.nexts[i]; + break; + } + cfail = cfail.fail; + } + queue.add(cur.nexts[i]); + } + } + } + } + + // 大文章:content + public List containWords(String content) { + char[] str = content.toCharArray(); + Node cur = root; + Node follow = null; + int index = 0; + List ans = new ArrayList<>(); + for (int i = 0; i < str.length; i++) { + index = str[i] - 'a'; // 路 + // 如果当前字符在这条路上没配出来,就随着fail方向走向下条路径 + while (cur.nexts[index] == null && cur != root) { + cur = cur.fail; + } + // 1) 现在来到的路径,是可以继续匹配的 + // 2) 现在来到的节点,就是前缀树的根节点 + cur = cur.nexts[index] != null ? cur.nexts[index] : root; + follow = cur; + while (follow != root) { + if (follow.endUse) { + break; + } + // 不同的需求,在这一段之间修改 + if (follow.end != null) { + ans.add(follow.end); + follow.endUse = true; + } + // 不同的需求,在这一段之间修改 + follow = follow.fail; + } + } + return ans; + } + + } + + public static void main(String[] args) { + ACAutomation ac = new ACAutomation(); + ac.insert("dhe"); + ac.insert("he"); + ac.insert("abcdheks"); + // 设置fail指针 + ac.build(); + + List contains = ac.containWords("abcdhekskdjfafhasldkflskdjhwqaeruv"); + for (String word : contains) { + System.out.println(word); + } + } + +} diff --git a/体系学习班/class33/Hash.java b/体系学习班/class33/Hash.java new file mode 100644 index 0000000..c9c01f3 --- /dev/null +++ b/体系学习班/class33/Hash.java @@ -0,0 +1,49 @@ +package class33; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +// 需要自己找一下javax.xml.bind的jar,然后导入到项目 +import javax.xml.bind.DatatypeConverter; + +public class Hash { + + private MessageDigest hash; + + public Hash(String algorithm) { + try { + hash = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public String hashCode(String input) { + return DatatypeConverter.printHexBinary(hash.digest(input.getBytes())).toUpperCase(); + } + + public static void main(String[] args) { + System.out.println("支持的算法 : "); + for (String str : Security.getAlgorithms("MessageDigest")) { + System.out.println(str); + } + System.out.println("======="); + + String algorithm = "MD5"; + Hash hash = new Hash(algorithm); + + String input1 = "zuochengyunzuochengyun1"; + String input2 = "zuochengyunzuochengyun2"; + String input3 = "zuochengyunzuochengyun3"; + String input4 = "zuochengyunzuochengyun4"; + String input5 = "zuochengyunzuochengyun5"; + System.out.println(hash.hashCode(input1)); + System.out.println(hash.hashCode(input2)); + System.out.println(hash.hashCode(input3)); + System.out.println(hash.hashCode(input4)); + System.out.println(hash.hashCode(input5)); + + } + +} diff --git a/体系学习班/class34/ReadMe.java b/体系学习班/class34/ReadMe.java new file mode 100644 index 0000000..16e42b9 --- /dev/null +++ b/体系学习班/class34/ReadMe.java @@ -0,0 +1,2 @@ +// 本章并无code,因为资源限制类题目输入需要的条件较多并且真的实现代码量巨大 +// 面试中这类题目出现也就和面试官聊解法,不会有代码实现的要求 \ No newline at end of file diff --git a/体系学习班/class35/Code01_AVLTreeMap.java b/体系学习班/class35/Code01_AVLTreeMap.java new file mode 100644 index 0000000..caafd46 --- /dev/null +++ b/体系学习班/class35/Code01_AVLTreeMap.java @@ -0,0 +1,257 @@ +package class35; + +public class Code01_AVLTreeMap { + + public static class AVLNode, V> { + public K k; + public V v; + public AVLNode l; + public AVLNode r; + public int h; + + public AVLNode(K key, V value) { + k = key; + v = value; + h = 1; + } + } + + public static class AVLTreeMap, V> { + private AVLNode root; + private int size; + + public AVLTreeMap() { + root = null; + size = 0; + } + + private AVLNode rightRotate(AVLNode cur) { + AVLNode left = cur.l; + cur.l = left.r; + left.r = cur; + cur.h = Math.max((cur.l != null ? cur.l.h : 0), (cur.r != null ? cur.r.h : 0)) + 1; + left.h = Math.max((left.l != null ? left.l.h : 0), (left.r != null ? left.r.h : 0)) + 1; + return left; + } + + private AVLNode leftRotate(AVLNode cur) { + AVLNode right = cur.r; + cur.r = right.l; + right.l = cur; + cur.h = Math.max((cur.l != null ? cur.l.h : 0), (cur.r != null ? cur.r.h : 0)) + 1; + right.h = Math.max((right.l != null ? right.l.h : 0), (right.r != null ? right.r.h : 0)) + 1; + return right; + } + + private AVLNode maintain(AVLNode cur) { + if (cur == null) { + return null; + } + int leftHeight = cur.l != null ? cur.l.h : 0; + int rightHeight = cur.r != null ? cur.r.h : 0; + if (Math.abs(leftHeight - rightHeight) > 1) { + if (leftHeight > rightHeight) { + int leftLeftHeight = cur.l != null && cur.l.l != null ? cur.l.l.h : 0; + int leftRightHeight = cur.l != null && cur.l.r != null ? cur.l.r.h : 0; + if (leftLeftHeight >= leftRightHeight) { + cur = rightRotate(cur); + } else { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + } + } else { + int rightLeftHeight = cur.r != null && cur.r.l != null ? cur.r.l.h : 0; + int rightRightHeight = cur.r != null && cur.r.r != null ? cur.r.r.h : 0; + if (rightRightHeight >= rightLeftHeight) { + cur = leftRotate(cur); + } else { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + } + } + } + return cur; + } + + private AVLNode findLastIndex(K key) { + AVLNode pre = root; + AVLNode cur = root; + while (cur != null) { + pre = cur; + if (key.compareTo(cur.k) == 0) { + break; + } else if (key.compareTo(cur.k) < 0) { + cur = cur.l; + } else { + cur = cur.r; + } + } + return pre; + } + + private AVLNode findLastNoSmallIndex(K key) { + AVLNode ans = null; + AVLNode cur = root; + while (cur != null) { + if (key.compareTo(cur.k) == 0) { + ans = cur; + break; + } else if (key.compareTo(cur.k) < 0) { + ans = cur; + cur = cur.l; + } else { + cur = cur.r; + } + } + return ans; + } + + private AVLNode findLastNoBigIndex(K key) { + AVLNode ans = null; + AVLNode cur = root; + while (cur != null) { + if (key.compareTo(cur.k) == 0) { + ans = cur; + break; + } else if (key.compareTo(cur.k) < 0) { + cur = cur.l; + } else { + ans = cur; + cur = cur.r; + } + } + return ans; + } + + private AVLNode add(AVLNode cur, K key, V value) { + if (cur == null) { + return new AVLNode(key, value); + } else { + if (key.compareTo(cur.k) < 0) { + cur.l = add(cur.l, key, value); + } else { + cur.r = add(cur.r, key, value); + } + cur.h = Math.max(cur.l != null ? cur.l.h : 0, cur.r != null ? cur.r.h : 0) + 1; + return maintain(cur); + } + } + + // 在cur这棵树上,删掉key所代表的节点 + // 返回cur这棵树的新头部 + private AVLNode delete(AVLNode cur, K key) { + if (key.compareTo(cur.k) > 0) { + cur.r = delete(cur.r, key); + } else if (key.compareTo(cur.k) < 0) { + cur.l = delete(cur.l, key); + } else { + if (cur.l == null && cur.r == null) { + cur = null; + } else if (cur.l == null && cur.r != null) { + cur = cur.r; + } else if (cur.l != null && cur.r == null) { + cur = cur.l; + } else { + AVLNode des = cur.r; + while (des.l != null) { + des = des.l; + } + cur.r = delete(cur.r, des.k); + des.l = cur.l; + des.r = cur.r; + cur = des; + } + } + if (cur != null) { + cur.h = Math.max(cur.l != null ? cur.l.h : 0, cur.r != null ? cur.r.h : 0) + 1; + } + return maintain(cur); + } + + public int size() { + return size; + } + + public boolean containsKey(K key) { + if (key == null) { + return false; + } + AVLNode lastNode = findLastIndex(key); + return lastNode != null && key.compareTo(lastNode.k) == 0 ? true : false; + } + + public void put(K key, V value) { + if (key == null) { + return; + } + AVLNode lastNode = findLastIndex(key); + if (lastNode != null && key.compareTo(lastNode.k) == 0) { + lastNode.v = value; + } else { + size++; + root = add(root, key, value); + } + } + + public void remove(K key) { + if (key == null) { + return; + } + if (containsKey(key)) { + size--; + root = delete(root, key); + } + } + + public V get(K key) { + if (key == null) { + return null; + } + AVLNode lastNode = findLastIndex(key); + if (lastNode != null && key.compareTo(lastNode.k) == 0) { + return lastNode.v; + } + return null; + } + + public K firstKey() { + if (root == null) { + return null; + } + AVLNode cur = root; + while (cur.l != null) { + cur = cur.l; + } + return cur.k; + } + + public K lastKey() { + if (root == null) { + return null; + } + AVLNode cur = root; + while (cur.r != null) { + cur = cur.r; + } + return cur.k; + } + + public K floorKey(K key) { + if (key == null) { + return null; + } + AVLNode lastNoBigNode = findLastNoBigIndex(key); + return lastNoBigNode == null ? null : lastNoBigNode.k; + } + + public K ceilingKey(K key) { + if (key == null) { + return null; + } + AVLNode lastNoSmallNode = findLastNoSmallIndex(key); + return lastNoSmallNode == null ? null : lastNoSmallNode.k; + } + + } + +} diff --git a/体系学习班/class36/Code01_SizeBalancedTreeMap.java b/体系学习班/class36/Code01_SizeBalancedTreeMap.java new file mode 100644 index 0000000..5077b7a --- /dev/null +++ b/体系学习班/class36/Code01_SizeBalancedTreeMap.java @@ -0,0 +1,360 @@ +package class36; + +public class Code01_SizeBalancedTreeMap { + + public static class SBTNode, V> { + public K key; + public V value; + public SBTNode l; + public SBTNode r; + public int size; // 不同的key的数量 + + public SBTNode(K key, V value) { + this.key = key; + this.value = value; + size = 1; + } + } + + public static class SizeBalancedTreeMap, V> { + private SBTNode root; + + private SBTNode rightRotate(SBTNode cur) { + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + int leftSize = cur.l != null ? cur.l.size : 0; + int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + int rightSize = cur.r != null ? cur.r.size : 0; + int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + private SBTNode findLastIndex(K key) { + SBTNode pre = root; + SBTNode cur = root; + while (cur != null) { + pre = cur; + if (key.compareTo(cur.key) == 0) { + break; + } else if (key.compareTo(cur.key) < 0) { + cur = cur.l; + } else { + cur = cur.r; + } + } + return pre; + } + + private SBTNode findLastNoSmallIndex(K key) { + SBTNode ans = null; + SBTNode cur = root; + while (cur != null) { + if (key.compareTo(cur.key) == 0) { + ans = cur; + break; + } else if (key.compareTo(cur.key) < 0) { + ans = cur; + cur = cur.l; + } else { + cur = cur.r; + } + } + return ans; + } + + private SBTNode findLastNoBigIndex(K key) { + SBTNode ans = null; + SBTNode cur = root; + while (cur != null) { + if (key.compareTo(cur.key) == 0) { + ans = cur; + break; + } else if (key.compareTo(cur.key) < 0) { + cur = cur.l; + } else { + ans = cur; + cur = cur.r; + } + } + return ans; + } + + // 现在,以cur为头的树上,新增,加(key, value)这样的记录 + // 加完之后,会对cur做检查,该调整调整 + // 返回,调整完之后,整棵树的新头部 + private SBTNode add(SBTNode cur, K key, V value) { + if (cur == null) { + return new SBTNode(key, value); + } else { + cur.size++; + if (key.compareTo(cur.key) < 0) { + cur.l = add(cur.l, key, value); + } else { + cur.r = add(cur.r, key, value); + } + return maintain(cur); + } + } + + // 在cur这棵树上,删掉key所代表的节点 + // 返回cur这棵树的新头部 + private SBTNode delete(SBTNode cur, K key) { + cur.size--; + if (key.compareTo(cur.key) > 0) { + cur.r = delete(cur.r, key); + } else if (key.compareTo(cur.key) < 0) { + cur.l = delete(cur.l, key); + } else { // 当前要删掉cur + if (cur.l == null && cur.r == null) { + // free cur memory -> C++ + cur = null; + } else if (cur.l == null && cur.r != null) { + // free cur memory -> C++ + cur = cur.r; + } else if (cur.l != null && cur.r == null) { + // free cur memory -> C++ + cur = cur.l; + } else { // 有左有右 + SBTNode pre = null; + SBTNode des = cur.r; + des.size--; + while (des.l != null) { + pre = des; + des = des.l; + des.size--; + } + if (pre != null) { + pre.l = des.r; + des.r = cur.r; + } + des.l = cur.l; + des.size = des.l.size + (des.r == null ? 0 : des.r.size) + 1; + // free cur memory -> C++ + cur = des; + } + } + // cur = maintain(cur); + return cur; + } + + private SBTNode getIndex(SBTNode cur, int kth) { + if (kth == (cur.l != null ? cur.l.size : 0) + 1) { + return cur; + } else if (kth <= (cur.l != null ? cur.l.size : 0)) { + return getIndex(cur.l, kth); + } else { + return getIndex(cur.r, kth - (cur.l != null ? cur.l.size : 0) - 1); + } + } + + public int size() { + return root == null ? 0 : root.size; + } + + public boolean containsKey(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + SBTNode lastNode = findLastIndex(key); + return lastNode != null && key.compareTo(lastNode.key) == 0 ? true : false; + } + + // (key,value) put -> 有序表 新增、改value + public void put(K key, V value) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + SBTNode lastNode = findLastIndex(key); + if (lastNode != null && key.compareTo(lastNode.key) == 0) { + lastNode.value = value; + } else { + root = add(root, key, value); + } + } + + public void remove(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + if (containsKey(key)) { + root = delete(root, key); + } + } + + public K getIndexKey(int index) { + if (index < 0 || index >= this.size()) { + throw new RuntimeException("invalid parameter."); + } + return getIndex(root, index + 1).key; + } + + public V getIndexValue(int index) { + if (index < 0 || index >= this.size()) { + throw new RuntimeException("invalid parameter."); + } + return getIndex(root, index + 1).value; + } + + public V get(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + SBTNode lastNode = findLastIndex(key); + if (lastNode != null && key.compareTo(lastNode.key) == 0) { + return lastNode.value; + } else { + return null; + } + } + + public K firstKey() { + if (root == null) { + return null; + } + SBTNode cur = root; + while (cur.l != null) { + cur = cur.l; + } + return cur.key; + } + + public K lastKey() { + if (root == null) { + return null; + } + SBTNode cur = root; + while (cur.r != null) { + cur = cur.r; + } + return cur.key; + } + + public K floorKey(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + SBTNode lastNoBigNode = findLastNoBigIndex(key); + return lastNoBigNode == null ? null : lastNoBigNode.key; + } + + public K ceilingKey(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + SBTNode lastNoSmallNode = findLastNoSmallIndex(key); + return lastNoSmallNode == null ? null : lastNoSmallNode.key; + } + + } + + // for test + public static void printAll(SBTNode head) { + System.out.println("Binary Tree:"); + printInOrder(head, 0, "H", 17); + System.out.println(); + } + + // for test + public static void printInOrder(SBTNode head, int height, String to, int len) { + if (head == null) { + return; + } + printInOrder(head.r, height + 1, "v", len); + String val = to + "(" + head.key + "," + head.value + ")" + to; + int lenM = val.length(); + int lenL = (len - lenM) / 2; + int lenR = len - lenM - lenL; + val = getSpace(lenL) + val + getSpace(lenR); + System.out.println(getSpace(height * len) + val); + printInOrder(head.l, height + 1, "^", len); + } + + // for test + public static String getSpace(int num) { + String space = " "; + StringBuffer buf = new StringBuffer(""); + for (int i = 0; i < num; i++) { + buf.append(space); + } + return buf.toString(); + } + + public static void main(String[] args) { + SizeBalancedTreeMap sbt = new SizeBalancedTreeMap(); + sbt.put("d", 4); + sbt.put("c", 3); + sbt.put("a", 1); + sbt.put("b", 2); + // sbt.put("e", 5); + sbt.put("g", 7); + sbt.put("f", 6); + sbt.put("h", 8); + sbt.put("i", 9); + sbt.put("a", 111); + System.out.println(sbt.get("a")); + sbt.put("a", 1); + System.out.println(sbt.get("a")); + for (int i = 0; i < sbt.size(); i++) { + System.out.println(sbt.getIndexKey(i) + " , " + sbt.getIndexValue(i)); + } + printAll(sbt.root); + System.out.println(sbt.firstKey()); + System.out.println(sbt.lastKey()); + System.out.println(sbt.floorKey("g")); + System.out.println(sbt.ceilingKey("g")); + System.out.println(sbt.floorKey("e")); + System.out.println(sbt.ceilingKey("e")); + System.out.println(sbt.floorKey("")); + System.out.println(sbt.ceilingKey("")); + System.out.println(sbt.floorKey("j")); + System.out.println(sbt.ceilingKey("j")); + sbt.remove("d"); + printAll(sbt.root); + sbt.remove("f"); + printAll(sbt.root); + + } + +} diff --git a/体系学习班/class36/Code02_SkipListMap.java b/体系学习班/class36/Code02_SkipListMap.java new file mode 100644 index 0000000..a450e78 --- /dev/null +++ b/体系学习班/class36/Code02_SkipListMap.java @@ -0,0 +1,248 @@ +package class36; + +import java.util.ArrayList; + +public class Code02_SkipListMap { + + // 跳表的节点定义 + public static class SkipListNode, V> { + public K key; + public V val; + public ArrayList> nextNodes; + + public SkipListNode(K k, V v) { + key = k; + val = v; + nextNodes = new ArrayList>(); + } + + // 遍历的时候,如果是往右遍历到的null(next == null), 遍历结束 + // 头(null), 头节点的null,认为最小 + // node -> 头,node(null, "") node.isKeyLess(!null) true + // node里面的key是否比otherKey小,true,不是false + public boolean isKeyLess(K otherKey) { + // otherKey == null -> false + return otherKey != null && (key == null || key.compareTo(otherKey) < 0); + } + + public boolean isKeyEqual(K otherKey) { + return (key == null && otherKey == null) + || (key != null && otherKey != null && key.compareTo(otherKey) == 0); + } + + } + + public static class SkipListMap, V> { + private static final double PROBABILITY = 0.5; // < 0.5 继续做,>=0.5 停 + private SkipListNode head; + private int size; + private int maxLevel; + + public SkipListMap() { + head = new SkipListNode(null, null); + head.nextNodes.add(null); // 0 + size = 0; + maxLevel = 0; + } + + // 从最高层开始,一路找下去, + // 最终,找到第0层的 mostRightLessNodeInTree(K key) { + if (key == null) { + return null; + } + int level = maxLevel; + SkipListNode cur = head; + while (level >= 0) { // 从上层跳下层 + // cur level -> level-1 + cur = mostRightLessNodeInLevel(key, cur, level--); + } + return cur; + } + + // 在level层里,如何往右移动 + // 现在来到的节点是cur,来到了cur的level层,在level层上,找到 mostRightLessNodeInLevel(K key, + SkipListNode cur, + int level) { + SkipListNode next = cur.nextNodes.get(level); + while (next != null && next.isKeyLess(key)) { + cur = next; + next = cur.nextNodes.get(level); + } + return cur; + } + + public boolean containsKey(K key) { + if (key == null) { + return false; + } + SkipListNode less = mostRightLessNodeInTree(key); + SkipListNode next = less.nextNodes.get(0); + return next != null && next.isKeyEqual(key); + } + + // 新增、改value + public void put(K key, V value) { + if (key == null) { + return; + } + // 0层上,最右一个,< key 的Node -> >key + SkipListNode less = mostRightLessNodeInTree(key); + SkipListNode find = less.nextNodes.get(0); + if (find != null && find.isKeyEqual(key)) { + find.val = value; + } else { // find == null 8 7 9 + size++; + int newNodeLevel = 0; + while (Math.random() < PROBABILITY) { + newNodeLevel++; + } + // newNodeLevel + while (newNodeLevel > maxLevel) { + head.nextNodes.add(null); + maxLevel++; + } + SkipListNode newNode = new SkipListNode(key, value); + for (int i = 0; i <= newNodeLevel; i++) { + newNode.nextNodes.add(null); + } + int level = maxLevel; + SkipListNode pre = head; + while (level >= 0) { + // level 层中,找到最右的 < key 的节点 + pre = mostRightLessNodeInLevel(key, pre, level); + if (level <= newNodeLevel) { + newNode.nextNodes.set(level, pre.nextNodes.get(level)); + pre.nextNodes.set(level, newNode); + } + level--; + } + } + } + + public V get(K key) { + if (key == null) { + return null; + } + SkipListNode less = mostRightLessNodeInTree(key); + SkipListNode next = less.nextNodes.get(0); + return next != null && next.isKeyEqual(key) ? next.val : null; + } + + public void remove(K key) { + if (containsKey(key)) { + size--; + int level = maxLevel; + SkipListNode pre = head; + while (level >= 0) { + pre = mostRightLessNodeInLevel(key, pre, level); + SkipListNode next = pre.nextNodes.get(level); + // 1)在这一层中,pre下一个就是key + // 2)在这一层中,pre的下一个key是>要删除key + if (next != null && next.isKeyEqual(key)) { + // free delete node memory -> C++ + // level : pre -> next(key) -> ... + pre.nextNodes.set(level, next.nextNodes.get(level)); + } + // 在level层只有一个节点了,就是默认节点head + if (level != 0 && pre == head && pre.nextNodes.get(level) == null) { + head.nextNodes.remove(level); + maxLevel--; + } + level--; + } + } + } + + public K firstKey() { + return head.nextNodes.get(0) != null ? head.nextNodes.get(0).key : null; + } + + public K lastKey() { + int level = maxLevel; + SkipListNode cur = head; + while (level >= 0) { + SkipListNode next = cur.nextNodes.get(level); + while (next != null) { + cur = next; + next = cur.nextNodes.get(level); + } + level--; + } + return cur.key; + } + + public K ceilingKey(K key) { + if (key == null) { + return null; + } + SkipListNode less = mostRightLessNodeInTree(key); + SkipListNode next = less.nextNodes.get(0); + return next != null ? next.key : null; + } + + public K floorKey(K key) { + if (key == null) { + return null; + } + SkipListNode less = mostRightLessNodeInTree(key); + SkipListNode next = less.nextNodes.get(0); + return next != null && next.isKeyEqual(key) ? next.key : less.key; + } + + public int size() { + return size; + } + + } + + // for test + public static void printAll(SkipListMap obj) { + for (int i = obj.maxLevel; i >= 0; i--) { + System.out.print("Level " + i + " : "); + SkipListNode cur = obj.head; + while (cur.nextNodes.get(i) != null) { + SkipListNode next = cur.nextNodes.get(i); + System.out.print("(" + next.key + " , " + next.val + ") "); + cur = next; + } + System.out.println(); + } + } + + public static void main(String[] args) { + SkipListMap test = new SkipListMap<>(); + printAll(test); + System.out.println("======================"); + test.put("A", "10"); + printAll(test); + System.out.println("======================"); + test.remove("A"); + printAll(test); + System.out.println("======================"); + test.put("E", "E"); + test.put("B", "B"); + test.put("A", "A"); + test.put("F", "F"); + test.put("C", "C"); + test.put("D", "D"); + printAll(test); + System.out.println("======================"); + System.out.println(test.containsKey("B")); + System.out.println(test.containsKey("Z")); + System.out.println(test.firstKey()); + System.out.println(test.lastKey()); + System.out.println(test.floorKey("D")); + System.out.println(test.ceilingKey("D")); + System.out.println("======================"); + test.remove("D"); + printAll(test); + System.out.println("======================"); + System.out.println(test.floorKey("D")); + System.out.println(test.ceilingKey("D")); + + + } + +} diff --git a/体系学习班/class37/Code01_CountofRangeSum.java b/体系学习班/class37/Code01_CountofRangeSum.java new file mode 100644 index 0000000..d79fa02 --- /dev/null +++ b/体系学习班/class37/Code01_CountofRangeSum.java @@ -0,0 +1,223 @@ +package class37; + +import java.util.HashSet; + +public class Code01_CountofRangeSum { + + public static int countRangeSum1(int[] nums, int lower, int upper) { + int n = nums.length; + long[] sums = new long[n + 1]; + for (int i = 0; i < n; ++i) + sums[i + 1] = sums[i] + nums[i]; + return countWhileMergeSort(sums, 0, n + 1, lower, upper); + } + + private static int countWhileMergeSort(long[] sums, int start, int end, int lower, int upper) { + if (end - start <= 1) + return 0; + int mid = (start + end) / 2; + int count = countWhileMergeSort(sums, start, mid, lower, upper) + + countWhileMergeSort(sums, mid, end, lower, upper); + int j = mid, k = mid, t = mid; + long[] cache = new long[end - start]; + for (int i = start, r = 0; i < mid; ++i, ++r) { + while (k < end && sums[k] - sums[i] < lower) + k++; + while (j < end && sums[j] - sums[i] <= upper) + j++; + while (t < end && sums[t] < sums[i]) + cache[r++] = sums[t++]; + cache[r] = sums[i]; + count += j - k; + } + System.arraycopy(cache, 0, sums, start, t - start); + return count; + } + + public static class SBTNode { + public long key; + public SBTNode l; + public SBTNode r; + public long size; // 不同key的size + public long all; // 总的size + + public SBTNode(long k) { + key = k; + size = 1; + all = 1; + } + } + + public static class SizeBalancedTreeSet { + private SBTNode root; + private HashSet set = new HashSet<>(); + + private SBTNode rightRotate(SBTNode cur) { + long same = cur.all - (cur.l != null ? cur.l.all : 0) - (cur.r != null ? cur.r.all : 0); + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + // all modify + leftNode.all = cur.all; + cur.all = (cur.l != null ? cur.l.all : 0) + (cur.r != null ? cur.r.all : 0) + same; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + long same = cur.all - (cur.l != null ? cur.l.all : 0) - (cur.r != null ? cur.r.all : 0); + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + // all modify + rightNode.all = cur.all; + cur.all = (cur.l != null ? cur.l.all : 0) + (cur.r != null ? cur.r.all : 0) + same; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + long leftSize = cur.l != null ? cur.l.size : 0; + long leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + long leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + long rightSize = cur.r != null ? cur.r.size : 0; + long rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + long rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + private SBTNode add(SBTNode cur, long key, boolean contains) { + if (cur == null) { + return new SBTNode(key); + } else { + cur.all++; + if (key == cur.key) { + return cur; + } else { // 还在左滑或者右滑 + if (!contains) { + cur.size++; + } + if (key < cur.key) { + cur.l = add(cur.l, key, contains); + } else { + cur.r = add(cur.r, key, contains); + } + return maintain(cur); + } + } + } + + public void add(long sum) { + boolean contains = set.contains(sum); + root = add(root, sum, contains); + set.add(sum); + } + + public long lessKeySize(long key) { + SBTNode cur = root; + long ans = 0; + while (cur != null) { + if (key == cur.key) { + return ans + (cur.l != null ? cur.l.all : 0); + } else if (key < cur.key) { + cur = cur.l; + } else { + ans += cur.all - (cur.r != null ? cur.r.all : 0); + cur = cur.r; + } + } + return ans; + } + + // > 7 8... + // <8 ...<=7 + public long moreKeySize(long key) { + return root != null ? (root.all - lessKeySize(key + 1)) : 0; + } + + } + + public static int countRangeSum2(int[] nums, int lower, int upper) { + // 黑盒,加入数字(前缀和),不去重,可以接受重复数字 + // < num , 有几个数? + SizeBalancedTreeSet treeSet = new SizeBalancedTreeSet(); + long sum = 0; + int ans = 0; + treeSet.add(0);// 一个数都没有的时候,就已经有一个前缀和累加和为0, + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + // [sum - upper, sum - lower] + // [10, 20] ? + // < 10 ? < 21 ? + long a = treeSet.lessKeySize(sum - lower + 1); + long b = treeSet.lessKeySize(sum - upper); + ans += a - b; + treeSet.add(sum); + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static int[] generateArray(int len, int varible) { + int[] arr = new int[len]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * varible); + } + return arr; + } + + public static void main(String[] args) { + int len = 200; + int varible = 50; + for (int i = 0; i < 10000; i++) { + int[] test = generateArray(len, varible); + int lower = (int) (Math.random() * varible) - (int) (Math.random() * varible); + int upper = lower + (int) (Math.random() * varible); + int ans1 = countRangeSum1(test, lower, upper); + int ans2 = countRangeSum2(test, lower, upper); + if (ans1 != ans2) { + printArray(test); + System.out.println(lower); + System.out.println(upper); + System.out.println(ans1); + System.out.println(ans2); + } + } + + } + +} diff --git a/体系学习班/class37/Code02_SlidingWindowMedian.java b/体系学习班/class37/Code02_SlidingWindowMedian.java new file mode 100644 index 0000000..2f3fa63 --- /dev/null +++ b/体系学习班/class37/Code02_SlidingWindowMedian.java @@ -0,0 +1,228 @@ +package class37; + +public class Code02_SlidingWindowMedian { + + public static class SBTNode> { + public K key; + public SBTNode l; + public SBTNode r; + public int size; + + public SBTNode(K k) { + key = k; + size = 1; + } + } + + public static class SizeBalancedTreeMap> { + private SBTNode root; + + private SBTNode rightRotate(SBTNode cur) { + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + int leftSize = cur.l != null ? cur.l.size : 0; + int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + int rightSize = cur.r != null ? cur.r.size : 0; + int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + private SBTNode findLastIndex(K key) { + SBTNode pre = root; + SBTNode cur = root; + while (cur != null) { + pre = cur; + if (key.compareTo(cur.key) == 0) { + break; + } else if (key.compareTo(cur.key) < 0) { + cur = cur.l; + } else { + cur = cur.r; + } + } + return pre; + } + + private SBTNode add(SBTNode cur, K key) { + if (cur == null) { + return new SBTNode(key); + } else { + cur.size++; + if (key.compareTo(cur.key) < 0) { + cur.l = add(cur.l, key); + } else { + cur.r = add(cur.r, key); + } + return maintain(cur); + } + } + + private SBTNode delete(SBTNode cur, K key) { + cur.size--; + if (key.compareTo(cur.key) > 0) { + cur.r = delete(cur.r, key); + } else if (key.compareTo(cur.key) < 0) { + cur.l = delete(cur.l, key); + } else { + if (cur.l == null && cur.r == null) { + // free cur memory -> C++ + cur = null; + } else if (cur.l == null && cur.r != null) { + // free cur memory -> C++ + cur = cur.r; + } else if (cur.l != null && cur.r == null) { + // free cur memory -> C++ + cur = cur.l; + } else { + SBTNode pre = null; + SBTNode des = cur.r; + des.size--; + while (des.l != null) { + pre = des; + des = des.l; + des.size--; + } + if (pre != null) { + pre.l = des.r; + des.r = cur.r; + } + des.l = cur.l; + des.size = des.l.size + (des.r == null ? 0 : des.r.size) + 1; + // free cur memory -> C++ + cur = des; + } + } + return cur; + } + + private SBTNode getIndex(SBTNode cur, int kth) { + if (kth == (cur.l != null ? cur.l.size : 0) + 1) { + return cur; + } else if (kth <= (cur.l != null ? cur.l.size : 0)) { + return getIndex(cur.l, kth); + } else { + return getIndex(cur.r, kth - (cur.l != null ? cur.l.size : 0) - 1); + } + } + + public int size() { + return root == null ? 0 : root.size; + } + + public boolean containsKey(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + SBTNode lastNode = findLastIndex(key); + return lastNode != null && key.compareTo(lastNode.key) == 0 ? true : false; + } + + public void add(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + SBTNode lastNode = findLastIndex(key); + if (lastNode == null || key.compareTo(lastNode.key) != 0) { + root = add(root, key); + } + } + + public void remove(K key) { + if (key == null) { + throw new RuntimeException("invalid parameter."); + } + if (containsKey(key)) { + root = delete(root, key); + } + } + + public K getIndexKey(int index) { + if (index < 0 || index >= this.size()) { + throw new RuntimeException("invalid parameter."); + } + return getIndex(root, index + 1).key; + } + + } + + public static class Node implements Comparable { + public int index; + public int value; + + public Node(int i, int v) { + index = i; + value = v; + } + + @Override + public int compareTo(Node o) { + return value != o.value ? Integer.valueOf(value).compareTo(o.value) + : Integer.valueOf(index).compareTo(o.index); + } + } + + public static double[] medianSlidingWindow(int[] nums, int k) { + SizeBalancedTreeMap map = new SizeBalancedTreeMap<>(); + for (int i = 0; i < k - 1; i++) { + map.add(new Node(i, nums[i])); + } + double[] ans = new double[nums.length - k + 1]; + int index = 0; + for (int i = k - 1; i < nums.length; i++) { + map.add(new Node(i, nums[i])); + if (map.size() % 2 == 0) { + Node upmid = map.getIndexKey(map.size() / 2 - 1); + Node downmid = map.getIndexKey(map.size() / 2); + ans[index++] = ((double) upmid.value + (double) downmid.value) / 2; + } else { + Node mid = map.getIndexKey(map.size() / 2); + ans[index++] = (double) mid.value; + } + map.remove(new Node(i - k + 1, nums[i - k + 1])); + } + return ans; + } + +} diff --git a/体系学习班/class37/Code03_AddRemoveGetIndexGreat.java b/体系学习班/class37/Code03_AddRemoveGetIndexGreat.java new file mode 100644 index 0000000..2c2436a --- /dev/null +++ b/体系学习班/class37/Code03_AddRemoveGetIndexGreat.java @@ -0,0 +1,259 @@ +package class37; + +import java.util.ArrayList; + +public class Code03_AddRemoveGetIndexGreat { + + public static class SBTNode { + public V value; + public SBTNode l; + public SBTNode r; + public int size; + + public SBTNode(V v) { + value = v; + size = 1; + } + } + + public static class SbtList { + private SBTNode root; + + private SBTNode rightRotate(SBTNode cur) { + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + int leftSize = cur.l != null ? cur.l.size : 0; + int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + int rightSize = cur.r != null ? cur.r.size : 0; + int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + private SBTNode add(SBTNode root, int index, SBTNode cur) { + if (root == null) { + return cur; + } + root.size++; + int leftAndHeadSize = (root.l != null ? root.l.size : 0) + 1; + if (index < leftAndHeadSize) { + root.l = add(root.l, index, cur); + } else { + root.r = add(root.r, index - leftAndHeadSize, cur); + } + root = maintain(root); + return root; + } + + private SBTNode remove(SBTNode root, int index) { + root.size--; + int rootIndex = root.l != null ? root.l.size : 0; + if (index != rootIndex) { + if (index < rootIndex) { + root.l = remove(root.l, index); + } else { + root.r = remove(root.r, index - rootIndex - 1); + } + return root; + } + if (root.l == null && root.r == null) { + return null; + } + if (root.l == null) { + return root.r; + } + if (root.r == null) { + return root.l; + } + SBTNode pre = null; + SBTNode suc = root.r; + suc.size--; + while (suc.l != null) { + pre = suc; + suc = suc.l; + suc.size--; + } + if (pre != null) { + pre.l = suc.r; + suc.r = root.r; + } + suc.l = root.l; + suc.size = suc.l.size + (suc.r == null ? 0 : suc.r.size) + 1; + return suc; + } + + private SBTNode get(SBTNode root, int index) { + int leftSize = root.l != null ? root.l.size : 0; + if (index < leftSize) { + return get(root.l, index); + } else if (index == leftSize) { + return root; + } else { + return get(root.r, index - leftSize - 1); + } + } + + public void add(int index, V num) { + SBTNode cur = new SBTNode(num); + if (root == null) { + root = cur; + } else { + if (index <= root.size) { + root = add(root, index, cur); + } + } + } + + public V get(int index) { + SBTNode ans = get(root, index); + return ans.value; + } + + public void remove(int index) { + if (index >= 0 && size() > index) { + root = remove(root, index); + } + } + + public int size() { + return root == null ? 0 : root.size; + } + + } + + // 通过以下这个测试, + // 可以很明显的看到LinkedList的插入、删除、get效率不如SbtList + // LinkedList需要找到index所在的位置之后才能插入或者读取,时间复杂度O(N) + // SbtList是平衡搜索二叉树,所以插入或者读取时间复杂度都是O(logN) + public static void main(String[] args) { + // 功能测试 + int test = 50000; + int max = 1000000; + boolean pass = true; + ArrayList list = new ArrayList<>(); + SbtList sbtList = new SbtList<>(); + for (int i = 0; i < test; i++) { + if (list.size() != sbtList.size()) { + pass = false; + break; + } + if (list.size() > 1 && Math.random() < 0.5) { + int removeIndex = (int) (Math.random() * list.size()); + list.remove(removeIndex); + sbtList.remove(removeIndex); + } else { + int randomIndex = (int) (Math.random() * (list.size() + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + sbtList.add(randomIndex, randomValue); + } + } + for (int i = 0; i < list.size(); i++) { + if (!list.get(i).equals(sbtList.get(i))) { + pass = false; + break; + } + } + System.out.println("功能测试是否通过 : " + pass); + + // 性能测试 + test = 500000; + list = new ArrayList<>(); + sbtList = new SbtList<>(); + long start = 0; + long end = 0; + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (list.size() + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("ArrayList插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + list.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("ArrayList读取总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * list.size()); + list.remove(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("ArrayList删除总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (sbtList.size() + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + sbtList.add(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("SbtList插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + sbtList.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("SbtList读取总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * sbtList.size()); + sbtList.remove(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("SbtList删除总时长(毫秒) : " + (end - start)); + + } + +} diff --git a/体系学习班/class37/Code04_QueueReconstructionByHeight.java b/体系学习班/class37/Code04_QueueReconstructionByHeight.java new file mode 100644 index 0000000..2513402 --- /dev/null +++ b/体系学习班/class37/Code04_QueueReconstructionByHeight.java @@ -0,0 +1,265 @@ +package class37; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; + +// 本题测试链接:https://leetcode.com/problems/queue-reconstruction-by-height/ +public class Code04_QueueReconstructionByHeight { + + public static int[][] reconstructQueue1(int[][] people) { + int N = people.length; + Unit[] units = new Unit[N]; + for (int i = 0; i < N; i++) { + units[i] = new Unit(people[i][0], people[i][1]); + } + Arrays.sort(units, new UnitComparator()); + ArrayList arrList = new ArrayList<>(); + for (Unit unit : units) { + arrList.add(unit.k, unit); + } + int[][] ans = new int[N][2]; + int index = 0; + for (Unit unit : arrList) { + ans[index][0] = unit.h; + ans[index++][1] = unit.k; + } + return ans; + } + + public static int[][] reconstructQueue2(int[][] people) { + int N = people.length; + Unit[] units = new Unit[N]; + for (int i = 0; i < N; i++) { + units[i] = new Unit(people[i][0], people[i][1]); + } + Arrays.sort(units, new UnitComparator()); + SBTree tree = new SBTree(); + for (int i = 0; i < N; i++) { + tree.insert(units[i].k, i); + } + LinkedList allIndexes = tree.allIndexes(); + int[][] ans = new int[N][2]; + int index = 0; + for (Integer arri : allIndexes) { + ans[index][0] = units[arri].h; + ans[index++][1] = units[arri].k; + } + return ans; + } + + public static class Unit { + public int h; + public int k; + + public Unit(int height, int greater) { + h = height; + k = greater; + } + } + + public static class UnitComparator implements Comparator { + + @Override + public int compare(Unit o1, Unit o2) { + return o1.h != o2.h ? (o2.h - o1.h) : (o1.k - o2.k); + } + + } + + public static class SBTNode { + public int value; + public SBTNode l; + public SBTNode r; + public int size; + + public SBTNode(int arrIndex) { + value = arrIndex; + size = 1; + } + } + + public static class SBTree { + private SBTNode root; + + private SBTNode rightRotate(SBTNode cur) { + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + int leftSize = cur.l != null ? cur.l.size : 0; + int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + int rightSize = cur.r != null ? cur.r.size : 0; + int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + private SBTNode insert(SBTNode root, int index, SBTNode cur) { + if (root == null) { + return cur; + } + root.size++; + int leftAndHeadSize = (root.l != null ? root.l.size : 0) + 1; + if (index < leftAndHeadSize) { + root.l = insert(root.l, index, cur); + } else { + root.r = insert(root.r, index - leftAndHeadSize, cur); + } + root = maintain(root); + return root; + } + + private SBTNode get(SBTNode root, int index) { + int leftSize = root.l != null ? root.l.size : 0; + if (index < leftSize) { + return get(root.l, index); + } else if (index == leftSize) { + return root; + } else { + return get(root.r, index - leftSize - 1); + } + } + + private void process(SBTNode head, LinkedList indexes) { + if (head == null) { + return; + } + process(head.l, indexes); + indexes.addLast(head.value); + process(head.r, indexes); + } + + public void insert(int index, int value) { + SBTNode cur = new SBTNode(value); + if (root == null) { + root = cur; + } else { + if (index <= root.size) { + root = insert(root, index, cur); + } + } + } + + public int get(int index) { + SBTNode ans = get(root, index); + return ans.value; + } + + public LinkedList allIndexes() { + LinkedList indexes = new LinkedList<>(); + process(root, indexes); + return indexes; + } + + } + + // 通过以下这个测试, + // 可以很明显的看到LinkedList的插入和get效率不如SBTree + // LinkedList需要找到index所在的位置之后才能插入或者读取,时间复杂度O(N) + // SBTree是平衡搜索二叉树,所以插入或者读取时间复杂度都是O(logN) + public static void main(String[] args) { + // 功能测试 + int test = 10000; + int max = 1000000; + boolean pass = true; + LinkedList list = new LinkedList<>(); + SBTree sbtree = new SBTree(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + sbtree.insert(randomIndex, randomValue); + } + for (int i = 0; i < test; i++) { + if (list.get(i) != sbtree.get(i)) { + pass = false; + break; + } + } + System.out.println("功能测试是否通过 : " + pass); + + // 性能测试 + test = 50000; + list = new LinkedList<>(); + sbtree = new SBTree(); + long start = 0; + long end = 0; + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("LinkedList插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + list.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("LinkedList读取总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + sbtree.insert(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("SBTree插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + sbtree.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("SBTree读取总时长(毫秒) : " + (end - start)); + + } + +} diff --git a/体系学习班/class37/Compare.java b/体系学习班/class37/Compare.java new file mode 100644 index 0000000..5bbfd0d --- /dev/null +++ b/体系学习班/class37/Compare.java @@ -0,0 +1,408 @@ +package class37; + +import java.util.TreeMap; + +import class35.Code01_AVLTreeMap.AVLTreeMap; +import class36.Code01_SizeBalancedTreeMap.SizeBalancedTreeMap; +import class36.Code02_SkipListMap.SkipListMap; + +// 本文件为avl、sbt、skiplist三种结构的测试文件 +public class Compare { + + public static void functionTest() { + System.out.println("功能测试开始"); + TreeMap treeMap = new TreeMap<>(); + AVLTreeMap avl = new AVLTreeMap<>(); + SizeBalancedTreeMap sbt = new SizeBalancedTreeMap<>(); + SkipListMap skip = new SkipListMap<>(); + int maxK = 500; + int maxV = 50000; + int testTime = 1000000; + for (int i = 0; i < testTime; i++) { + int addK = (int) (Math.random() * maxK); + int addV = (int) (Math.random() * maxV); + treeMap.put(addK, addV); + avl.put(addK, addV); + sbt.put(addK, addV); + skip.put(addK, addV); + + int removeK = (int) (Math.random() * maxK); + treeMap.remove(removeK); + avl.remove(removeK); + sbt.remove(removeK); + skip.remove(removeK); + + int querryK = (int) (Math.random() * maxK); + if (treeMap.containsKey(querryK) != avl.containsKey(querryK) + || sbt.containsKey(querryK) != skip.containsKey(querryK) + || treeMap.containsKey(querryK) != sbt.containsKey(querryK)) { + System.out.println("containsKey Oops"); + System.out.println(treeMap.containsKey(querryK)); + System.out.println(avl.containsKey(querryK)); + System.out.println(sbt.containsKey(querryK)); + System.out.println(skip.containsKey(querryK)); + break; + } + + if (treeMap.containsKey(querryK)) { + int v1 = treeMap.get(querryK); + int v2 = avl.get(querryK); + int v3 = sbt.get(querryK); + int v4 = skip.get(querryK); + if (v1 != v2 || v3 != v4 || v1 != v3) { + System.out.println("get Oops"); + System.out.println(treeMap.get(querryK)); + System.out.println(avl.get(querryK)); + System.out.println(sbt.get(querryK)); + System.out.println(skip.get(querryK)); + break; + } + Integer f1 = treeMap.floorKey(querryK); + Integer f2 = avl.floorKey(querryK); + Integer f3 = sbt.floorKey(querryK); + Integer f4 = skip.floorKey(querryK); + if (f1 == null && (f2 != null || f3 != null || f4 != null)) { + System.out.println("floorKey Oops"); + System.out.println(treeMap.floorKey(querryK)); + System.out.println(avl.floorKey(querryK)); + System.out.println(sbt.floorKey(querryK)); + System.out.println(skip.floorKey(querryK)); + break; + } + if (f1 != null && (f2 == null || f3 == null || f4 == null)) { + System.out.println("floorKey Oops"); + System.out.println(treeMap.floorKey(querryK)); + System.out.println(avl.floorKey(querryK)); + System.out.println(sbt.floorKey(querryK)); + System.out.println(skip.floorKey(querryK)); + break; + } + if (f1 != null) { + int ans1 = f1; + int ans2 = f2; + int ans3 = f3; + int ans4 = f4; + if (ans1 != ans2 || ans3 != ans4 || ans1 != ans3) { + System.out.println("floorKey Oops"); + System.out.println(treeMap.floorKey(querryK)); + System.out.println(avl.floorKey(querryK)); + System.out.println(sbt.floorKey(querryK)); + System.out.println(skip.floorKey(querryK)); + break; + } + } + f1 = treeMap.ceilingKey(querryK); + f2 = avl.ceilingKey(querryK); + f3 = sbt.ceilingKey(querryK); + f4 = skip.ceilingKey(querryK); + if (f1 == null && (f2 != null || f3 != null || f4 != null)) { + System.out.println("ceilingKey Oops"); + System.out.println(treeMap.ceilingKey(querryK)); + System.out.println(avl.ceilingKey(querryK)); + System.out.println(sbt.ceilingKey(querryK)); + System.out.println(skip.ceilingKey(querryK)); + break; + } + if (f1 != null && (f2 == null || f3 == null || f4 == null)) { + System.out.println("ceilingKey Oops"); + System.out.println(treeMap.ceilingKey(querryK)); + System.out.println(avl.ceilingKey(querryK)); + System.out.println(sbt.ceilingKey(querryK)); + System.out.println(skip.ceilingKey(querryK)); + break; + } + if (f1 != null) { + int ans1 = f1; + int ans2 = f2; + int ans3 = f3; + int ans4 = f4; + if (ans1 != ans2 || ans3 != ans4 || ans1 != ans3) { + System.out.println("ceilingKey Oops"); + System.out.println(treeMap.ceilingKey(querryK)); + System.out.println(avl.ceilingKey(querryK)); + System.out.println(sbt.ceilingKey(querryK)); + System.out.println(skip.ceilingKey(querryK)); + break; + } + } + + } + + Integer f1 = treeMap.firstKey(); + Integer f2 = avl.firstKey(); + Integer f3 = sbt.firstKey(); + Integer f4 = skip.firstKey(); + if (f1 == null && (f2 != null || f3 != null || f4 != null)) { + System.out.println("firstKey Oops"); + System.out.println(treeMap.firstKey()); + System.out.println(avl.firstKey()); + System.out.println(sbt.firstKey()); + System.out.println(skip.firstKey()); + break; + } + if (f1 != null && (f2 == null || f3 == null || f4 == null)) { + System.out.println("firstKey Oops"); + System.out.println(treeMap.firstKey()); + System.out.println(avl.firstKey()); + System.out.println(sbt.firstKey()); + System.out.println(skip.firstKey()); + break; + } + if (f1 != null) { + int ans1 = f1; + int ans2 = f2; + int ans3 = f3; + int ans4 = f4; + if (ans1 != ans2 || ans3 != ans4 || ans1 != ans3) { + System.out.println("firstKey Oops"); + System.out.println(treeMap.firstKey()); + System.out.println(avl.firstKey()); + System.out.println(sbt.firstKey()); + System.out.println(skip.firstKey()); + break; + } + } + + f1 = treeMap.lastKey(); + f2 = avl.lastKey(); + f3 = sbt.lastKey(); + f4 = skip.lastKey(); + if (f1 == null && (f2 != null || f3 != null || f4 != null)) { + System.out.println("lastKey Oops"); + System.out.println(treeMap.lastKey()); + System.out.println(avl.lastKey()); + System.out.println(sbt.lastKey()); + System.out.println(skip.lastKey()); + break; + } + if (f1 != null && (f2 == null || f3 == null || f4 == null)) { + System.out.println("firstKey Oops"); + System.out.println(treeMap.lastKey()); + System.out.println(avl.lastKey()); + System.out.println(sbt.lastKey()); + System.out.println(skip.lastKey()); + break; + } + if (f1 != null) { + int ans1 = f1; + int ans2 = f2; + int ans3 = f3; + int ans4 = f4; + if (ans1 != ans2 || ans3 != ans4 || ans1 != ans3) { + System.out.println("lastKey Oops"); + System.out.println(treeMap.lastKey()); + System.out.println(avl.lastKey()); + System.out.println(sbt.lastKey()); + System.out.println(skip.lastKey()); + break; + } + } + if (treeMap.size() != avl.size() || sbt.size() != skip.size() || treeMap.size() != sbt.size()) { + System.out.println("size Oops"); + System.out.println(treeMap.size()); + System.out.println(avl.size()); + System.out.println(sbt.size()); + System.out.println(skip.size()); + break; + } + } + System.out.println("功能测试结束"); + } + + public static void performanceTest() { + System.out.println("性能测试开始"); + TreeMap treeMap; + AVLTreeMap avl; + SizeBalancedTreeMap sbt; + SkipListMap skip; + long start; + long end; + int max = 1000000; + treeMap = new TreeMap<>(); + avl = new AVLTreeMap<>(); + sbt = new SizeBalancedTreeMap<>(); + skip = new SkipListMap<>(); + System.out.println("顺序递增加入测试,数据规模 : " + max); + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + treeMap.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("treeMap 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + avl.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("avl 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + sbt.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("sbt 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + skip.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("skip 运行时间 : " + (end - start) + "ms"); + + System.out.println("顺序递增删除测试,数据规模 : " + max); + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + treeMap.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("treeMap 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + avl.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("avl 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + sbt.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("sbt 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + skip.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("skip 运行时间 : " + (end - start) + "ms"); + + System.out.println("顺序递减加入测试,数据规模 : " + max); + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + treeMap.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("treeMap 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + avl.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("avl 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + sbt.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("sbt 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + skip.put(i, i); + } + end = System.currentTimeMillis(); + System.out.println("skip 运行时间 : " + (end - start) + "ms"); + + System.out.println("顺序递减删除测试,数据规模 : " + max); + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + treeMap.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("treeMap 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + avl.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("avl 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + sbt.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("sbt 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + skip.remove(i); + } + end = System.currentTimeMillis(); + System.out.println("skip 运行时间 : " + (end - start) + "ms"); + + System.out.println("随机加入测试,数据规模 : " + max); + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + treeMap.put((int) (Math.random() * i), i); + } + end = System.currentTimeMillis(); + System.out.println("treeMap 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + avl.put((int) (Math.random() * i), i); + } + end = System.currentTimeMillis(); + System.out.println("avl 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + sbt.put((int) (Math.random() * i), i); + } + end = System.currentTimeMillis(); + System.out.println("sbt 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + skip.put((int) (Math.random() * i), i); + } + end = System.currentTimeMillis(); + System.out.println("skip 运行时间 : " + (end - start) + "ms"); + + System.out.println("随机删除测试,数据规模 : " + max); + start = System.currentTimeMillis(); + for (int i = 0; i < max; i++) { + treeMap.remove((int) (Math.random() * i)); + } + end = System.currentTimeMillis(); + System.out.println("treeMap 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + avl.remove((int) (Math.random() * i)); + } + end = System.currentTimeMillis(); + System.out.println("avl 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + sbt.remove((int) (Math.random() * i)); + } + end = System.currentTimeMillis(); + System.out.println("sbt 运行时间 : " + (end - start) + "ms"); + + start = System.currentTimeMillis(); + for (int i = max; i >= 0; i--) { + skip.remove((int) (Math.random() * i)); + } + end = System.currentTimeMillis(); + System.out.println("skip 运行时间 : " + (end - start) + "ms"); + + System.out.println("性能测试结束"); + } + + public static void main(String[] args) { + functionTest(); + System.out.println("======"); + performanceTest(); + } + +} diff --git a/体系学习班/class38/Code01_AppleMinBags.java b/体系学习班/class38/Code01_AppleMinBags.java new file mode 100644 index 0000000..e1b5f21 --- /dev/null +++ b/体系学习班/class38/Code01_AppleMinBags.java @@ -0,0 +1,41 @@ +package class38; + +public class Code01_AppleMinBags { + + public static int minBags(int apple) { + if (apple < 0) { + return -1; + } + int bag8 = (apple >> 3); + int rest = apple - (bag8 << 3); + while(bag8 >= 0) { + // rest 个 + if(rest % 6 ==0) { + return bag8 + (rest / 6); + } else { + bag8--; + rest += 8; + } + } + return -1; + } + + public static int minBagAwesome(int apple) { + if ((apple & 1) != 0) { // 如果是奇数,返回-1 + return -1; + } + if (apple < 18) { + return apple == 0 ? 0 : (apple == 6 || apple == 8) ? 1 + : (apple == 12 || apple == 14 || apple == 16) ? 2 : -1; + } + return (apple - 18) / 8 + 3; + } + + public static void main(String[] args) { + for(int apple = 1; apple < 200;apple++) { + System.out.println(apple + " : "+ minBags(apple)); + } + + } + +} diff --git a/体系学习班/class38/Code02_EatGrass.java b/体系学习班/class38/Code02_EatGrass.java new file mode 100644 index 0000000..8b400fb --- /dev/null +++ b/体系学习班/class38/Code02_EatGrass.java @@ -0,0 +1,57 @@ +package class38; + +public class Code02_EatGrass { + + // 如果n份草,最终先手赢,返回"先手" + // 如果n份草,最终后手赢,返回"后手" + public static String whoWin(int n) { + if (n < 5) { + return n == 0 || n == 2 ? "后手" : "先手"; + } + // 进到这个过程里来,当前的先手,先选 + int want = 1; + while (want <= n) { + if (whoWin(n - want).equals("后手")) { + return "先手"; + } + if (want <= (n / 4)) { + want *= 4; + } else { + break; + } + } + return "后手"; + } + + public static String winner1(int n) { + if (n < 5) { + return (n == 0 || n == 2) ? "后手" : "先手"; + } + int base = 1; + while (base <= n) { + if (winner1(n - base).equals("后手")) { + return "先手"; + } + if (base > n / 4) { // 防止base*4之后溢出 + break; + } + base *= 4; + } + return "后手"; + } + + public static String winner2(int n) { + if (n % 5 == 0 || n % 5 == 2) { + return "后手"; + } else { + return "先手"; + } + } + + public static void main(String[] args) { + for (int i = 0; i <= 50; i++) { + System.out.println(i + " : " + whoWin(i)); + } + } + +} diff --git a/体系学习班/class38/Code03_MSumToN.java b/体系学习班/class38/Code03_MSumToN.java new file mode 100644 index 0000000..50c72be --- /dev/null +++ b/体系学习班/class38/Code03_MSumToN.java @@ -0,0 +1,44 @@ +package class38; + +public class Code03_MSumToN { + + public static boolean isMSum1(int num) { + for (int start = 1; start <= num; start++) { + int sum = start; + for (int j = start + 1; j <= num; j++) { + if (sum + j > num) { + break; + } + if (sum + j == num) { + return true; + } + sum += j; + } + } + return false; + } + + public static boolean isMSum2(int num) { +// +// return num == (num & (~num + 1)); +// +// return num == (num & (-num)); +// +// + return (num & (num - 1)) != 0; + } + + public static void main(String[] args) { + for (int num = 1; num < 200; num++) { + System.out.println(num + " : " + isMSum1(num)); + } + System.out.println("test begin"); + for (int num = 1; num < 5000; num++) { + if (isMSum1(num) != isMSum2(num)) { + System.out.println("Oops!"); + } + } + System.out.println("test end"); + + } +} diff --git a/体系学习班/class38/Code04_MoneyProblem.java b/体系学习班/class38/Code04_MoneyProblem.java new file mode 100644 index 0000000..722c5a6 --- /dev/null +++ b/体系学习班/class38/Code04_MoneyProblem.java @@ -0,0 +1,169 @@ +package class38; + +public class Code04_MoneyProblem { + + // int[] d d[i]:i号怪兽的武力 + // int[] p p[i]:i号怪兽要求的钱 + // ability 当前你所具有的能力 + // index 来到了第index个怪兽的面前 + + // 目前,你的能力是ability,你来到了index号怪兽的面前,如果要通过后续所有的怪兽, + // 请返回需要花的最少钱数 + public static long process1(int[] d, int[] p, int ability, int index) { + if (index == d.length) { + return 0; + } + if (ability < d[index]) { + return p[index] + process1(d, p, ability + d[index], index + 1); + } else { // ability >= d[index] 可以贿赂,也可以不贿赂 + return Math.min( + + p[index] + process1(d, p, ability + d[index], index + 1), + + 0 + process1(d, p, ability, index + 1)); + } + } + + public static long func1(int[] d, int[] p) { + return process1(d, p, 0, 0); + } + + // 从0....index号怪兽,花的钱,必须严格==money + // 如果通过不了,返回-1 + // 如果可以通过,返回能通过情况下的最大能力值 + public static long process2(int[] d, int[] p, int index, int money) { + if (index == -1) { // 一个怪兽也没遇到呢 + return money == 0 ? 0 : -1; + } + // index >= 0 + // 1) 不贿赂当前index号怪兽 + long preMaxAbility = process2(d, p, index - 1, money); + long p1 = -1; + if (preMaxAbility != -1 && preMaxAbility >= d[index]) { + p1 = preMaxAbility; + } + // 2) 贿赂当前的怪兽 当前的钱 p[index] + long preMaxAbility2 = process2(d, p, index - 1, money - p[index]); + long p2 = -1; + if (preMaxAbility2 != -1) { + p2 = d[index] + preMaxAbility2; + } + return Math.max(p1, p2); + } + + public static int minMoney2(int[] d, int[] p) { + int allMoney = 0; + for (int i = 0; i < p.length; i++) { + allMoney += p[i]; + } + int N = d.length; + for (int money = 0; money < allMoney; money++) { + if (process2(d, p, N - 1, money) != -1) { + return money; + } + } + return allMoney; + } + + public static long func2(int[] d, int[] p) { + int sum = 0; + for (int num : d) { + sum += num; + } + long[][] dp = new long[d.length + 1][sum + 1]; + for (int i = 0; i <= sum; i++) { + dp[0][i] = 0; + } + for (int cur = d.length - 1; cur >= 0; cur--) { + for (int hp = 0; hp <= sum; hp++) { + // 如果这种情况发生,那么这个hp必然是递归过程中不会出现的状态 + // 既然动态规划是尝试过程的优化,尝试过程碰不到的状态,不必计算 + if (hp + d[cur] > sum) { + continue; + } + if (hp < d[cur]) { + dp[cur][hp] = p[cur] + dp[cur + 1][hp + d[cur]]; + } else { + dp[cur][hp] = Math.min(p[cur] + dp[cur + 1][hp + d[cur]], dp[cur + 1][hp]); + } + } + } + return dp[0][0]; + } + + public static long func3(int[] d, int[] p) { + int sum = 0; + for (int num : p) { + sum += num; + } + // dp[i][j]含义: + // 能经过0~i的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少? + // 如果dp[i][j]==-1,表示经过0~i的怪兽,花钱为j是无法通过的,或者之前的钱怎么组合也得不到正好为j的钱数 + int[][] dp = new int[d.length][sum + 1]; + for (int i = 0; i < dp.length; i++) { + for (int j = 0; j <= sum; j++) { + dp[i][j] = -1; + } + } + // 经过0~i的怪兽,花钱数一定为p[0],达到武力值d[0]的地步。其他第0行的状态一律是无效的 + dp[0][p[0]] = d[0]; + for (int i = 1; i < d.length; i++) { + for (int j = 0; j <= sum; j++) { + // 可能性一,为当前怪兽花钱 + // 存在条件: + // j - p[i]要不越界,并且在钱数为j - p[i]时,要能通过0~i-1的怪兽,并且钱数组合是有效的。 + if (j >= p[i] && dp[i - 1][j - p[i]] != -1) { + dp[i][j] = dp[i - 1][j - p[i]] + d[i]; + } + // 可能性二,不为当前怪兽花钱 + // 存在条件: + // 0~i-1怪兽在花钱为j的情况下,能保证通过当前i位置的怪兽 + if (dp[i - 1][j] >= d[i]) { + // 两种可能性中,选武力值最大的 + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]); + } + } + } + int ans = 0; + // dp表最后一行上,dp[N-1][j]代表: + // 能经过0~N-1的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少? + // 那么最后一行上,最左侧的不为-1的列数(j),就是答案 + for (int j = 0; j <= sum; j++) { + if (dp[d.length - 1][j] != -1) { + ans = j; + break; + } + } + return ans; + } + + public static int[][] generateTwoRandomArray(int len, int value) { + int size = (int) (Math.random() * len) + 1; + int[][] arrs = new int[2][size]; + for (int i = 0; i < size; i++) { + arrs[0][i] = (int) (Math.random() * value) + 1; + arrs[1][i] = (int) (Math.random() * value) + 1; + } + return arrs; + } + + public static void main(String[] args) { + int len = 10; + int value = 20; + int testTimes = 10000; + for (int i = 0; i < testTimes; i++) { + int[][] arrs = generateTwoRandomArray(len, value); + int[] d = arrs[0]; + int[] p = arrs[1]; + long ans1 = func1(d, p); + long ans2 = func2(d, p); + long ans3 = func3(d, p); + long ans4 = minMoney2(d,p); + if (ans1 != ans2 || ans2 != ans3 || ans1 != ans4) { + System.out.println("oops!"); + } + } + + } + +} diff --git a/体系学习班/class39/Code01_SubsquenceMaxModM.java b/体系学习班/class39/Code01_SubsquenceMaxModM.java new file mode 100644 index 0000000..9890bf4 --- /dev/null +++ b/体系学习班/class39/Code01_SubsquenceMaxModM.java @@ -0,0 +1,141 @@ +package class39; + +import java.util.HashSet; +import java.util.TreeSet; + +// 给定一个非负数组arr,和一个正数m。 返回arr的所有子序列中累加和%m之后的最大值。 +public class Code01_SubsquenceMaxModM { + + public static int max1(int[] arr, int m) { + HashSet set = new HashSet<>(); + process(arr, 0, 0, set); + int max = 0; + for (Integer sum : set) { + max = Math.max(max, sum % m); + } + return max; + } + + public static void process(int[] arr, int index, int sum, HashSet set) { + if (index == arr.length) { + set.add(sum); + } else { + process(arr, index + 1, sum, set); + process(arr, index + 1, sum + arr[index], set); + } + } + + public static int max2(int[] arr, int m) { + int sum = 0; + int N = arr.length; + for (int i = 0; i < N; i++) { + sum += arr[i]; + } + boolean[][] dp = new boolean[N][sum + 1]; + for (int i = 0; i < N; i++) { + dp[i][0] = true; + } + dp[0][arr[0]] = true; + for (int i = 1; i < N; i++) { + for (int j = 1; j <= sum; j++) { + dp[i][j] = dp[i - 1][j]; + if (j - arr[i] >= 0) { + dp[i][j] |= dp[i - 1][j - arr[i]]; + } + } + } + int ans = 0; + for (int j = 0; j <= sum; j++) { + if (dp[N - 1][j]) { + ans = Math.max(ans, j % m); + } + } + return ans; + } + + public static int max3(int[] arr, int m) { + int N = arr.length; + // 0...m-1 + boolean[][] dp = new boolean[N][m]; + for (int i = 0; i < N; i++) { + dp[i][0] = true; + } + dp[0][arr[0] % m] = true; + for (int i = 1; i < N; i++) { + for (int j = 1; j < m; j++) { + // dp[i][j] T or F + dp[i][j] = dp[i - 1][j]; + int cur = arr[i] % m; + if (cur <= j) { + dp[i][j] |= dp[i - 1][j - cur]; + } else { + dp[i][j] |= dp[i - 1][m + j - cur]; + } + } + } + int ans = 0; + for (int i = 0; i < m; i++) { + if (dp[N - 1][i]) { + ans = i; + } + } + return ans; + } + + // 如果arr的累加和很大,m也很大 + // 但是arr的长度相对不大 + public static int max4(int[] arr, int m) { + if (arr.length == 1) { + return arr[0] % m; + } + int mid = (arr.length - 1) / 2; + TreeSet sortSet1 = new TreeSet<>(); + process4(arr, 0, 0, mid, m, sortSet1); + TreeSet sortSet2 = new TreeSet<>(); + process4(arr, mid + 1, 0, arr.length - 1, m, sortSet2); + int ans = 0; + for (Integer leftMod : sortSet1) { + ans = Math.max(ans, leftMod + sortSet2.floor(m - 1 - leftMod)); + } + return ans; + } + + // 从index出发,最后有边界是end+1,arr[index...end] + public static void process4(int[] arr, int index, int sum, int end, int m, TreeSet sortSet) { + if (index == end + 1) { + sortSet.add(sum % m); + } else { + process4(arr, index + 1, sum, end, m, sortSet); + process4(arr, index + 1, sum + arr[index], end, m, sortSet); + } + } + + public static int[] generateRandomArray(int len, int value) { + int[] ans = new int[(int) (Math.random() * len) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * value); + } + return ans; + } + + public static void main(String[] args) { + int len = 10; + int value = 100; + int m = 76; + int testTime = 500000; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(len, value); + int ans1 = max1(arr, m); + int ans2 = max2(arr, m); + int ans3 = max3(arr, m); + int ans4 = max4(arr, m); + if (ans1 != ans2 || ans2 != ans3 || ans3 != ans4) { + System.out.println("Oops!"); + } + } + System.out.println("test finish!"); + + } + +} diff --git a/体系学习班/class39/Code02_SnacksWays.java b/体系学习班/class39/Code02_SnacksWays.java new file mode 100644 index 0000000..bb2c538 --- /dev/null +++ b/体系学习班/class39/Code02_SnacksWays.java @@ -0,0 +1,79 @@ +package class39; + +public class Code02_SnacksWays { + + public static int ways1(int[] arr, int w) { + // arr[0...] + return process(arr, 0, w); + } + + // 从左往右的经典模型 + // 还剩的容量是rest,arr[index...]自由选择, + // 返回选择方案 + // index : 0~N + // rest : 0~w + public static int process(int[] arr, int index, int rest) { + if (rest < 0) { // 没有容量了 + // -1 无方案的意思 + return -1; + } + // rest>=0, + if (index == arr.length) { // 无零食可选 + return 1; + } + // rest >=0 + // 有零食index + // index号零食,要 or 不要 + // index, rest + // (index+1, rest) + // (index+1, rest-arr[i]) + int next1 = process(arr, index + 1, rest); // 不要 + int next2 = process(arr, index + 1, rest - arr[index]); // 要 + return next1 + (next2 == -1 ? 0 : next2); + } + + public static int ways2(int[] arr, int w) { + int N = arr.length; + int[][] dp = new int[N + 1][w + 1]; + for (int j = 0; j <= w; j++) { + dp[N][j] = 1; + } + for (int i = N - 1; i >= 0; i--) { + for (int j = 0; j <= w; j++) { + dp[i][j] = dp[i + 1][j] + ((j - arr[i] >= 0) ? dp[i + 1][j - arr[i]] : 0); + } + } + return dp[0][w]; + } + + public static int ways3(int[] arr, int w) { + int N = arr.length; + int[][] dp = new int[N][w + 1]; + for (int i = 0; i < N; i++) { + dp[i][0] = 1; + } + if (arr[0] <= w) { + dp[0][arr[0]] = 1; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j <= w; j++) { + dp[i][j] = dp[i - 1][j] + ((j - arr[i]) >= 0 ? dp[i - 1][j - arr[i]] : 0); + } + } + int ans = 0; + for (int j = 0; j <= w; j++) { + ans += dp[N - 1][j]; + } + return ans; + } + + public static void main(String[] args) { + int[] arr = { 4, 3, 2, 9 }; + int w = 8; + System.out.println(ways1(arr, w)); + System.out.println(ways2(arr, w)); + System.out.println(ways3(arr, w)); + + } + +} diff --git a/体系学习班/class39/Code02_SnacksWaysMain1.java b/体系学习班/class39/Code02_SnacksWaysMain1.java new file mode 100644 index 0000000..f282098 --- /dev/null +++ b/体系学习班/class39/Code02_SnacksWaysMain1.java @@ -0,0 +1,126 @@ +// 不要拷贝包信息的内容 +package class39; + +// 课堂版本 +// 本文件是Code02_SnacksWays问题的牛客题目解答 +// 但是用的分治的方法 +// 这是牛客的测试链接: +// https://www.nowcoder.com/questionTerminal/d94bb2fa461d42bcb4c0f2b94f5d4281 +// 把如下的全部代码拷贝进编辑器(java) +// 可以直接通过 +import java.util.Map.Entry; +import java.util.Scanner; +import java.util.TreeMap; + +public class Code02_SnacksWaysMain1 { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int N = sc.nextInt(); + int bag = sc.nextInt(); + int[] arr = new int[N]; + for (int i = 0; i < arr.length; i++) { + arr[i] = sc.nextInt(); + } + long ways = ways(arr, bag); + System.out.println(ways); + sc.close(); + } + + public static long ways(int[] arr, int bag) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0] <= bag ? 2 : 1; + } + int mid = (arr.length - 1) >> 1; + TreeMap lmap = new TreeMap<>(); + long ways = process(arr, 0, 0, mid, bag, lmap); + TreeMap rmap = new TreeMap<>(); + ways += process(arr, mid + 1, 0, arr.length - 1, bag, rmap); + TreeMap rpre = new TreeMap<>(); + long pre = 0; + for (Entry entry : rmap.entrySet()) { + pre += entry.getValue(); + rpre.put(entry.getKey(), pre); + } + for (Entry entry : lmap.entrySet()) { + long lweight = entry.getKey(); + long lways = entry.getValue(); + Long floor = rpre.floorKey(bag - lweight); + if (floor != null) { + long rways = rpre.get(floor); + ways += lways * rways; + } + } + return ways + 1; + } + + + + + // arr 30 + // func(arr, 0, 14, 0, bag, map) + + // func(arr, 15, 29, 0, bag, map) + + // 从index出发,到end结束 + // 之前的选择,已经形成的累加和sum + // 零食[index....end]自由选择,出来的所有累加和,不能超过bag,每一种累加和对应的方法数,填在map里 + // 最后不能什么货都没选 + // [3,3,3,3] bag = 6 + // 0 1 2 3 + // - - - - 0 -> (0 : 1) + // - - - $ 3 -> (0 : 1)(3, 1) + // - - $ - 3 -> (0 : 1)(3, 2) + public static long func(int[] arr, int index, int end, long sum, long bag, TreeMap map) { + if(sum > bag) { + return 0; + } + // sum <= bag + if(index > end) { // 所有商品自由选择完了! + // sum + if(sum != 0) { + if (!map.containsKey(sum)) { + map.put(sum, 1L); + } else { + map.put(sum, map.get(sum) + 1); + } + return 1; + } else { + return 0; + } + } + // sum <= bag 并且 index <= end(还有货) + // 1) 不要当前index位置的货 + long ways = func(arr, index + 1, end, sum, bag, map); + + // 2) 要当前index位置的货 + ways += func(arr, index + 1, end, sum + arr[index], bag, map); + return ways; + } + + public static long process(int[] arr, int index, long w, int end, int bag, TreeMap map) { + if (w > bag) { + return 0; + } + if (index > end) { + if (w != 0) { + if (!map.containsKey(w)) { + map.put(w, 1L); + } else { + map.put(w, map.get(w) + 1); + } + return 1; + } else { + return 0; + } + } else { + long ways = process(arr, index + 1, w, end, bag, map); + ways += process(arr, index + 1, w + arr[index], end, bag, map); + return ways; + } + } + +} \ No newline at end of file diff --git a/体系学习班/class39/Code02_SnacksWaysMain2.java b/体系学习班/class39/Code02_SnacksWaysMain2.java new file mode 100644 index 0000000..0d4bf87 --- /dev/null +++ b/体系学习班/class39/Code02_SnacksWaysMain2.java @@ -0,0 +1,135 @@ +// 不要拷贝包信息的内容 +package class39; + +//优化版本 +import java.util.Arrays; +import java.util.Scanner; + +public class Code02_SnacksWaysMain2 { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + size = sc.nextInt(); + long w = (long) sc.nextInt(); + for (int i = 0; i < size; i++) { + arr[i] = (long) sc.nextInt(); + } + long ways = ways(w); + System.out.println(ways); + } + sc.close(); + } + + // 用来收集所有输入的数字 + public static long[] arr = new long[31]; + public static int size = 0; + // 用来生成左部分可能的所有累加和 + public static long[] leftSum = new long[1 << 16]; + // 准备的数组可能用不完,左部分生成了多少累加和,用leftSize表示 + public static int leftSize = 0; + // 用来生成右部分可能的所有累加和 + public static long[] rightSum = new long[1 << 16]; + // 准备的数组可能用不完,左部分生成了多少累加和,用leftSize表示 + public static int rightSize = 0; + + public static long ways(long w) { + if (size == 0) { + return 0; + } + if (size == 1) { + return arr[0] <= w ? 2 : 1; + } + // 求中点 + int mid = size >> 1; + // 生成左侧的累加和 + leftSize = 0; + dfsLeft(0, mid + 1, 0L); + // 生成右侧的累加和 + rightSize = 0; + dfsRight(mid + 1, size, 0L); + // 把左侧累加和排序 + Arrays.sort(leftSum, 0, leftSize); + // 把右侧累加和排序 + Arrays.sort(rightSum, 0, rightSize); + // 解释一下,接下来的流程。 + // 举个例子,比如: + // 左侧累加和是:{0, 1, 1, 1, 2, 2, 3, 4, 4} + // 右侧累加和是:{0, 1, 2, 3, 3, 3, 4, 4, 5} + // w = 5 + // 左侧严格得到0的方法数:1 + // 右侧得到<=5的方法数(二分求出):9 + // 1 * 9 + // 左侧严格得到1的方法数:3 + // 右侧得到<=4的方法数(二分求出):8 + // 3 * 8 + // 左侧严格得到2的方法数:2 + // 右侧得到<=3的方法数(二分求出):6 + // 2 * 6 + // 左侧严格得到3的方法数:1 + // 右侧得到<=2的方法数(二分求出):3 + // 1 * 3 + // 左侧严格得到4的方法数:2 + // 右侧得到<=1的方法数(二分求出):2 + // 2 * 2 + // 都累加起来 + // 其实和课上讲的一样!多看一下例子 + long ans = 0; + long count = 1; + for (int i = 1; i < leftSize; i++) { + if (leftSum[i] != leftSum[i - 1]) { + ans += count * (long) find(w - leftSum[i - 1]); + count = 1; + } else { + count++; + } + } + ans += count * (long) find(w - leftSum[leftSize - 1]); + return ans; + } + + // 生成左部分的累加和,每一个累加和出来,都记录 + public static void dfsLeft(int cur, int end, long sum) { + if (cur == end) { // 已经终止位置了 + // 记录累加和 + leftSum[leftSize++] = sum; + } else { + // 可能性1,不要当前数 + dfsLeft(cur + 1, end, sum); + // 可能性2,要当前数 + dfsLeft(cur + 1, end, sum + arr[cur]); + } + } + + // 生成右部分的累加和,每一个累加和出来,都记录 + public static void dfsRight(int cur, int end, long sum) { + if (cur == end) { // 已经终止位置了 + // 记录累加和 + rightSum[rightSize++] = sum; + } else { + // 可能性1,不要当前数 + dfsRight(cur + 1, end, sum); + // 可能性2,要当前数 + dfsRight(cur + 1, end, sum + arr[cur]); + } + } + + // <= num的数的个数,返回 + public static int find(long num) { + int ans = -1; + int l = 0; + int r = rightSize - 1; + int m = 0; + while (l <= r) { + m = (l + r) / 2; + if (rightSum[m] <= num) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans + 1; + } + +} \ No newline at end of file diff --git a/体系学习班/class39/Code03_10Ways.java b/体系学习班/class39/Code03_10Ways.java new file mode 100644 index 0000000..1afffb7 --- /dev/null +++ b/体系学习班/class39/Code03_10Ways.java @@ -0,0 +1,92 @@ +package class39; + +import java.util.LinkedList; + +public class Code03_10Ways { + + public static long ways1(int N) { + int zero = N; + int one = N; + LinkedList path = new LinkedList<>(); + LinkedList> ans = new LinkedList<>(); + process(zero, one, path, ans); + long count = 0; + for (LinkedList cur : ans) { + int status = 0; + for (Integer num : cur) { + if (num == 0) { + status++; + } else { + status--; + } + if (status < 0) { + break; + } + } + if (status == 0) { + count++; + } + } + return count; + } + + public static void process(int zero, int one, LinkedList path, LinkedList> ans) { + if (zero == 0 && one == 0) { + LinkedList cur = new LinkedList<>(); + for (Integer num : path) { + cur.add(num); + } + ans.add(cur); + } else { + if (zero == 0) { + path.addLast(1); + process(zero, one - 1, path, ans); + path.removeLast(); + } else if (one == 0) { + path.addLast(0); + process(zero - 1, one, path, ans); + path.removeLast(); + } else { + path.addLast(1); + process(zero, one - 1, path, ans); + path.removeLast(); + path.addLast(0); + process(zero - 1, one, path, ans); + path.removeLast(); + } + } + } + + public static long ways2(int N) { + if (N < 0) { + return 0; + } + if (N < 2) { + return 1; + } + long a = 1; + long b = 1; + long limit = N << 1; + for (long i = 1; i <= limit; i++) { + if (i <= N) { + a *= i; + } else { + b *= i; + } + } + return (b / a) / (N + 1); + } + + public static void main(String[] args) { + System.out.println("test begin"); + for (int i = 0; i < 10; i++) { + long ans1 = ways1(i); + long ans2 = ways2(i); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class39/Code04_DifferentBTNum.java b/体系学习班/class39/Code04_DifferentBTNum.java new file mode 100644 index 0000000..3c48911 --- /dev/null +++ b/体系学习班/class39/Code04_DifferentBTNum.java @@ -0,0 +1,66 @@ +package class39; + +public class Code04_DifferentBTNum { + +// k(0) = 1, k(1) = 1 +// +// k(n) = k(0) * k(n - 1) + k(1) * k(n - 2) + ... + k(n - 2) * k(1) + k(n - 1) * k(0) +// 或者 +// k(n) = c(2n, n) / (n + 1) +// 或者 +// k(n) = c(2n, n) - c(2n, n-1) + + public static long num1(int N) { + if (N < 0) { + return 0; + } + if (N < 2) { + return 1; + } + long[] dp = new long[N + 1]; + dp[0] = 1; + dp[1] = 1; + for (int i = 2; i <= N; i++) { + for (int leftSize = 0; leftSize < i; leftSize++) { + dp[i] += dp[leftSize] * dp[i - 1 - leftSize]; + } + } + return dp[N]; + } + + public static long num2(int N) { + if (N < 0) { + return 0; + } + if (N < 2) { + return 1; + } + long a = 1; + long b = 1; + for (int i = 1, j = N + 1; i <= N; i++, j++) { + a *= i; + b *= j; + long gcd = gcd(a, b); + a /= gcd; + b /= gcd; + } + return (b / a) / (N + 1); + } + + public static long gcd(long m, long n) { + return n == 0 ? m : gcd(n, m % n); + } + + public static void main(String[] args) { + System.out.println("test begin"); + for (int i = 0; i < 15; i++) { + long ans1 = num1(i); + long ans2 = num2(i); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class39/IsSum.java b/体系学习班/class39/IsSum.java new file mode 100644 index 0000000..9ff7a3c --- /dev/null +++ b/体系学习班/class39/IsSum.java @@ -0,0 +1,180 @@ +package class39; + +import java.util.HashMap; +import java.util.HashSet; + +// 这道题是一个小小的补充,课上没有讲 +// 但是如果你听过体系学习班动态规划专题和本节课的话 +// 这道题就是一道水题 +public class IsSum { + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 暴力递归方法 + public static boolean isSum1(int[] arr, int sum) { + if (sum == 0) { + return true; + } + if (arr == null || arr.length == 0) { + return false; + } + return process1(arr, arr.length - 1, sum); + } + + // 可以自由使用arr[0...i]上的数字,能不能累加得到sum + public static boolean process1(int[] arr, int i, int sum) { + if (sum == 0) { + return true; + } + if (i == -1) { + return false; + } + return process1(arr, i - 1, sum) || process1(arr, i - 1, sum - arr[i]); + } + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 记忆化搜索方法 + // 从暴力递归方法来,加了记忆化缓存,就是动态规划了 + public static boolean isSum2(int[] arr, int sum) { + if (sum == 0) { + return true; + } + if (arr == null || arr.length == 0) { + return false; + } + return process2(arr, arr.length - 1, sum, new HashMap<>()); + } + + public static boolean process2(int[] arr, int i, int sum, HashMap> dp) { + if (dp.containsKey(i) && dp.get(i).containsKey(sum)) { + return dp.get(i).get(sum); + } + boolean ans = false; + if (sum == 0) { + ans = true; + } else if (i != -1) { + ans = process2(arr, i - 1, sum, dp) || process2(arr, i - 1, sum - arr[i], dp); + } + if (!dp.containsKey(i)) { + dp.put(i, new HashMap<>()); + } + dp.get(i).put(sum, ans); + return ans; + } + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 经典动态规划 + public static boolean isSum3(int[] arr, int sum) { + if (sum == 0) { + return true; + } + if (arr == null || arr.length == 0) { + return false; + } + int min = 0; + int max = 0; + for (int num : arr) { + min += num < 0 ? num : 0; + max += num > 0 ? num : 0; + } + if (sum < min || sum > max) { + return false; + } + int N = arr.length; + boolean[][] dp = new boolean[N][max - min + 1]; + dp[0][-min] = true; + dp[0][arr[0] - min] = true; + for (int i = 1; i < N; i++) { + for (int j = min; j <= max; j++) { + dp[i][j - min] = dp[i - 1][j - min]; + int next = j - min - arr[i]; + dp[i][j - min] |= (next >= 0 && next <= max - min && dp[i - 1][next]); + } + } + return dp[N - 1][sum - min]; + } + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 分治的方法 + // 如果arr中的数值特别大,动态规划方法依然会很慢 + // 此时如果arr的数字个数不算多(40以内),哪怕其中的数值很大,分治的方法也将是最优解 + public static boolean isSum4(int[] arr, int sum) { + if (sum == 0) { + return true; + } + if (arr == null || arr.length == 0) { + return false; + } + if (arr.length == 1) { + return arr[0] == sum; + } + int N = arr.length; + int mid = N >> 1; + HashSet leftSum = new HashSet<>(); + HashSet rightSum = new HashSet<>(); + process4(arr, 0, mid, 0, leftSum); + process4(arr, mid, N, 0, rightSum); + for (int l : leftSum) { + if (rightSum.contains(sum - l)) { + return true; + } + } + return false; + } + + public static void process4(int[] arr, int i, int end, int pre, HashSet ans) { + if (i == end) { + ans.add(pre); + } else { + process4(arr, i + 1, end, pre, ans); + process4(arr, i + 1, end, pre + arr[i], ans); + } + } + + // 为了测试 + // 生成长度为len的随机数组 + // 值在[-max, max]上随机 + public static int[] randomArray(int len, int max) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * ((max << 1) + 1)) - max; + } + return arr; + } + + // 对数器验证所有方法 + public static void main(String[] args) { + int N = 20; + int M = 100; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * (N + 1)); + int[] arr = randomArray(size, M); + int sum = (int) (Math.random() * ((M << 1) + 1)) - M; + boolean ans1 = isSum1(arr, sum); + boolean ans2 = isSum2(arr, sum); + boolean ans3 = isSum3(arr, sum); + boolean ans4 = isSum4(arr, sum); + if (ans1 ^ ans2 || ans3 ^ ans4 || ans1 ^ ans3) { + System.out.println("出错了!"); + System.out.print("arr : "); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("sum : " + sum); + System.out.println("方法一答案 : " + ans1); + System.out.println("方法二答案 : " + ans2); + System.out.println("方法三答案 : " + ans3); + System.out.println("方法四答案 : " + ans4); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java b/体系学习班/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java new file mode 100644 index 0000000..a2389fa --- /dev/null +++ b/体系学习班/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java @@ -0,0 +1,91 @@ +package class40; + +public class Code01_LongestSumSubArrayLengthInPositiveArray { + + public static int getMaxLength(int[] arr, int K) { + if (arr == null || arr.length == 0 || K <= 0) { + return 0; + } + int left = 0; + int right = 0; + int sum = arr[0]; + int len = 0; + while (right < arr.length) { + if (sum == K) { + len = Math.max(len, right - left + 1); + sum -= arr[left++]; + } else if (sum < K) { + right++; + if (right == arr.length) { + break; + } + sum += arr[right]; + } else { + sum -= arr[left++]; + } + } + return len; + } + + // for test + public static int right(int[] arr, int K) { + int max = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + if (valid(arr, i, j, K)) { + max = Math.max(max, j - i + 1); + } + } + } + return max; + } + + // for test + public static boolean valid(int[] arr, int L, int R, int K) { + int sum = 0; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + return sum == K; + } + + // for test + public static int[] generatePositiveArray(int size, int value) { + int[] ans = new int[size]; + for (int i = 0; i != size; i++) { + ans[i] = (int) (Math.random() * value) + 1; + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 50; + int value = 100; + int testTime = 500000; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generatePositiveArray(len, value); + int K = (int) (Math.random() * value) + 1; + int ans1 = getMaxLength(arr, K); + int ans2 = right(arr, K); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println("K : " + K); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("test end"); + } + +} diff --git a/体系学习班/class40/Code02_LongestSumSubArrayLength.java b/体系学习班/class40/Code02_LongestSumSubArrayLength.java new file mode 100644 index 0000000..a89fb14 --- /dev/null +++ b/体系学习班/class40/Code02_LongestSumSubArrayLength.java @@ -0,0 +1,92 @@ +package class40; + +import java.util.HashMap; + +public class Code02_LongestSumSubArrayLength { + + public static int maxLength(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return 0; + } + // key:前缀和 + // value : 0~value这个前缀和是最早出现key这个值的 + HashMap map = new HashMap(); + map.put(0, -1); // important + int len = 0; + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + if (map.containsKey(sum - k)) { + len = Math.max(i - map.get(sum - k), len); + } + if (!map.containsKey(sum)) { + map.put(sum, i); + } + } + return len; + } + + // for test + public static int right(int[] arr, int K) { + int max = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + if (valid(arr, i, j, K)) { + max = Math.max(max, j - i + 1); + } + } + } + return max; + } + + // for test + public static boolean valid(int[] arr, int L, int R, int K) { + int sum = 0; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + return sum == K; + } + + // for test + public static int[] generateRandomArray(int size, int value) { + int[] ans = new int[(int) (Math.random() * size) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * value) - (int) (Math.random() * value); + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 50; + int value = 100; + int testTime = 500000; + + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(len, value); + int K = (int) (Math.random() * value) - (int) (Math.random() * value); + int ans1 = maxLength(arr, K); + int ans2 = right(arr, K); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println("K : " + K); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("test end"); + + } + +} diff --git a/体系学习班/class40/Code03_LongestLessSumSubArrayLength.java b/体系学习班/class40/Code03_LongestLessSumSubArrayLength.java new file mode 100644 index 0000000..c4ebc56 --- /dev/null +++ b/体系学习班/class40/Code03_LongestLessSumSubArrayLength.java @@ -0,0 +1,103 @@ +package class40; + +public class Code03_LongestLessSumSubArrayLength { + + public static int maxLengthAwesome(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return 0; + } + int[] minSums = new int[arr.length]; + int[] minSumEnds = new int[arr.length]; + minSums[arr.length - 1] = arr[arr.length - 1]; + minSumEnds[arr.length - 1] = arr.length - 1; + for (int i = arr.length - 2; i >= 0; i--) { + if (minSums[i + 1] < 0) { + minSums[i] = arr[i] + minSums[i + 1]; + minSumEnds[i] = minSumEnds[i + 1]; + } else { + minSums[i] = arr[i]; + minSumEnds[i] = i; + } + } + // 迟迟扩不进来那一块儿的开头位置 + int end = 0; + int sum = 0; + int ans = 0; + for (int i = 0; i < arr.length; i++) { + // while循环结束之后: + // 1) 如果以i开头的情况下,累加和<=k的最长子数组是arr[i..end-1],看看这个子数组长度能不能更新res; + // 2) 如果以i开头的情况下,累加和<=k的最长子数组比arr[i..end-1]短,更新还是不更新res都不会影响最终结果; + while (end < arr.length && sum + minSums[end] <= k) { + sum += minSums[end]; + end = minSumEnds[end] + 1; + } + ans = Math.max(ans, end - i); + if (end > i) { // 还有窗口,哪怕窗口没有数字 [i~end) [4,4) + sum -= arr[i]; + } else { // i == end, 即将 i++, i > end, 此时窗口概念维持不住了,所以end跟着i一起走 + end = i + 1; + } + } + return ans; + } + + public static int maxLength(int[] arr, int k) { + int[] h = new int[arr.length + 1]; + int sum = 0; + h[0] = sum; + for (int i = 0; i != arr.length; i++) { + sum += arr[i]; + h[i + 1] = Math.max(sum, h[i]); + } + sum = 0; + int res = 0; + int pre = 0; + int len = 0; + for (int i = 0; i != arr.length; i++) { + sum += arr[i]; + pre = getLessIndex(h, sum - k); + len = pre == -1 ? 0 : i - pre + 1; + res = Math.max(res, len); + } + return res; + } + + public static int getLessIndex(int[] arr, int num) { + int low = 0; + int high = arr.length - 1; + int mid = 0; + int res = -1; + while (low <= high) { + mid = (low + high) / 2; + if (arr[mid] >= num) { + res = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return res; + } + + // for test + public static int[] generateRandomArray(int len, int maxValue) { + int[] res = new int[len]; + for (int i = 0; i != res.length; i++) { + res[i] = (int) (Math.random() * maxValue) - (maxValue / 3); + } + return res; + } + + public static void main(String[] args) { + System.out.println("test begin"); + for (int i = 0; i < 10000000; i++) { + int[] arr = generateRandomArray(10, 20); + int k = (int) (Math.random() * 20) - 5; + if (maxLengthAwesome(arr, k) != maxLength(arr, k)) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/体系学习班/class40/Code04_AvgLessEqualValueLongestSubarray.java b/体系学习班/class40/Code04_AvgLessEqualValueLongestSubarray.java new file mode 100644 index 0000000..c56eef9 --- /dev/null +++ b/体系学习班/class40/Code04_AvgLessEqualValueLongestSubarray.java @@ -0,0 +1,152 @@ +package class40; + +import java.util.TreeMap; + +public class Code04_AvgLessEqualValueLongestSubarray { + + // 暴力解,时间复杂度O(N^3),用于做对数器 + public static int ways1(int[] arr, int v) { + int ans = 0; + for (int L = 0; L < arr.length; L++) { + for (int R = L; R < arr.length; R++) { + int sum = 0; + int k = R - L + 1; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + double avg = (double) sum / (double) k; + if (avg <= v) { + ans = Math.max(ans, k); + } + } + } + return ans; + } + + // 想实现的解法2,时间复杂度O(N*logN) + public static int ways2(int[] arr, int v) { + if (arr == null || arr.length == 0) { + return 0; + } + TreeMap origins = new TreeMap<>(); + int ans = 0; + int modify = 0; + for (int i = 0; i < arr.length; i++) { + int p1 = arr[i] <= v ? 1 : 0; + int p2 = 0; + int querry = -arr[i] - modify; + if (origins.floorKey(querry) != null) { + p2 = i - origins.get(origins.floorKey(querry)) + 1; + } + ans = Math.max(ans, Math.max(p1, p2)); + int curOrigin = -modify - v; + if (origins.floorKey(curOrigin) == null) { + origins.put(curOrigin, i); + } + modify += arr[i] - v; + } + return ans; + } + + // 想实现的解法3,时间复杂度O(N) + public static int ways3(int[] arr, int v) { + if (arr == null || arr.length == 0) { + return 0; + } + for (int i = 0; i < arr.length; i++) { + arr[i] -= v; + } + return maxLengthAwesome(arr, 0); + } + + // 找到数组中累加和<=k的最长子数组 + public static int maxLengthAwesome(int[] arr, int k) { + int N = arr.length; + int[] sums = new int[N]; + int[] ends = new int[N]; + sums[N - 1] = arr[N - 1]; + ends[N - 1] = N - 1; + for (int i = N - 2; i >= 0; i--) { + if (sums[i + 1] < 0) { + sums[i] = arr[i] + sums[i + 1]; + ends[i] = ends[i + 1]; + } else { + sums[i] = arr[i]; + ends[i] = i; + } + } + int end = 0; + int sum = 0; + int res = 0; + for (int i = 0; i < N; i++) { + while (end < N && sum + sums[end] <= k) { + sum += sums[end]; + end = ends[end] + 1; + } + res = Math.max(res, end - i); + if (end > i) { + sum -= arr[i]; + } else { + end = i + 1; + } + } + return res; + } + + // 用于测试 + public static int[] randomArray(int maxLen, int maxValue) { + int len = (int) (Math.random() * maxLen) + 1; + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * maxValue); + } + return ans; + } + + // 用于测试 + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // 用于测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 用于测试 + public static void main(String[] args) { + System.out.println("测试开始"); + int maxLen = 20; + int maxValue = 100; + int testTime = 500000; + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int value = (int) (Math.random() * maxValue); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int[] arr3 = copyArray(arr); + int ans1 = ways1(arr1, value); + int ans2 = ways2(arr2, value); + int ans3 = ways3(arr3, value); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("测试出错!"); + System.out.print("测试数组:"); + printArray(arr); + System.out.println("子数组平均值不小于 :" + value); + System.out.println("方法1得到的最大长度:" + ans1); + System.out.println("方法2得到的最大长度:" + ans2); + System.out.println("方法3得到的最大长度:" + ans3); + System.out.println("========================="); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class40/Code05_PrintMatrixSpiralOrder.java b/体系学习班/class40/Code05_PrintMatrixSpiralOrder.java new file mode 100644 index 0000000..cb3f80c --- /dev/null +++ b/体系学习班/class40/Code05_PrintMatrixSpiralOrder.java @@ -0,0 +1,53 @@ +package class40; + +public class Code05_PrintMatrixSpiralOrder { + + public static void spiralOrderPrint(int[][] matrix) { + int tR = 0; + int tC = 0; + int dR = matrix.length - 1; + int dC = matrix[0].length - 1; + while (tR <= dR && tC <= dC) { + printEdge(matrix, tR++, tC++, dR--, dC--); + } + } + + public static void printEdge(int[][] m, int tR, int tC, int dR, int dC) { + if (tR == dR) { + for (int i = tC; i <= dC; i++) { + System.out.print(m[tR][i] + " "); + } + } else if (tC == dC) { + for (int i = tR; i <= dR; i++) { + System.out.print(m[i][tC] + " "); + } + } else { + int curC = tC; + int curR = tR; + while (curC != dC) { + System.out.print(m[tR][curC] + " "); + curC++; + } + while (curR != dR) { + System.out.print(m[curR][dC] + " "); + curR++; + } + while (curC != tC) { + System.out.print(m[dR][curC] + " "); + curC--; + } + while (curR != tR) { + System.out.print(m[curR][tC] + " "); + curR--; + } + } + } + + public static void main(String[] args) { + int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, + { 13, 14, 15, 16 } }; + spiralOrderPrint(matrix); + + } + +} diff --git a/体系学习班/class40/Code06_RotateMatrix.java b/体系学习班/class40/Code06_RotateMatrix.java new file mode 100644 index 0000000..4e9534f --- /dev/null +++ b/体系学习班/class40/Code06_RotateMatrix.java @@ -0,0 +1,44 @@ +package class40; + +public class Code06_RotateMatrix { + + public static void rotate(int[][] matrix) { + int a = 0; + int b = 0; + int c = matrix.length - 1; + int d = matrix[0].length - 1; + while (a < c) { + rotateEdge(matrix, a++, b++, c--, d--); + } + } + + public static void rotateEdge(int[][] m, int a, int b, int c, int d) { + int tmp = 0; + for (int i = 0; i < d - b; i++) { + tmp = m[a][b + i]; + m[a][b + i] = m[c - i][b]; + m[c - i][b] = m[c][d - i]; + m[c][d - i] = m[a + i][d]; + m[a + i][d] = tmp; + } + } + + public static void printMatrix(int[][] matrix) { + for (int i = 0; i != matrix.length; i++) { + for (int j = 0; j != matrix[0].length; j++) { + System.out.print(matrix[i][j] + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } }; + printMatrix(matrix); + rotate(matrix); + System.out.println("========="); + printMatrix(matrix); + + } + +} diff --git a/体系学习班/class40/Code07_ZigZagPrintMatrix.java b/体系学习班/class40/Code07_ZigZagPrintMatrix.java new file mode 100644 index 0000000..01b9921 --- /dev/null +++ b/体系学习班/class40/Code07_ZigZagPrintMatrix.java @@ -0,0 +1,42 @@ +package class40; + +public class Code07_ZigZagPrintMatrix { + + public static void printMatrixZigZag(int[][] matrix) { + int tR = 0; + int tC = 0; + int dR = 0; + int dC = 0; + int endR = matrix.length - 1; + int endC = matrix[0].length - 1; + boolean fromUp = false; + while (tR != endR + 1) { + printLevel(matrix, tR, tC, dR, dC, fromUp); + tR = tC == endC ? tR + 1 : tR; + tC = tC == endC ? tC : tC + 1; + dC = dR == endR ? dC + 1 : dC; + dR = dR == endR ? dR : dR + 1; + fromUp = !fromUp; + } + System.out.println(); + } + + public static void printLevel(int[][] m, int tR, int tC, int dR, int dC, boolean f) { + if (f) { + while (tR != dR + 1) { + System.out.print(m[tR++][tC--] + " "); + } + } else { + while (dR != tR - 1) { + System.out.print(m[dR--][dC++] + " "); + } + } + } + + public static void main(String[] args) { + int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; + printMatrixZigZag(matrix); + + } + +} diff --git a/体系学习班/class40/Code08_PrintStar.java b/体系学习班/class40/Code08_PrintStar.java new file mode 100644 index 0000000..77d41ef --- /dev/null +++ b/体系学习班/class40/Code08_PrintStar.java @@ -0,0 +1,46 @@ +package class40; + +public class Code08_PrintStar { + + public static void printStar(int N) { + int leftUp = 0; + int rightDown = N - 1; + char[][] m = new char[N][N]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + m[i][j] = ' '; + } + } + while (leftUp <= rightDown) { + set(m, leftUp, rightDown); + leftUp += 2; + rightDown -= 2; + } + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + System.out.print(m[i][j] + " "); + } + System.out.println(); + } + } + + public static void set(char[][] m, int leftUp, int rightDown) { + for (int col = leftUp; col <= rightDown; col++) { + m[leftUp][col] = '*'; + } + for (int row = leftUp + 1; row <= rightDown; row++) { + m[row][rightDown] = '*'; + } + for (int col = rightDown - 1; col > leftUp; col--) { + m[rightDown][col] = '*'; + } + for (int row = rightDown - 1; row > leftUp + 1; row--) { + m[row][leftUp + 1] = '*'; + } + } + + public static void main(String[] args) { + printStar(5); + } + +} diff --git a/体系学习班/class41/Code01_BestSplitForAll.java b/体系学习班/class41/Code01_BestSplitForAll.java new file mode 100644 index 0000000..a4eddb9 --- /dev/null +++ b/体系学习班/class41/Code01_BestSplitForAll.java @@ -0,0 +1,72 @@ +package class41; + +public class Code01_BestSplitForAll { + + public static int bestSplit1(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int ans = 0; + for (int s = 0; s < N - 1; s++) { + int sumL = 0; + for (int L = 0; L <= s; L++) { + sumL += arr[L]; + } + int sumR = 0; + for (int R = s + 1; R < N; R++) { + sumR += arr[R]; + } + ans = Math.max(ans, Math.min(sumL, sumR)); + } + return ans; + } + + public static int bestSplit2(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int sumAll = 0; + for (int num : arr) { + sumAll += num; + } + int ans = 0; + int sumL = 0; + // [0...s] [s+1...N-1] + for (int s = 0; s < N - 1; s++) { + sumL += arr[s]; + int sumR = sumAll - sumL; + ans = Math.max(ans, Math.min(sumL, sumR)); + } + return ans; + } + + public static int[] randomArray(int len, int max) { + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * max); + } + return ans; + } + + public static void main(String[] args) { + int N = 20; + int max = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N); + int[] arr = randomArray(len, max); + int ans1 = bestSplit1(arr); + int ans2 = bestSplit2(arr); + if (ans1 != ans2) { + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } + +} \ No newline at end of file diff --git a/体系学习班/class41/Code02_BestSplitForEveryPosition.java b/体系学习班/class41/Code02_BestSplitForEveryPosition.java new file mode 100644 index 0000000..d98b389 --- /dev/null +++ b/体系学习班/class41/Code02_BestSplitForEveryPosition.java @@ -0,0 +1,130 @@ +package class41; + +public class Code02_BestSplitForEveryPosition { + + public static int[] bestSplit1(int[] arr) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + int N = arr.length; + int[] ans = new int[N]; + ans[0] = 0; + for (int range = 1; range < N; range++) { + for (int s = 0; s < range; s++) { + int sumL = 0; + for (int L = 0; L <= s; L++) { + sumL += arr[L]; + } + int sumR = 0; + for (int R = s + 1; R <= range; R++) { + sumR += arr[R]; + } + ans[range] = Math.max(ans[range], Math.min(sumL, sumR)); + } + } + return ans; + } + + // 求原来的数组arr中,arr[L...R]的累加和 + public static int sum(int[] sum, int L, int R) { + return sum[R + 1] - sum[L]; + } + + public static int[] bestSplit2(int[] arr) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + int N = arr.length; + int[] ans = new int[N]; + ans[0] = 0; + int[] sum = new int[N + 1]; + for (int i = 0; i < N; i++) { + sum[i + 1] = sum[i] + arr[i]; + } + for (int range = 1; range < N; range++) { + for (int s = 0; s < range; s++) { + int sumL = sum(sum, 0, s); + int sumR = sum(sum, s + 1, range); + ans[range] = Math.max(ans[range], Math.min(sumL, sumR)); + } + } + return ans; + } + + public static int[] bestSplit3(int[] arr) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + int N = arr.length; + int[] ans = new int[N]; + ans[0] = 0; + // arr = {5, 3, 1, 3} + // 0 1 2 3 + // sum ={0, 5, 8, 9, 12} + // 0 1 2 3 4 + // 0~2 -> sum[3] - sum[0] + // 1~3 -> sum[4] - sum[1] + int[] sum = new int[N + 1]; + for (int i = 0; i < N; i++) { + sum[i + 1] = sum[i] + arr[i]; + } + // 最优划分 + // 0~range-1上,最优划分是左部分[0~best] 右部分[best+1~range-1] + int best = 0; + for (int range = 1; range < N; range++) { + while (best + 1 < range) { + int before = Math.min(sum(sum, 0, best), sum(sum, best + 1, range)); + int after = Math.min(sum(sum, 0, best + 1), sum(sum, best + 2, range)); + // 注意,一定要是>=,只是>会出错 + // 课上会讲解 + if (after >= before) { + best++; + } else { + break; + } + } + ans[range] = Math.min(sum(sum, 0, best), sum(sum, best + 1, range)); + } + return ans; + } + + public static int[] randomArray(int len, int max) { + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * max); + } + return ans; + } + + public static boolean isSameArray(int[] arr1, int[] arr2) { + if (arr1.length != arr2.length) { + return false; + } + int N = arr1.length; + for (int i = 0; i < N; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + int N = 20; + int max = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N); + int[] arr = randomArray(len, max); + int[] ans1 = bestSplit1(arr); + int[] ans2 = bestSplit2(arr); + int[] ans3 = bestSplit3(arr); + if (!isSameArray(ans1, ans2) || !isSameArray(ans1, ans3)) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class41/Code03_StoneMerge.java b/体系学习班/class41/Code03_StoneMerge.java new file mode 100644 index 0000000..793d7d2 --- /dev/null +++ b/体系学习班/class41/Code03_StoneMerge.java @@ -0,0 +1,117 @@ +package class41; + +// 四边形不等式:合并石子问题 +public class Code03_StoneMerge { + + public static int[] sum(int[] arr) { + int N = arr.length; + int[] s = new int[N + 1]; + s[0] = 0; + for (int i = 0; i < N; i++) { + s[i + 1] = s[i] + arr[i]; + } + return s; + } + + public static int w(int[] s, int l, int r) { + return s[r + 1] - s[l]; + } + + public static int min1(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int[] s = sum(arr); + return process1(0, N - 1, s); + } + + public static int process1(int L, int R, int[] s) { + if (L == R) { + return 0; + } + int next = Integer.MAX_VALUE; + for (int leftEnd = L; leftEnd < R; leftEnd++) { + next = Math.min(next, process1(L, leftEnd, s) + process1(leftEnd + 1, R, s)); + } + return next + w(s, L, R); + } + + public static int min2(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int[] s = sum(arr); + int[][] dp = new int[N][N]; + // dp[i][i] = 0 + for (int L = N - 2; L >= 0; L--) { + for (int R = L + 1; R < N; R++) { + int next = Integer.MAX_VALUE; + // dp(L..leftEnd) + dp[leftEnd+1...R] + 累加和[L...R] + for (int leftEnd = L; leftEnd < R; leftEnd++) { + next = Math.min(next, dp[L][leftEnd] + dp[leftEnd + 1][R]); + } + dp[L][R] = next + w(s, L, R); + } + } + return dp[0][N - 1]; + } + + public static int min3(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int[] s = sum(arr); + int[][] dp = new int[N][N]; + int[][] best = new int[N][N]; + for (int i = 0; i < N - 1; i++) { + best[i][i + 1] = i; + dp[i][i + 1] = w(s, i, i + 1); + } + for (int L = N - 3; L >= 0; L--) { + for (int R = L + 2; R < N; R++) { + int next = Integer.MAX_VALUE; + int choose = -1; + for (int leftEnd = best[L][R - 1]; leftEnd <= best[L + 1][R]; leftEnd++) { + int cur = dp[L][leftEnd] + dp[leftEnd + 1][R]; + if (cur <= next) { + next = cur; + choose = leftEnd; + } + } + best[L][R] = choose; + dp[L][R] = next + w(s, L, R); + } + } + return dp[0][N - 1]; + } + + public static int[] randomArray(int len, int maxValue) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * maxValue); + } + return arr; + } + + public static void main(String[] args) { + int N = 15; + int maxValue = 100; + int testTime = 1000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N); + int[] arr = randomArray(len, maxValue); + int ans1 = min1(arr); + int ans2 = min2(arr); + int ans3 = min3(arr); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class41/Code04_SplitArrayLargestSum.java b/体系学习班/class41/Code04_SplitArrayLargestSum.java new file mode 100644 index 0000000..8d644e6 --- /dev/null +++ b/体系学习班/class41/Code04_SplitArrayLargestSum.java @@ -0,0 +1,193 @@ +package class41; + +// leetcode原题 +// 测试链接:https://leetcode.com/problems/split-array-largest-sum/ +public class Code04_SplitArrayLargestSum { + + // 求原数组arr[L...R]的累加和 + public static int sum(int[] sum, int L, int R) { + return sum[R + 1] - sum[L]; + } + + // 不优化枚举的动态规划方法,O(N^2 * K) + public static int splitArray1(int[] nums, int K) { + int N = nums.length; + int[] sum = new int[N + 1]; + for (int i = 0; i < N; i++) { + sum[i + 1] = sum[i] + nums[i]; + } + int[][] dp = new int[N][K + 1]; + for (int j = 1; j <= K; j++) { + dp[0][j] = nums[0]; + } + for (int i = 1; i < N; i++) { + dp[i][1] = sum(sum, 0, i); + } + // 每一行从上往下 + // 每一列从左往右 + // 根本不去凑优化位置对儿! + for (int i = 1; i < N; i++) { + for (int j = 2; j <= K; j++) { + int ans = Integer.MAX_VALUE; + // 枚举是完全不优化的! + for (int leftEnd = 0; leftEnd <= i; leftEnd++) { + int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1]; + int rightCost = leftEnd == i ? 0 : sum(sum, leftEnd + 1, i); + int cur = Math.max(leftCost, rightCost); + if (cur < ans) { + ans = cur; + } + } + dp[i][j] = ans; + } + } + return dp[N - 1][K]; + } + + // 课上现场写的方法,用了枚举优化,O(N * K) + public static int splitArray2(int[] nums, int K) { + int N = nums.length; + int[] sum = new int[N + 1]; + for (int i = 0; i < N; i++) { + sum[i + 1] = sum[i] + nums[i]; + } + int[][] dp = new int[N][K + 1]; + int[][] best = new int[N][K + 1]; + for (int j = 1; j <= K; j++) { + dp[0][j] = nums[0]; + best[0][j] = -1; + } + for (int i = 1; i < N; i++) { + dp[i][1] = sum(sum, 0, i); + best[i][1] = -1; + } + // 从第2列开始,从左往右 + // 每一列,从下往上 + // 为什么这样的顺序?因为要去凑(左,下)优化位置对儿! + for (int j = 2; j <= K; j++) { + for (int i = N - 1; i >= 1; i--) { + int down = best[i][j - 1]; + // 如果i==N-1,则不优化上限 + int up = i == N - 1 ? N - 1 : best[i + 1][j]; + int ans = Integer.MAX_VALUE; + int bestChoose = -1; + for (int leftEnd = down; leftEnd <= up; leftEnd++) { + int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1]; + int rightCost = leftEnd == i ? 0 : sum(sum, leftEnd + 1, i); + int cur = Math.max(leftCost, rightCost); + // 注意下面的if一定是 < 课上的错误就是此处!当时写的 <= ! + // 也就是说,只有取得明显的好处才移动! + // 举个例子来说明,比如[2,6,4,4],3个画匠时候,如下两种方案都是最优: + // (2,6) (4) 两个画匠负责 | (4) 最后一个画匠负责 + // (2,6) (4,4)两个画匠负责 | 最后一个画匠什么也不负责 + // 第一种方案划分为,[0~2] [3~3] + // 第二种方案划分为,[0~3] [无] + // 两种方案的答案都是8,但是划分点位置一定不要移动! + // 只有明显取得好处时(<),划分点位置才移动! + // 也就是说后面的方案如果==前面的最优,不要移动!只有优于前面的最优,才移动 + // 比如上面的两个方案,如果你移动到了方案二,你会得到: + // [2,6,4,4] 三个画匠时,最优为[0~3](前两个画家) [无](最后一个画家), + // 最优划分点为3位置(best[3][3]) + // 那么当4个画匠时,也就是求解dp[3][4]时 + // 因为best[3][3] = 3,这个值提供了dp[3][4]的下限 + // 而事实上dp[3][4]的最优划分为: + // [0~2](三个画家处理) [3~3] (一个画家处理),此时最优解为6 + // 所以,你就得不到dp[3][4]的最优解了,因为划分点已经越过2了 + // 提供了对数器验证,你可以改成<=,对数器和leetcode都过不了 + // 这里是<,对数器和leetcode都能通过 + // 这里面会让同学们感到困惑的点: + // 为啥==的时候,不移动,只有<的时候,才移动呢?例子懂了,但是道理何在? + // 哈哈哈哈哈,看了邮局选址问题,你更懵,请看42节! + if (cur < ans) { + ans = cur; + bestChoose = leftEnd; + } + } + dp[i][j] = ans; + best[i][j] = bestChoose; + } + } + return dp[N - 1][K]; + } + + public static int splitArray3(int[] nums, int M) { + long sum = 0; + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + } + long l = 0; + long r = sum; + long ans = 0; + while (l <= r) { + long mid = (l + r) / 2; + long cur = getNeedParts(nums, mid); + if (cur <= M) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return (int) ans; + } + + public static int getNeedParts(int[] arr, long aim) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] > aim) { + return Integer.MAX_VALUE; + } + } + int parts = 1; + int all = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (all + arr[i] > aim) { + parts++; + all = arr[i]; + } else { + all += arr[i]; + } + } + return parts; + } + + public static int[] randomArray(int len, int maxValue) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * maxValue); + } + return arr; + } + + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int N = 100; + int maxValue = 100; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N) + 1; + int M = (int) (Math.random() * N) + 1; + int[] arr = randomArray(len, maxValue); + int ans1 = splitArray1(arr, M); + int ans2 = splitArray2(arr, M); + int ans3 = splitArray3(arr, M); + if (ans1 != ans2 || ans1 != ans3) { + System.out.print("arr : "); + printArray(arr); + System.out.println("M : " + M); + System.out.println("ans1 : " + ans1); + System.out.println("ans2 : " + ans2); + System.out.println("ans3 : " + ans3); + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } +} diff --git a/体系学习班/class42/Code01_PostOfficeProblem.java b/体系学习班/class42/Code01_PostOfficeProblem.java new file mode 100644 index 0000000..c6fa893 --- /dev/null +++ b/体系学习班/class42/Code01_PostOfficeProblem.java @@ -0,0 +1,115 @@ +package class42; + +import java.util.Arrays; + +public class Code01_PostOfficeProblem { + + public static int min1(int[] arr, int num) { + if (arr == null || num < 1 || arr.length < num) { + return 0; + } + int N = arr.length; + int[][] w = new int[N + 1][N + 1]; + for (int L = 0; L < N; L++) { + for (int R = L + 1; R < N; R++) { + w[L][R] = w[L][R - 1] + arr[R] - arr[(L + R) >> 1]; + } + } + int[][] dp = new int[N][num + 1]; + for (int i = 0; i < N; i++) { + dp[i][1] = w[0][i]; + } + for (int i = 1; i < N; i++) { + for (int j = 2; j <= Math.min(i, num); j++) { + int ans = Integer.MAX_VALUE; + for (int k = 0; k <= i; k++) { + ans = Math.min(ans, dp[k][j - 1] + w[k + 1][i]); + } + dp[i][j] = ans; + } + } + return dp[N - 1][num]; + } + + public static int min2(int[] arr, int num) { + if (arr == null || num < 1 || arr.length < num) { + return 0; + } + int N = arr.length; + int[][] w = new int[N + 1][N + 1]; + for (int L = 0; L < N; L++) { + for (int R = L + 1; R < N; R++) { + w[L][R] = w[L][R - 1] + arr[R] - arr[(L + R) >> 1]; + } + } + int[][] dp = new int[N][num + 1]; + int[][] best = new int[N][num + 1]; + for (int i = 0; i < N; i++) { + dp[i][1] = w[0][i]; + best[i][1] = -1; + } + for (int j = 2; j <= num; j++) { + for (int i = N - 1; i >= j; i--) { + int down = best[i][j - 1]; + int up = i == N - 1 ? N - 1 : best[i + 1][j]; + int ans = Integer.MAX_VALUE; + int bestChoose = -1; + for (int leftEnd = down; leftEnd <= up; leftEnd++) { + int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1]; + int rightCost = leftEnd == i ? 0 : w[leftEnd + 1][i]; + int cur = leftCost + rightCost; + if (cur <= ans) { + ans = cur; + bestChoose = leftEnd; + } + } + dp[i][j] = ans; + best[i][j] = bestChoose; + } + } + return dp[N - 1][num]; + } + + // for test + public static int[] randomSortedArray(int len, int range) { + int[] arr = new int[len]; + for (int i = 0; i != len; i++) { + arr[i] = (int) (Math.random() * range); + } + Arrays.sort(arr); + return arr; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int N = 30; + int maxValue = 100; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N) + 1; + int[] arr = randomSortedArray(len, maxValue); + int num = (int) (Math.random() * N) + 1; + int ans1 = min1(arr, num); + int ans2 = min2(arr, num); + if (ans1 != ans2) { + printArray(arr); + System.out.println(num); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + + } + +} diff --git a/体系学习班/class42/Code02_ThrowChessPiecesProblem.java b/体系学习班/class42/Code02_ThrowChessPiecesProblem.java new file mode 100644 index 0000000..d996c70 --- /dev/null +++ b/体系学习班/class42/Code02_ThrowChessPiecesProblem.java @@ -0,0 +1,166 @@ +package class42; + +// leetcode测试链接:https://leetcode.com/problems/super-egg-drop +// 方法1和方法2会超时 +// 方法3勉强通过 +// 方法4打败100% +// 方法5打败100%,方法5是在方法4的基础上做了进一步的常数优化 +public class Code02_ThrowChessPiecesProblem { + + public static int superEggDrop1(int kChess, int nLevel) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + return Process1(nLevel, kChess); + } + + // rest还剩多少层楼需要去验证 + // k还有多少颗棋子能够使用 + // 一定要验证出最高的不会碎的楼层!但是每次都是坏运气。 + // 返回至少需要扔几次? + public static int Process1(int rest, int k) { + if (rest == 0) { + return 0; + } + if (k == 1) { + return rest; + } + int min = Integer.MAX_VALUE; + for (int i = 1; i != rest + 1; i++) { // 第一次扔的时候,仍在了i层 + min = Math.min(min, Math.max(Process1(i - 1, k - 1), Process1(rest - i, k))); + } + return min + 1; + } + + public static int superEggDrop2(int kChess, int nLevel) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + if (kChess == 1) { + return nLevel; + } + int[][] dp = new int[nLevel + 1][kChess + 1]; + for (int i = 1; i != dp.length; i++) { + dp[i][1] = i; + } + for (int i = 1; i != dp.length; i++) { + for (int j = 2; j != dp[0].length; j++) { + int min = Integer.MAX_VALUE; + for (int k = 1; k != i + 1; k++) { + min = Math.min(min, Math.max(dp[k - 1][j - 1], dp[i - k][j])); + } + dp[i][j] = min + 1; + } + } + return dp[nLevel][kChess]; + } + + public static int superEggDrop3(int kChess, int nLevel) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + if (kChess == 1) { + return nLevel; + } + int[][] dp = new int[nLevel + 1][kChess + 1]; + for (int i = 1; i != dp.length; i++) { + dp[i][1] = i; + } + int[][] best = new int[nLevel + 1][kChess + 1]; + for (int i = 1; i != dp[0].length; i++) { + dp[1][i] = 1; + best[1][i] = 1; + } + for (int i = 2; i < nLevel + 1; i++) { + for (int j = kChess; j > 1; j--) { + int ans = Integer.MAX_VALUE; + int bestChoose = -1; + int down = best[i - 1][j]; + int up = j == kChess ? i : best[i][j + 1]; + for (int first = down; first <= up; first++) { + int cur = Math.max(dp[first - 1][j - 1], dp[i - first][j]); + if (cur <= ans) { + ans = cur; + bestChoose = first; + } + } + dp[i][j] = ans + 1; + best[i][j] = bestChoose; + } + } + return dp[nLevel][kChess]; + } + + public static int superEggDrop4(int kChess, int nLevel) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + int[] dp = new int[kChess]; + int res = 0; + while (true) { + res++; + int previous = 0; + for (int i = 0; i < dp.length; i++) { + int tmp = dp[i]; + dp[i] = dp[i] + previous + 1; + previous = tmp; + if (dp[i] >= nLevel) { + return res; + } + } + } + } + + public static int superEggDrop5(int kChess, int nLevel) { + if (nLevel < 1 || kChess < 1) { + return 0; + } + int bsTimes = log2N(nLevel) + 1; + if (kChess >= bsTimes) { + return bsTimes; + } + int[] dp = new int[kChess]; + int res = 0; + while (true) { + res++; + int previous = 0; + for (int i = 0; i < dp.length; i++) { + int tmp = dp[i]; + dp[i] = dp[i] + previous + 1; + previous = tmp; + if (dp[i] >= nLevel) { + return res; + } + } + } + } + + public static int log2N(int n) { + int res = -1; + while (n != 0) { + res++; + n >>>= 1; + } + return res; + } + + public static void main(String[] args) { + int maxN = 500; + int maxK = 30; + int testTime = 1000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * maxN) + 1; + int K = (int) (Math.random() * maxK) + 1; + int ans2 = superEggDrop2(K, N); + int ans3 = superEggDrop3(K, N); + int ans4 = superEggDrop4(K, N); + int ans5 = superEggDrop5(K, N); + if (ans2 != ans3 || ans4 != ans5 || ans2 != ans4) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class43/Code01_CanIWin.java b/体系学习班/class43/Code01_CanIWin.java new file mode 100644 index 0000000..608b1f1 --- /dev/null +++ b/体系学习班/class43/Code01_CanIWin.java @@ -0,0 +1,116 @@ +package class43; + +// leetcode 464题 +public class Code01_CanIWin { + + // 1~choose 拥有的数字 + // total 一开始的剩余 + // 返回先手会不会赢 + public static boolean canIWin0(int choose, int total) { + if (total == 0) { + return true; + } + if ((choose * (choose + 1) >> 1) < total) { + return false; + } + int[] arr = new int[choose]; + for (int i = 0; i < choose; i++) { + arr[i] = i + 1; + } + // arr[i] != -1 表示arr[i]这个数字还没被拿走 + // arr[i] == -1 表示arr[i]这个数字已经被拿走 + // 集合,arr,1~choose + return process(arr, total); + } + + // 当前轮到先手拿, + // 先手只能选择在arr中还存在的数字, + // 还剩rest这么值, + // 返回先手会不会赢 + public static boolean process(int[] arr, int rest) { + if (rest <= 0) { + return false; + } + // 先手去尝试所有的情况 + for (int i = 0; i < arr.length; i++) { + if (arr[i] != -1) { + int cur = arr[i]; + arr[i] = -1; + boolean next = process(arr, rest - cur); + arr[i] = cur; + if (!next) { + return true; + } + } + } + return false; + } + + // 这个是暴力尝试,思路是正确的,超时而已 + public static boolean canIWin1(int choose, int total) { + if (total == 0) { + return true; + } + if ((choose * (choose + 1) >> 1) < total) { + return false; + } + return process1(choose, 0, total); + } + + // 当前轮到先手拿, + // 先手可以拿1~choose中的任何一个数字 + // status i位如果为0,代表没拿,当前可以拿 + // i位为1,代表已经拿过了,当前不能拿 + // 还剩rest这么值, + // 返回先手会不会赢 + public static boolean process1(int choose, int status, int rest) { + if (rest <= 0) { + return false; + } + for (int i = 1; i <= choose; i++) { + if (((1 << i) & status) == 0) { // i 这个数字,是此时先手的决定! + if (!process1(choose, (status | (1 << i)), rest - i)) { + return true; + } + } + } + return false; + } + + // 暴力尝试改动态规划而已 + public static boolean canIWin2(int choose, int total) { + if (total == 0) { + return true; + } + if ((choose * (choose + 1) >> 1) < total) { + return false; + } + int[] dp = new int[1 << (choose + 1)]; + // dp[status] == 1 true + // dp[status] == -1 false + // dp[status] == 0 process(status) 没算过!去算! + return process2(choose, 0, total, dp); + } + + // 为什么明明status和rest是两个可变参数,却只用status来代表状态(也就是dp) + // 因为选了一批数字之后,得到的和一定是一样的,所以rest是由status决定的,所以rest不需要参与记忆化搜索 + public static boolean process2(int choose, int status, int rest, int[] dp) { + if (dp[status] != 0) { + return dp[status] == 1 ? true : false; + } + boolean ans = false; + if (rest > 0) { + for (int i = 1; i <= choose; i++) { + if (((1 << i) & status) == 0) { + if (!process2(choose, (status | (1 << i)), rest - i, dp)) { + ans = true; + break; + } + } + } + } + dp[status] = ans ? 1 : -1; + return ans; + } + +} diff --git a/体系学习班/class43/Code02_TSP.java b/体系学习班/class43/Code02_TSP.java new file mode 100644 index 0000000..bf984a6 --- /dev/null +++ b/体系学习班/class43/Code02_TSP.java @@ -0,0 +1,295 @@ +package class43; + +import java.util.ArrayList; +import java.util.List; + +public class Code02_TSP { + + public static int t1(int[][] matrix) { + int N = matrix.length; // 0...N-1 + // set + // set.get(i) != null i这座城市在集合里 + // set.get(i) == null i这座城市不在集合里 + List set = new ArrayList<>(); + for (int i = 0; i < N; i++) { + set.add(1); + } + return func1(matrix, set, 0); + } + + // 任何两座城市之间的距离,可以在matrix里面拿到 + // set中表示着哪些城市的集合, + // start这座城一定在set里, + // 从start出发,要把set中所有的城市过一遍,最终回到0这座城市,最小距离是多少 + public static int func1(int[][] matrix, List set, int start) { + int cityNum = 0; + for (int i = 0; i < set.size(); i++) { + if (set.get(i) != null) { + cityNum++; + } + } + if (cityNum == 1) { + return matrix[start][0]; + } + // cityNum > 1 不只start这一座城 + set.set(start, null); + int min = Integer.MAX_VALUE; + for (int i = 0; i < set.size(); i++) { + if (set.get(i) != null) { + // start -> i i... -> 0 + int cur = matrix[start][i] + func1(matrix, set, i); + min = Math.min(min, cur); + } + } + set.set(start, 1); + return min; + } + + public static int t2(int[][] matrix) { + int N = matrix.length; // 0...N-1 + // 7座城 1111111 + int allCity = (1 << N) - 1; + return f2(matrix, allCity, 0); + } + + // 任何两座城市之间的距离,可以在matrix里面拿到 + // set中表示着哪些城市的集合, + // start这座城一定在set里, + // 从start出发,要把set中所有的城市过一遍,最终回到0这座城市,最小距离是多少 + public static int f2(int[][] matrix, int cityStatus, int start) { + // cityStatus == cityStatux & (~cityStaus + 1) + + if (cityStatus == (cityStatus & (~cityStatus + 1))) { + return matrix[start][0]; + } + + // 把start位的1去掉, + cityStatus &= (~(1 << start)); + int min = Integer.MAX_VALUE; + // 枚举所有的城市 + for (int move = 0; move < matrix.length; move++) { + if ((cityStatus & (1 << move)) != 0) { + int cur = matrix[start][move] + f2(matrix, cityStatus, move); + min = Math.min(min, cur); + } + } + cityStatus |= (1 << start); + return min; + } + + public static int t3(int[][] matrix) { + int N = matrix.length; // 0...N-1 + // 7座城 1111111 + int allCity = (1 << N) - 1; + int[][] dp = new int[1 << N][N]; + for (int i = 0; i < (1 << N); i++) { + for (int j = 0; j < N; j++) { + dp[i][j] = -1; + } + } + return f3(matrix, allCity, 0, dp); + } + + // 任何两座城市之间的距离,可以在matrix里面拿到 + // set中表示着哪些城市的集合, + // start这座城一定在set里, + // 从start出发,要把set中所有的城市过一遍,最终回到0这座城市,最小距离是多少 + public static int f3(int[][] matrix, int cityStatus, int start, int[][] dp) { + if (dp[cityStatus][start] != -1) { + return dp[cityStatus][start]; + } + if (cityStatus == (cityStatus & (~cityStatus + 1))) { + dp[cityStatus][start] = matrix[start][0]; + } else { + // 把start位的1去掉, + cityStatus &= (~(1 << start)); + int min = Integer.MAX_VALUE; + // 枚举所有的城市 + for (int move = 0; move < matrix.length; move++) { + if (move != start && (cityStatus & (1 << move)) != 0) { + int cur = matrix[start][move] + f3(matrix, cityStatus, move, dp); + min = Math.min(min, cur); + } + } + cityStatus |= (1 << start); + dp[cityStatus][start] = min; + } + return dp[cityStatus][start]; + } + + public static int t4(int[][] matrix) { + int N = matrix.length; // 0...N-1 + int statusNums = 1 << N; + int[][] dp = new int[statusNums][N]; + + for (int status = 0; status < statusNums; status++) { + for (int start = 0; start < N; start++) { + if ((status & (1 << start)) != 0) { + if (status == (status & (~status + 1))) { + dp[status][start] = matrix[start][0]; + } else { + int min = Integer.MAX_VALUE; + // start 城市在status里去掉之后,的状态 + int preStatus = status & (~(1 << start)); + // start -> i + for (int i = 0; i < N; i++) { + if ((preStatus & (1 << i)) != 0) { + int cur = matrix[start][i] + dp[preStatus][i]; + min = Math.min(min, cur); + } + } + dp[status][start] = min; + } + } + } + } + return dp[statusNums - 1][0]; + } + + // matrix[i][j] -> i城市到j城市的距离 + public static int tsp1(int[][] matrix, int origin) { + if (matrix == null || matrix.length < 2 || origin < 0 || origin >= matrix.length) { + return 0; + } + // 要考虑的集合 + ArrayList cities = new ArrayList<>(); + // cities[0] != null 表示0城在集合里 + // cities[i] != null 表示i城在集合里 + for (int i = 0; i < matrix.length; i++) { + cities.add(1); + } + // null,1,1,1,1,1,1 + // origin城不参与集合 + cities.set(origin, null); + return process(matrix, origin, cities, origin); + } + + // matrix 所有距离,存在其中 + // origin 固定参数,唯一的目标 + // cities 要考虑的集合,一定不含有origin + // 当前来到的城市是谁,cur + public static int process(int[][] matrix, int aim, ArrayList cities, int cur) { + boolean hasCity = false; // 集团中还是否有城市 + int ans = Integer.MAX_VALUE; + for (int i = 0; i < cities.size(); i++) { + if (cities.get(i) != null) { + hasCity = true; + cities.set(i, null); + // matrix[cur][i] + f(i, 集团(去掉i) ) + ans = Math.min(ans, matrix[cur][i] + process(matrix, aim, cities, i)); + cities.set(i, 1); + } + } + return hasCity ? ans : matrix[cur][aim]; + } + + // cities 里,一定含有cur这座城 + // 解决的是,集合从cur出发,通过集合里所有的城市,最终来到aim,最短距离 + public static int process2(int[][] matrix, int aim, ArrayList cities, int cur) { + if (cities.size() == 1) { + return matrix[cur][aim]; + } + cities.set(cur, null); + int ans = Integer.MAX_VALUE; + for (int i = 0; i < cities.size(); i++) { + if (cities.get(i) != null) { + int dis = matrix[cur][i] + process2(matrix, aim, cities, i); + ans = Math.min(ans, dis); + } + } + cities.set(cur, 1); + return ans; + } + + public static int tsp2(int[][] matrix, int origin) { + if (matrix == null || matrix.length < 2 || origin < 0 || origin >= matrix.length) { + return 0; + } + int N = matrix.length - 1; // 除去origin之后是n-1个点 + int S = 1 << N; // 状态数量 + int[][] dp = new int[S][N]; + int icity = 0; + int kcity = 0; + for (int i = 0; i < N; i++) { + icity = i < origin ? i : i + 1; + // 00000000 i + dp[0][i] = matrix[icity][origin]; + } + for (int status = 1; status < S; status++) { + // 尝试每一种状态 status = 0 0 1 0 0 0 0 0 0 + // 下标 8 7 6 5 4 3 2 1 0 + for (int i = 0; i < N; i++) { + // i 枚举的出发城市 + dp[status][i] = Integer.MAX_VALUE; + if ((1 << i & status) != 0) { + // 如果i这座城是可以枚举的,i = 6 , i对应的原始城的编号,icity + icity = i < origin ? i : i + 1; + for (int k = 0; k < N; k++) { // i 这一步连到的点,k + if ((1 << k & status) != 0) { // i 这一步可以连到k + kcity = k < origin ? k : k + 1; // k对应的原始城的编号,kcity + dp[status][i] = Math.min(dp[status][i], dp[status ^ (1 << i)][k] + matrix[icity][kcity]); + } + } + } + } + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + icity = i < origin ? i : i + 1; + ans = Math.min(ans, dp[S - 1][i] + matrix[origin][icity]); + } + return ans; + } + + public static int[][] generateGraph(int maxSize, int maxValue) { + int len = (int) (Math.random() * maxSize) + 1; + int[][] matrix = new int[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + matrix[i][j] = (int) (Math.random() * maxValue) + 1; + } + } + for (int i = 0; i < len; i++) { + matrix[i][i] = 0; + } + return matrix; + } + + public static void main(String[] args) { + int len = 10; + int value = 100; + System.out.println("功能测试开始"); + for (int i = 0; i < 20000; i++) { + int[][] matrix = generateGraph(len, value); + int origin = (int) (Math.random() * matrix.length); + int ans1 = t3(matrix); + int ans2 = t4(matrix); + int ans3 = tsp2(matrix, origin); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("fuck"); + } + } + System.out.println("功能测试结束"); + + len = 22; + System.out.println("性能测试开始,数据规模 : " + len); + int[][] matrix = new int[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + matrix[i][j] = (int) (Math.random() * value) + 1; + } + } + for (int i = 0; i < len; i++) { + matrix[i][i] = 0; + } + long start; + long end; + start = System.currentTimeMillis(); + t4(matrix); + end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + System.out.println("性能测试结束"); + + } + +} diff --git a/体系学习班/class43/Code03_PavingTile.java b/体系学习班/class43/Code03_PavingTile.java new file mode 100644 index 0000000..c4bf363 --- /dev/null +++ b/体系学习班/class43/Code03_PavingTile.java @@ -0,0 +1,215 @@ +package class43; + +public class Code03_PavingTile { + + /* + * 2*M铺地的问题非常简单,这个是解决N*M铺地的问题 + */ + + public static int ways1(int N, int M) { + if (N < 1 || M < 1 || ((N * M) & 1) != 0) { + return 0; + } + if (N == 1 || M == 1) { + return 1; + } + int[] pre = new int[M]; // pre代表-1行的状况 + for (int i = 0; i < pre.length; i++) { + pre[i] = 1; + } + return process(pre, 0, N); + } + + // pre 表示level-1行的状态 + // level表示,正在level行做决定 + // N 表示一共有多少行 固定的 + // level-2行及其之上所有行,都摆满砖了 + // level做决定,让所有区域都满,方法数返回 + public static int process(int[] pre, int level, int N) { + if (level == N) { // base case + for (int i = 0; i < pre.length; i++) { + if (pre[i] == 0) { + return 0; + } + } + return 1; + } + + // 没到终止行,可以选择在当前的level行摆瓷砖 + int[] op = getOp(pre); + return dfs(op, 0, level, N); + } + + // op[i] == 0 可以考虑摆砖 + // op[i] == 1 只能竖着向上 + public static int dfs(int[] op, int col, int level, int N) { + // 在列上自由发挥,玩深度优先遍历,当col来到终止列,i行的决定做完了 + // 轮到i+1行,做决定 + if (col == op.length) { + return process(op, level + 1, N); + } + int ans = 0; + // col位置不横摆 + ans += dfs(op, col + 1, level, N); // col位置上不摆横转 + // col位置横摆, 向右 + if (col + 1 < op.length && op[col] == 0 && op[col + 1] == 0) { + op[col] = 1; + op[col + 1] = 1; + ans += dfs(op, col + 2, level, N); + op[col] = 0; + op[col + 1] = 0; + } + return ans; + } + + public static int[] getOp(int[] pre) { + int[] cur = new int[pre.length]; + for (int i = 0; i < pre.length; i++) { + cur[i] = pre[i] ^ 1; + } + return cur; + } + + // Min (N,M) 不超过 32 + public static int ways2(int N, int M) { + if (N < 1 || M < 1 || ((N * M) & 1) != 0) { + return 0; + } + if (N == 1 || M == 1) { + return 1; + } + int max = Math.max(N, M); + int min = Math.min(N, M); + int pre = (1 << min) - 1; + return process2(pre, 0, max, min); + } + + // 上一行的状态,是pre,limit是用来对齐的,固定参数不用管 + // 当前来到i行,一共N行,返回填满的方法数 + public static int process2(int pre, int i, int N, int M) { + if (i == N) { // base case + return pre == ((1 << M) - 1) ? 1 : 0; + } + int op = ((~pre) & ((1 << M) - 1)); + return dfs2(op, M - 1, i, N, M); + } + + public static int dfs2(int op, int col, int level, int N, int M) { + if (col == -1) { + return process2(op, level + 1, N, M); + } + int ans = 0; + ans += dfs2(op, col - 1, level, N, M); + if ((op & (1 << col)) == 0 && col - 1 >= 0 && (op & (1 << (col - 1))) == 0) { + ans += dfs2((op | (3 << (col - 1))), col - 2, level, N, M); + } + return ans; + } + + // 记忆化搜索的解 + // Min(N,M) 不超过 32 + public static int ways3(int N, int M) { + if (N < 1 || M < 1 || ((N * M) & 1) != 0) { + return 0; + } + if (N == 1 || M == 1) { + return 1; + } + int max = Math.max(N, M); + int min = Math.min(N, M); + int pre = (1 << min) - 1; + int[][] dp = new int[1 << min][max + 1]; + for (int i = 0; i < dp.length; i++) { + for (int j = 0; j < dp[0].length; j++) { + dp[i][j] = -1; + } + } + return process3(pre, 0, max, min, dp); + } + + public static int process3(int pre, int i, int N, int M, int[][] dp) { + if (dp[pre][i] != -1) { + return dp[pre][i]; + } + int ans = 0; + if (i == N) { + ans = pre == ((1 << M) - 1) ? 1 : 0; + } else { + int op = ((~pre) & ((1 << M) - 1)); + ans = dfs3(op, M - 1, i, N, M, dp); + } + dp[pre][i] = ans; + return ans; + } + + public static int dfs3(int op, int col, int level, int N, int M, int[][] dp) { + if (col == -1) { + return process3(op, level + 1, N, M, dp); + } + int ans = 0; + ans += dfs3(op, col - 1, level, N, M, dp); + if (col > 0 && (op & (3 << (col - 1))) == 0) { + ans += dfs3((op | (3 << (col - 1))), col - 2, level, N, M, dp); + } + return ans; + } + + // 严格位置依赖的动态规划解 + public static int ways4(int N, int M) { + if (N < 1 || M < 1 || ((N * M) & 1) != 0) { + return 0; + } + if (N == 1 || M == 1) { + return 1; + } + int big = N > M ? N : M; + int small = big == N ? M : N; + int sn = 1 << small; + int limit = sn - 1; + int[] dp = new int[sn]; + dp[limit] = 1; + int[] cur = new int[sn]; + for (int level = 0; level < big; level++) { + for (int status = 0; status < sn; status++) { + if (dp[status] != 0) { + int op = (~status) & limit; + dfs4(dp[status], op, 0, small - 1, cur); + } + } + for (int i = 0; i < sn; i++) { + dp[i] = 0; + } + int[] tmp = dp; + dp = cur; + cur = tmp; + } + return dp[limit]; + } + + public static void dfs4(int way, int op, int index, int end, int[] cur) { + if (index == end) { + cur[op] += way; + } else { + dfs4(way, op, index + 1, end, cur); + if (((3 << index) & op) == 0) { // 11 << index 可以放砖 + dfs4(way, op | (3 << index), index + 1, end, cur); + } + } + } + + public static void main(String[] args) { + int N = 8; + int M = 6; + System.out.println(ways1(N, M)); + System.out.println(ways2(N, M)); + System.out.println(ways3(N, M)); + System.out.println(ways4(N, M)); + + N = 10; + M = 10; + System.out.println("========="); + System.out.println(ways3(N, M)); + System.out.println(ways4(N, M)); + } + +} diff --git a/体系学习班/class44/Code01_LastSubstringInLexicographicalOrder.java b/体系学习班/class44/Code01_LastSubstringInLexicographicalOrder.java new file mode 100644 index 0000000..27ac147 --- /dev/null +++ b/体系学习班/class44/Code01_LastSubstringInLexicographicalOrder.java @@ -0,0 +1,136 @@ +package class44; + +// 测试链接: https://leetcode.com/problems/last-substring-in-lexicographical-order/ +public class Code01_LastSubstringInLexicographicalOrder { + + public static String lastSubstring(String s) { + if (s == null || s.length() == 0) { + return s; + } + int N = s.length(); + char[] str = s.toCharArray(); + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (char cha : str) { + min = Math.min(min, cha); + max = Math.max(max, cha); + } + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = str[i] - min + 1; + } + DC3 dc3 = new DC3(arr, max - min + 1); + return s.substring(dc3.sa[N - 1]); + } + + public static class DC3 { + + public int[] sa; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + } + +} diff --git a/体系学习班/class44/DC3.java b/体系学习班/class44/DC3.java new file mode 100644 index 0000000..47e7601 --- /dev/null +++ b/体系学习班/class44/DC3.java @@ -0,0 +1,168 @@ +package class44; + +public class DC3 { + + public int[] sa; + + public int[] rank; + + public int[] height; + + // 构造方法的约定: + // 数组叫nums,如果你是字符串,请转成整型数组nums + // 数组中,最小值>=1 + // 如果不满足,处理成满足的,也不会影响使用 + // max, nums里面最大值是多少 + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + height = height(nums); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i; + } + return ans; + } + + private int[] height(int[] s) { + int n = s.length; + int[] ans = new int[n]; + for (int i = 0, k = 0; i < n; ++i) { + if (rank[i] != 0) { + if (k > 0) { + --k; + } + int j = sa[rank[i] - 1]; + while (i + k < n && j + k < n && s[i + k] == s[j + k]) { + ++k; + } + ans[rank[i]] = k; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int maxValue) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 100000; + int maxValue = 100; + long start = System.currentTimeMillis(); + new DC3(randomArray(len, maxValue), maxValue); + long end = System.currentTimeMillis(); + System.out.println("数据量 " + len + ", 运行时间 " + (end - start) + " ms"); + } + +} \ No newline at end of file diff --git a/体系学习班/class44/DC3_Algorithm.pdf b/体系学习班/class44/DC3_Algorithm.pdf new file mode 100644 index 0000000..f053a67 Binary files /dev/null and b/体系学习班/class44/DC3_Algorithm.pdf differ diff --git a/体系学习班/class45/Code01_InsertS2MakeMostAlphabeticalOrder.java b/体系学习班/class45/Code01_InsertS2MakeMostAlphabeticalOrder.java new file mode 100644 index 0000000..0962781 --- /dev/null +++ b/体系学习班/class45/Code01_InsertS2MakeMostAlphabeticalOrder.java @@ -0,0 +1,261 @@ +package class45; + +public class Code01_InsertS2MakeMostAlphabeticalOrder { + + // 暴力方法 + public static String right(String s1, String s2) { + if (s1 == null || s1.length() == 0) { + return s2; + } + if (s2 == null || s2.length() == 0) { + return s1; + } + String p1 = s1 + s2; + String p2 = s2 + s1; + String ans = p1.compareTo(p2) > 0 ? p1 : p2; + for (int end = 1; end < s1.length(); end++) { + String cur = s1.substring(0, end) + s2 + s1.substring(end); + if (cur.compareTo(ans) > 0) { + ans = cur; + } + } + return ans; + } + + // 正式方法 O(N+M) + O(M^2) + // N : s1长度 + // M : s2长度 + public static String maxCombine(String s1, String s2) { + if (s1 == null || s1.length() == 0) { + return s2; + } + if (s2 == null || s2.length() == 0) { + return s1; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length; + int M = str2.length; + int min = str1[0]; + int max = str1[0]; + for (int i = 1; i < N; i++) { + min = Math.min(min, str1[i]); + max = Math.max(max, str1[i]); + } + for (int i = 0; i < M; i++) { + min = Math.min(min, str2[i]); + max = Math.max(max, str2[i]); + } + int[] all = new int[N + M + 1]; + int index = 0; + for (int i = 0; i < N; i++) { + all[index++] = str1[i] - min + 2; + } + all[index++] = 1; + for (int i = 0; i < M; i++) { + all[index++] = str2[i] - min + 2; + } + DC3 dc3 = new DC3(all, max - min + 2); + int[] rank = dc3.rank; + int comp = N + 1; + for (int i = 0; i < N; i++) { + if (rank[i] < rank[comp]) { + int best = bestSplit(s1, s2, i); + return s1.substring(0, best) + s2 + s1.substring(best); + } + } + return s1 + s2; + } + + public static int bestSplit(String s1, String s2, int first) { + int N = s1.length(); + int M = s2.length(); + int end = N; + for (int i = first, j = 0; i < N && j < M; i++, j++) { + if (s1.charAt(i) < s2.charAt(j)) { + end = i; + break; + } + } + String bestPrefix = s2; + int bestSplit = first; + for (int i = first + 1, j = M - 1; i <= end; i++, j--) { + String curPrefix = s1.substring(first, i) + s2.substring(0, j); + if (curPrefix.compareTo(bestPrefix) >= 0) { + bestPrefix = curPrefix; + bestSplit = i; + } + } + return bestSplit; + } + + public static class DC3 { + + public int[] sa; + + public int[] rank; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i; + } + return ans; + } + + } + + // for test + public static String randomNumberString(int len, int range) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * range) + '0'); + } + return String.valueOf(str); + } + + // for test + public static void main(String[] args) { + int range = 10; + int len = 50; + int testTime = 100000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int s1Len = (int) (Math.random() * len); + int s2Len = (int) (Math.random() * len); + String s1 = randomNumberString(s1Len, range); + String s2 = randomNumberString(s2Len, range); + String ans1 = right(s1, s2); + String ans2 = maxCombine(s1, s2); + if (!ans1.equals(ans2)) { + System.out.println("Oops!"); + System.out.println(s1); + System.out.println(s2); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("=========="); + + System.out.println("性能测试开始"); + int s1Len = 1000000; + int s2Len = 500; + String s1 = randomNumberString(s1Len, range); + String s2 = randomNumberString(s2Len, range); + long start = System.currentTimeMillis(); + maxCombine(s1, s2); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " ms"); + System.out.println("性能测试结束"); + } + +} diff --git a/体系学习班/class45/Code02_CreateMaximumNumber.java b/体系学习班/class45/Code02_CreateMaximumNumber.java new file mode 100644 index 0000000..1e9cc28 --- /dev/null +++ b/体系学习班/class45/Code02_CreateMaximumNumber.java @@ -0,0 +1,250 @@ +package class45; + +// 测试链接: https://leetcode.com/problems/create-maximum-number/ +public class Code02_CreateMaximumNumber { + + public static int[] maxNumber1(int[] nums1, int[] nums2, int k) { + int len1 = nums1.length; + int len2 = nums2.length; + if (k < 0 || k > len1 + len2) { + return null; + } + int[] res = new int[k]; + int[][] dp1 = getdp(nums1); // 生成dp1这个表,以后从nums1中,只要固定拿N个数, + int[][] dp2 = getdp(nums2); + // get1 从arr1里拿的数量 + // K - get1 从arr2里拿的数量 + for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) { + // arr1 挑 get1个,怎么得到一个最优结果 + int[] pick1 = maxPick(nums1, dp1, get1); + int[] pick2 = maxPick(nums2, dp2, k - get1); + int[] merge = merge(pick1, pick2); + res = preMoreThanLast(res, 0, merge, 0) ? res : merge; + } + return res; + } + + public static int[] merge(int[] nums1, int[] nums2) { + int k = nums1.length + nums2.length; + int[] ans = new int[k]; + for (int i = 0, j = 0, r = 0; r < k; ++r) { + ans[r] = preMoreThanLast(nums1, i, nums2, j) ? nums1[i++] : nums2[j++]; + } + return ans; + } + + public static boolean preMoreThanLast(int[] nums1, int i, int[] nums2, int j) { + while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) { + i++; + j++; + } + return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]); + } + + public static int[] maxNumber2(int[] nums1, int[] nums2, int k) { + int len1 = nums1.length; + int len2 = nums2.length; + if (k < 0 || k > len1 + len2) { + return null; + } + int[] res = new int[k]; + int[][] dp1 = getdp(nums1); + int[][] dp2 = getdp(nums2); + for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) { + int[] pick1 = maxPick(nums1, dp1, get1); + int[] pick2 = maxPick(nums2, dp2, k - get1); + int[] merge = mergeBySuffixArray(pick1, pick2); + res = moreThan(res, merge) ? res : merge; + } + return res; + } + + public static boolean moreThan(int[] pre, int[] last) { + int i = 0; + int j = 0; + while (i < pre.length && j < last.length && pre[i] == last[j]) { + i++; + j++; + } + return j == last.length || (i < pre.length && pre[i] > last[j]); + } + + public static int[] mergeBySuffixArray(int[] nums1, int[] nums2) { + int size1 = nums1.length; + int size2 = nums2.length; + int[] nums = new int[size1 + 1 + size2]; + for (int i = 0; i < size1; i++) { + nums[i] = nums1[i] + 2; + } + nums[size1] = 1; + for (int j = 0; j < size2; j++) { + nums[j + size1 + 1] = nums2[j] + 2; + } + DC3 dc3 = new DC3(nums, 11); + int[] rank = dc3.rank; + int[] ans = new int[size1 + size2]; + int i = 0; + int j = 0; + int r = 0; + while (i < size1 && j < size2) { + ans[r++] = rank[i] > rank[j + size1 + 1] ? nums1[i++] : nums2[j++]; + } + while (i < size1) { + ans[r++] = nums1[i++]; + } + while (j < size2) { + ans[r++] = nums2[j++]; + } + return ans; + } + + public static class DC3 { + + public int[] sa; + + public int[] rank; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i; + } + return ans; + } + + } + + public static int[][] getdp(int[] arr) { + int size = arr.length; // 0~N-1 + int pick = arr.length + 1; // 1 ~ N + int[][] dp = new int[size][pick]; + // get 不从0开始,因为拿0个无意义 + for (int get = 1; get < pick; get++) { // 1 ~ N + int maxIndex = size - get; + // i~N-1 + for (int i = size - get; i >= 0; i--) { + if (arr[i] >= arr[maxIndex]) { + maxIndex = i; + } + dp[i][get] = maxIndex; + } + } + return dp; + } + + public static int[] maxPick(int[] arr, int[][] dp, int pick) { + int[] res = new int[pick]; + for (int resIndex = 0, dpRow = 0; pick > 0; pick--, resIndex++) { + res[resIndex] = arr[dp[dpRow][pick]]; + dpRow = dp[dpRow][pick] + 1; + } + return res; + } + +} diff --git a/体系学习班/class45/Code03_LongestCommonSubstringConquerByHeight.java b/体系学习班/class45/Code03_LongestCommonSubstringConquerByHeight.java new file mode 100644 index 0000000..2b92fff --- /dev/null +++ b/体系学习班/class45/Code03_LongestCommonSubstringConquerByHeight.java @@ -0,0 +1,286 @@ +package class45; + +// 最长公共子串问题是面试常见题目之一 +// 假设str1长度N,str2长度M +// 因为最优解的难度所限,一般在面试场上回答出O(N*M)的解法已经是比较优秀了 +// 因为得到O(N*M)的解法,就已经需要用到动态规划了 +// 但其实这个问题的最优解是O(N+M),为了达到这个复杂度可是不容易 +// 首先需要用到DC3算法得到后缀数组(sa) +// 进而用sa数组去生成height数组 +// 而且在生成的时候,还有一个不回退的优化,都非常不容易理解 +// 这就是后缀数组在面试算法中的地位 : 德高望重的噩梦 +public class Code03_LongestCommonSubstringConquerByHeight { + + public static int lcs1(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int row = 0; + int col = str2.length - 1; + int max = 0; + while (row < str1.length) { + int i = row; + int j = col; + int len = 0; + while (i < str1.length && j < str2.length) { + if (str1[i] != str2[j]) { + len = 0; + } else { + len++; + } + if (len > max) { + max = len; + } + i++; + j++; + } + if (col > 0) { + col--; + } else { + row++; + } + } + return max; + } + + public static int lcs2(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length; + int M = str2.length; + int min = str1[0]; + int max = str1[0]; + for (int i = 1; i < N; i++) { + min = Math.min(min, str1[i]); + max = Math.max(max, str1[i]); + } + for (int i = 0; i < M; i++) { + min = Math.min(min, str2[i]); + max = Math.max(max, str2[i]); + } + int[] all = new int[N + M + 1]; + int index = 0; + for (int i = 0; i < N; i++) { + all[index++] = str1[i] - min + 2; + } + all[index++] = 1; + for (int i = 0; i < M; i++) { + all[index++] = str2[i] - min + 2; + } + DC3 dc3 = new DC3(all, max - min + 2); + int n = all.length; + int[] sa = dc3.sa; + int[] height = dc3.height; + int ans = 0; + for (int i = 1; i < n; i++) { + int Y = sa[i - 1]; + int X = sa[i]; + if (Math.min(X, Y) < N && Math.max(X, Y) > N) { + ans = Math.max(ans, height[i]); + } + } + return ans; + } + + public static class DC3 { + + public int[] sa; + + public int[] rank; + + public int[] height; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + height = height(nums); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i; + } + return ans; + } + + private int[] height(int[] s) { + int n = s.length; + int[] ans = new int[n]; + // 依次求h[i] , k = 0 + for (int i = 0, k = 0; i < n; ++i) { + if (rank[i] != 0) { + if (k > 0) { + --k; + } + int j = sa[rank[i] - 1]; + while (i + k < n && j + k < n && s[i + k] == s[j + k]) { + ++k; + } + // h[i] = k + ans[rank[i]] = k; + } + } + return ans; + } + + } + + // for test + public static String randomNumberString(int len, int range) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * range) + 'a'); + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int len = 30; + int range = 5; + int testTime = 100000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int N1 = (int) (Math.random() * len); + int N2 = (int) (Math.random() * len); + String str1 = randomNumberString(N1, range); + String str2 = randomNumberString(N2, range); + int ans1 = lcs1(str1, str2); + int ans2 = lcs2(str1, str2); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("功能测试结束"); + System.out.println("=========="); + + System.out.println("性能测试开始"); + len = 80000; + range = 26; + long start; + long end; + + String str1 = randomNumberString(len, range); + String str2 = randomNumberString(len, range); + + start = System.currentTimeMillis(); + int ans1 = lcs1(str1, str2); + end = System.currentTimeMillis(); + System.out.println("方法1结果 : " + ans1 + " , 运行时间 : " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + int ans2 = lcs2(str1, str2); + end = System.currentTimeMillis(); + System.out.println("方法2结果 : " + ans2 + " , 运行时间 : " + (end - start) + " ms"); + + System.out.println("性能测试结束"); + + } + +} diff --git a/体系学习班/class45/Code04_LongestRepeatingSubstring.java b/体系学习班/class45/Code04_LongestRepeatingSubstring.java new file mode 100644 index 0000000..a6090e3 --- /dev/null +++ b/体系学习班/class45/Code04_LongestRepeatingSubstring.java @@ -0,0 +1,196 @@ +package class45; + +// 一个非常经典的题 +// 这道题课上没有讲 +// 后缀数组的模版题 +// 需要学会DC3算法生成后缀数组 +// 需要学会课上讲的如何生成高度数组 +// 时间复杂度O(N),连官方题解都没有做到的时间复杂度,但这才是最优解 +// 测试链接 : https://leetcode.cn/problems/longest-repeating-substring/ +public class Code04_LongestRepeatingSubstring { + + public static int longestRepeatingSubstring(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int n = str.length; + int min = str[0]; + int max = str[0]; + for (int i = 1; i < n; i++) { + min = Math.min(min, str[i]); + max = Math.max(max, str[i]); + } + int[] all = new int[n]; + for (int i = 0; i < n; i++) { + all[i] = str[i] - min + 1; + } + DC3 dc3 = new DC3(all, max - min + 1); + int ans = 0; + for (int i = 1; i < n; i++) { + ans = Math.max(ans, dc3.height[i]); + } + return ans; + } + + public static class DC3 { + public int[] sa; + public int[] rank; + public int[] height; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + height = height(nums); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i; + } + return ans; + } + + private int[] height(int[] s) { + int n = s.length; + int[] ans = new int[n]; + for (int i = 0, k = 0; i < n; ++i) { + if (rank[i] != 0) { + if (k > 0) { + --k; + } + int j = sa[rank[i] - 1]; + while (i + k < n && j + k < n && s[i + k] == s[j + k]) { + ++k; + } + ans[rank[i]] = k; + } + } + return ans; + } + + } + + // 为了测试, 不用提交 + public static String randomString(int n, int r) { + char[] str = new char[n]; + for (int i = 0; i < n; i++) { + str[i] = (char) ((int) (Math.random() * r) + 'a'); + } + return String.valueOf(str); + } + + // 为了测试, 不用提交 + public static void main(String[] args) { + int n = 500000; + int r = 3; + long start = System.currentTimeMillis(); + longestRepeatingSubstring(randomString(n, r)); + long end = System.currentTimeMillis(); + System.out.println("字符长度为 " + n + ", 字符种类数为 " + r + " 时"); + System.out.println("求最长重复子串的运行时间 : " + (end - start) + " 毫秒"); + } + +} diff --git a/体系学习班/class46/Code01_BurstBalloons.java b/体系学习班/class46/Code01_BurstBalloons.java new file mode 100644 index 0000000..71d2602 --- /dev/null +++ b/体系学习班/class46/Code01_BurstBalloons.java @@ -0,0 +1,107 @@ +package class46; + +// 本题测试链接 : https://leetcode.com/problems/burst-balloons/ +public class Code01_BurstBalloons { + + public static int maxCoins0(int[] arr) { + // [3,2,1,3] + // [1,3,2,1,3,1] + int N = arr.length; + int[] help = new int[N + 2]; + for (int i = 0; i < N; i++) { + help[i + 1] = arr[i]; + } + help[0] = 1; + help[N + 1] = 1; + return func(help, 1, N); + } + + // L-1位置,和R+1位置,永远不越界,并且,[L-1] 和 [R+1] 一定没爆呢! + // 返回,arr[L...R]打爆所有气球,最大得分是什么 + public static int func(int[] arr, int L, int R) { + if (L == R) { + return arr[L - 1] * arr[L] * arr[R + 1]; + } + // 尝试每一种情况,最后打爆的气球,是什么位置 + // L...R + // L位置的气球,最后打爆 + int max = func(arr, L + 1, R) + arr[L - 1] * arr[L] * arr[R + 1]; + // R位置的气球,最后打爆 + max = Math.max(max, func(arr, L, R - 1) + arr[L - 1] * arr[R] * arr[R + 1]); + // 尝试所有L...R,中间的位置,(L,R) + for (int i = L + 1; i < R; i++) { + // i位置的气球,最后打爆 + int left = func(arr, L, i - 1); + int right = func(arr, i + 1, R); + int last = arr[L - 1] * arr[i] * arr[R + 1]; + int cur = left + right + last; + max = Math.max(max, cur); + } + return max; + } + + public static int maxCoins1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0]; + } + int N = arr.length; + int[] help = new int[N + 2]; + help[0] = 1; + help[N + 1] = 1; + for (int i = 0; i < N; i++) { + help[i + 1] = arr[i]; + } + return process(help, 1, N); + } + + // 打爆arr[L..R]范围上的所有气球,返回最大的分数 + // 假设arr[L-1]和arr[R+1]一定没有被打爆 + public static int process(int[] arr, int L, int R) { + if (L == R) {// 如果arr[L..R]范围上只有一个气球,直接打爆即可 + return arr[L - 1] * arr[L] * arr[R + 1]; + } + // 最后打爆arr[L]的方案,和最后打爆arr[R]的方案,先比较一下 + int max = Math.max(arr[L - 1] * arr[L] * arr[R + 1] + process(arr, L + 1, R), + arr[L - 1] * arr[R] * arr[R + 1] + process(arr, L, R - 1)); + // 尝试中间位置的气球最后被打爆的每一种方案 + for (int i = L + 1; i < R; i++) { + max = Math.max(max, arr[L - 1] * arr[i] * arr[R + 1] + process(arr, L, i - 1) + process(arr, i + 1, R)); + } + return max; + } + + public static int maxCoins2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0]; + } + int N = arr.length; + int[] help = new int[N + 2]; + help[0] = 1; + help[N + 1] = 1; + for (int i = 0; i < N; i++) { + help[i + 1] = arr[i]; + } + int[][] dp = new int[N + 2][N + 2]; + for (int i = 1; i <= N; i++) { + dp[i][i] = help[i - 1] * help[i] * help[i + 1]; + } + for (int L = N; L >= 1; L--) { + for (int R = L + 1; R <= N; R++) { + int ans = help[L - 1] * help[L] * help[R + 1] + dp[L + 1][R]; + ans = Math.max(ans, help[L - 1] * help[R] * help[R + 1] + dp[L][R - 1]); + for (int i = L + 1; i < R; i++) { + ans = Math.max(ans, help[L - 1] * help[i] * help[R + 1] + dp[L][i - 1] + dp[i + 1][R]); + } + dp[L][R] = ans; + } + } + return dp[1][N]; + } + +} diff --git a/体系学习班/class46/Code02_RemoveBoxes.java b/体系学习班/class46/Code02_RemoveBoxes.java new file mode 100644 index 0000000..8db4d8f --- /dev/null +++ b/体系学习班/class46/Code02_RemoveBoxes.java @@ -0,0 +1,81 @@ +package class46; + +// 本题测试链接 : https://leetcode.com/problems/remove-boxes/ +public class Code02_RemoveBoxes { + + // arr[L...R]消除,而且前面跟着K个arr[L]这个数 + // 返回:所有东西都消掉,最大得分 + public static int func1(int[] arr, int L, int R, int K) { + if (L > R) { + return 0; + } + int ans = func1(arr, L + 1, R, 0) + (K + 1) * (K + 1); + + // 前面的K个X,和arr[L]数,合在一起了,现在有K+1个arr[L]位置的数 + for (int i = L + 1; i <= R; i++) { + if (arr[i] == arr[L]) { + ans = Math.max(ans, func1(arr, L + 1, i - 1, 0) + func1(arr, i, R, K + 1)); + } + } + return ans; + } + + public static int removeBoxes1(int[] boxes) { + int N = boxes.length; + int[][][] dp = new int[N][N][N]; + int ans = process1(boxes, 0, N - 1, 0, dp); + return ans; + } + + public static int process1(int[] boxes, int L, int R, int K, int[][][] dp) { + if (L > R) { + return 0; + } + if (dp[L][R][K] > 0) { + return dp[L][R][K]; + } + int ans = process1(boxes, L + 1, R, 0, dp) + (K + 1) * (K + 1); + for (int i = L + 1; i <= R; i++) { + if (boxes[i] == boxes[L]) { + ans = Math.max(ans, process1(boxes, L + 1, i - 1, 0, dp) + process1(boxes, i, R, K + 1, dp)); + } + } + dp[L][R][K] = ans; + return ans; + } + + public static int removeBoxes2(int[] boxes) { + int N = boxes.length; + int[][][] dp = new int[N][N][N]; + int ans = process2(boxes, 0, N - 1, 0, dp); + return ans; + } + + public static int process2(int[] boxes, int L, int R, int K, int[][][] dp) { + if (L > R) { + return 0; + } + if (dp[L][R][K] > 0) { + return dp[L][R][K]; + } + // 找到开头, + // 1,1,1,1,1,5 + // 3 4 5 6 7 8 + // ! + int last = L; + while (last + 1 <= R && boxes[last + 1] == boxes[L]) { + last++; + } + // K个1 (K + last - L) last + int pre = K + last - L; + int ans = (pre + 1) * (pre + 1) + process2(boxes, last + 1, R, 0, dp); + for (int i = last + 2; i <= R; i++) { + if (boxes[i] == boxes[L] && boxes[i - 1] != boxes[L]) { + ans = Math.max(ans, process2(boxes, last + 1, i - 1, 0, dp) + process2(boxes, i, R, pre + 1, dp)); + } + } + dp[L][R][K] = ans; + return ans; + } + +} diff --git a/体系学习班/class46/Code03_DeleteAdjacentSameCharacter.java b/体系学习班/class46/Code03_DeleteAdjacentSameCharacter.java new file mode 100644 index 0000000..0b1e7f3 --- /dev/null +++ b/体系学习班/class46/Code03_DeleteAdjacentSameCharacter.java @@ -0,0 +1,176 @@ +package class46; + +// 如果一个字符相邻的位置没有相同字符,那么这个位置的字符出现不能被消掉 +// 比如:"ab",其中a和b都不能被消掉 +// 如果一个字符相邻的位置有相同字符,就可以一起消掉 +// 比如:"abbbc",中间一串的b是可以被消掉的,消除之后剩下"ac" +// 某些字符如果消掉了,剩下的字符认为重新靠在一起 +// 给定一个字符串,你可以决定每一步消除的顺序,目标是请尽可能多的消掉字符,返回最少的剩余字符数量 +// 比如:"aacca", 如果先消掉最左侧的"aa",那么将剩下"cca",然后把"cc"消掉,剩下的"a"将无法再消除,返回1 +// 但是如果先消掉中间的"cc",那么将剩下"aaa",最后都消掉就一个字符也不剩了,返回0,这才是最优解。 +// 再比如:"baaccabb", +// 如果先消除最左侧的两个a,剩下"bccabb", +// 如果再消除最左侧的两个c,剩下"babb", +// 最后消除最右侧的两个b,剩下"ba"无法再消除,返回2 +// 而最优策略是: +// 如果先消除中间的两个c,剩下"baaabb", +// 如果再消除中间的三个a,剩下"bbb", +// 最后消除三个b,不留下任何字符,返回0,这才是最优解 +public class Code03_DeleteAdjacentSameCharacter { + + // 暴力解 + public static int restMin1(String s) { + if (s == null) { + return 0; + } + if (s.length() < 2) { + return s.length(); + } + int minLen = s.length(); + for (int L = 0; L < s.length(); L++) { + for (int R = L + 1; R < s.length(); R++) { + if (canDelete(s.substring(L, R + 1))) { + minLen = Math.min(minLen, restMin1(s.substring(0, L) + s.substring(R + 1, s.length()))); + } + } + } + return minLen; + } + + public static boolean canDelete(String s) { + char[] str = s.toCharArray(); + for (int i = 1; i < str.length; i++) { + if (str[i - 1] != str[i]) { + return false; + } + } + return true; + } + + // 优良尝试的暴力递归版本 + public static int restMin2(String s) { + if (s == null) { + return 0; + } + if (s.length() < 2) { + return s.length(); + } + char[] str = s.toCharArray(); + return process(str, 0, str.length - 1, false); + } + + // str[L...R] 前面有没有跟着[L]字符,has T 有 F 无 + // L,R,has + // 最少能剩多少字符,消不了 + public static int process(char[] str, int L, int R, boolean has) { + if (L > R) { + return 0; + } + if (L == R) { + return has ? 0 : 1; + } + int index = L; + int K = has ? 1 : 0; + while (index <= R && str[index] == str[L]) { + K++; + index++; + } + // index表示,第一个不是[L]字符的位置 + int way1 = (K > 1 ? 0 : 1) + process(str, index, R, false); + int way2 = Integer.MAX_VALUE; + for (int split = index; split <= R; split++) { + if (str[split] == str[L] && str[split] != str[split - 1]) { + if (process(str, index, split - 1, false) == 0) { + way2 = Math.min(way2, process(str, split, R, K != 0)); + } + } + } + return Math.min(way1, way2); + } + + // 优良尝试的动态规划版本 + public static int restMin3(String s) { + if (s == null) { + return 0; + } + if (s.length() < 2) { + return s.length(); + } + char[] str = s.toCharArray(); + int N = str.length; + int[][][] dp = new int[N][N][2]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + for (int k = 0; k < 2; k++) { + dp[i][j][k] = -1; + } + } + } + return dpProcess(str, 0, N - 1, false, dp); + } + + public static int dpProcess(char[] str, int L, int R, boolean has, int[][][] dp) { + if (L > R) { + return 0; + } + int K = has ? 1 : 0; + if (dp[L][R][K] != -1) { + return dp[L][R][K]; + } + int ans = 0; + if (L == R) { + ans = (K == 0 ? 1 : 0); + } else { + int index = L; + int all = K; + while (index <= R && str[index] == str[L]) { + all++; + index++; + } + int way1 = (all > 1 ? 0 : 1) + dpProcess(str, index, R, false, dp); + int way2 = Integer.MAX_VALUE; + for (int split = index; split <= R; split++) { + if (str[split] == str[L] && str[split] != str[split - 1]) { + if (dpProcess(str, index, split - 1, false, dp) == 0) { + way2 = Math.min(way2, dpProcess(str, split, R, all > 0, dp)); + } + } + } + ans = Math.min(way1, way2); + } + dp[L][R][K] = ans; + return ans; + } + + public static String randomString(int len, int variety) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * variety) + 'a'); + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int maxLen = 16; + int variety = 3; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * maxLen); + String str = randomString(len, variety); + int ans1 = restMin1(str); + int ans2 = restMin2(str); + int ans3 = restMin3(str); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println(str); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class46/Code04_MaxSumLengthNoMore.java b/体系学习班/class46/Code04_MaxSumLengthNoMore.java new file mode 100644 index 0000000..be50615 --- /dev/null +++ b/体系学习班/class46/Code04_MaxSumLengthNoMore.java @@ -0,0 +1,100 @@ +package class46; + +import java.util.LinkedList; + +// 给定一个数组arr,和一个正数M +// 返回在子数组长度不大于M的情况下,最大的子数组累加和 +public class Code04_MaxSumLengthNoMore { + + // O(N^2)的解法,暴力解,用作对数器 + public static int test(int[] arr, int M) { + if (arr == null || arr.length == 0 || M < 1) { + return 0; + } + int N = arr.length; + int max = Integer.MIN_VALUE; + for (int L = 0; L < N; L++) { + int sum = 0; + for (int R = L; R < N; R++) { + if (R - L + 1 > M) { + break; + } + sum += arr[R]; + max = Math.max(max, sum); + } + } + return max; + } + + // O(N)的解法,最优解 + public static int maxSum(int[] arr, int M) { + if (arr == null || arr.length == 0 || M < 1) { + return 0; + } + int N = arr.length; + int[] sum = new int[N]; + sum[0] = arr[0]; + for (int i = 1; i < N; i++) { + sum[i] = sum[i - 1] + arr[i]; + } + LinkedList qmax = new LinkedList<>(); + int i = 0; + int end = Math.min(N, M); + for (; i < end; i++) { + while (!qmax.isEmpty() && sum[qmax.peekLast()] <= sum[i]) { + qmax.pollLast(); + } + qmax.add(i); + } + int max = sum[qmax.peekFirst()]; + int L = 0; + for (; i < N; L++, i++) { + if (qmax.peekFirst() == L) { + qmax.pollFirst(); + } + while (!qmax.isEmpty() && sum[qmax.peekLast()] <= sum[i]) { + qmax.pollLast(); + } + qmax.add(i); + max = Math.max(max, sum[qmax.peekFirst()] - sum[L]); + } + for (; L < N - 1; L++) { + if (qmax.peekFirst() == L) { + qmax.pollFirst(); + } + max = Math.max(max, sum[qmax.peekFirst()] - sum[L]); + } + return max; + } + + // 用作测试 + public static int[] randomArray(int len, int max) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * max) - (int) (Math.random() * max); + } + return arr; + } + + // 用作测试 + public static void main(String[] args) { + int maxN = 50; + int maxValue = 100; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * maxN); + int M = (int) (Math.random() * maxN); + int[] arr = randomArray(N, maxValue); + int ans1 = test(arr, M); + int ans2 = maxSum(arr, M); + if (ans1 != ans2) { + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/体系学习班/class46/Code05_HuffmanTree.java b/体系学习班/class46/Code05_HuffmanTree.java new file mode 100644 index 0000000..65b6be8 --- /dev/null +++ b/体系学习班/class46/Code05_HuffmanTree.java @@ -0,0 +1,214 @@ +package class46; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.PriorityQueue; + +// 本文件不牵扯任何byte类型的转化 +// 怎么转byte自己来,我只负责huffman算法本身的正确实现 +// 字符串为空的时候,自己处理边界吧 +// 实现的代码通过了大样本随机测试的对数器 +// 可以从main函数的内容开始看起 +public class Code05_HuffmanTree { + + // 根据文章str, 生成词频统计表 + public static HashMap countMap(String str) { + HashMap ans = new HashMap<>(); + char[] s = str.toCharArray(); + for (char cha : s) { + if (!ans.containsKey(cha)) { + ans.put(cha, 1); + } else { + ans.put(cha, ans.get(cha) + 1); + } + } + return ans; + } + + public static class Node { + public int count; + public Node left; + public Node right; + + public Node(int c) { + count = c; + } + } + + public static class NodeComp implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.count - o2.count; + } + + } + + // 根据由文章生成词频表countMap,生成哈夫曼编码表 + // key : 字符 + // value: 该字符编码后的二进制形式 + // 比如,频率表 A:60, B:45, C:13 D:69 E:14 F:5 G:3 + // A 10 + // B 01 + // C 0011 + // D 11 + // E 000 + // F 00101 + // G 00100 + public static HashMap huffmanForm(HashMap countMap) { + HashMap ans = new HashMap<>(); + if (countMap.size() == 1) { + for (char key : countMap.keySet()) { + ans.put(key, "0"); + } + return ans; + } + HashMap nodes = new HashMap<>(); + PriorityQueue heap = new PriorityQueue<>(new NodeComp()); + for (Entry entry : countMap.entrySet()) { + Node cur = new Node(entry.getValue()); + char cha = entry.getKey(); + nodes.put(cur, cha); + heap.add(cur); + } + while (heap.size() != 1) { + Node a = heap.poll(); + Node b = heap.poll(); + Node h = new Node(a.count + b.count); + h.left = a; + h.right = b; + heap.add(h); + } + Node head = heap.poll(); + fillForm(head, "", nodes, ans); + return ans; + } + + public static void fillForm(Node head, String pre, HashMap nodes, HashMap ans) { + if (nodes.containsKey(head)) { + ans.put(nodes.get(head), pre); + } else { + fillForm(head.left, pre + "0", nodes, ans); + fillForm(head.right, pre + "1", nodes, ans); + } + } + + // 原始字符串str,根据哈夫曼编码表,转译成哈夫曼编码返回 + public static String huffmanEncode(String str, HashMap huffmanForm) { + char[] s = str.toCharArray(); + StringBuilder builder = new StringBuilder(); + for (char cha : s) { + builder.append(huffmanForm.get(cha)); + } + return builder.toString(); + } + + // 原始字符串的哈夫曼编码huffmanEncode,根据哈夫曼编码表,还原成原始字符串 + public static String huffmanDecode(String huffmanEncode, HashMap huffmanForm) { + TrieNode root = createTrie(huffmanForm); + TrieNode cur = root; + char[] encode = huffmanEncode.toCharArray(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < encode.length; i++) { + int index = encode[i] == '0' ? 0 : 1; + cur = cur.nexts[index]; + if (cur.nexts[0] == null && cur.nexts[1] == null) { + builder.append(cur.value); + cur = root; + } + } + return builder.toString(); + } + + public static TrieNode createTrie(HashMap huffmanForm) { + TrieNode root = new TrieNode(); + for (char key : huffmanForm.keySet()) { + char[] path = huffmanForm.get(key).toCharArray(); + TrieNode cur = root; + for (int i = 0; i < path.length; i++) { + int index = path[i] == '0' ? 0 : 1; + if (cur.nexts[index] == null) { + cur.nexts[index] = new TrieNode(); + } + cur = cur.nexts[index]; + } + cur.value = key; + } + return root; + } + + public static class TrieNode { + public char value; + public TrieNode[] nexts; + + public TrieNode() { + value = 0; + nexts = new TrieNode[2]; + } + } + + // 为了测试 + public static String randomNumberString(int len, int range) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * range) + 'a'); + } + return String.valueOf(str); + } + + // 为了测试 + public static void main(String[] args) { + // 根据词频表生成哈夫曼编码表 + HashMap map = new HashMap<>(); + map.put('A', 60); + map.put('B', 45); + map.put('C', 13); + map.put('D', 69); + map.put('E', 14); + map.put('F', 5); + map.put('G', 3); + HashMap huffmanForm = huffmanForm(map); + for (Entry entry : huffmanForm.entrySet()) { + System.out.println(entry.getKey() + " : " + entry.getValue()); + } + System.out.println("===================="); + // str是原始字符串 + String str = "CBBBAABBACAABDDEFBA"; + System.out.println(str); + // countMap是根据str建立的词频表 + HashMap countMap = countMap(str); + // hf是根据countMap生成的哈夫曼编码表 + HashMap hf = huffmanForm(countMap); + // huffmanEncode是原始字符串转译后的哈夫曼编码 + String huffmanEncode = huffmanEncode(str, hf); + System.out.println(huffmanEncode); + // huffmanDecode是哈夫曼编码还原成的原始字符串 + String huffmanDecode = huffmanDecode(huffmanEncode, hf); + System.out.println(huffmanDecode); + System.out.println("===================="); + System.out.println("大样本随机测试开始"); + // 字符串最大长度 + int len = 500; + // 所含字符种类 + int range = 26; + // 随机测试进行的次数 + int testTime = 100000; + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * len) + 1; + String test = randomNumberString(N, range); + HashMap counts = countMap(test); + HashMap form = huffmanForm(counts); + String encode = huffmanEncode(test, form); + String decode = huffmanDecode(encode, form); + if (!test.equals(decode)) { + System.out.println(test); + System.out.println(encode); + System.out.println(decode); + System.out.println("出错了!"); + } + } + System.out.println("大样本随机测试结束"); + } + +} diff --git a/体系学习班/class47/Code01_StrangePrinter.java b/体系学习班/class47/Code01_StrangePrinter.java new file mode 100644 index 0000000..b7172db --- /dev/null +++ b/体系学习班/class47/Code01_StrangePrinter.java @@ -0,0 +1,78 @@ +package class47; + +// 本题测试链接 : https://leetcode.com/problems/strange-printer/ +public class Code01_StrangePrinter { + + public static int strangePrinter1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + return process1(str, 0, str.length - 1); + } + + // 要想刷出str[L...R]的样子! + // 返回最少的转数 + public static int process1(char[] str, int L, int R) { + if (L == R) { + return 1; + } + // L...R + int ans = R - L + 1; + for (int k = L + 1; k <= R; k++) { + // L...k-1 k....R + ans = Math.min(ans, process1(str, L, k - 1) + process1(str, k, R) - (str[L] == str[k] ? 1 : 0)); + } + return ans; + } + + public static int strangePrinter2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + return process2(str, 0, N - 1, dp); + } + + public static int process2(char[] str, int L, int R, int[][] dp) { + if (dp[L][R] != 0) { + return dp[L][R]; + } + int ans = R - L + 1; + if (L == R) { + ans = 1; + } else { + for (int k = L + 1; k <= R; k++) { + ans = Math.min(ans, process2(str, L, k - 1, dp) + process2(str, k, R, dp) - (str[L] == str[k] ? 1 : 0)); + } + } + dp[L][R] = ans; + return ans; + } + + public static int strangePrinter3(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + dp[N - 1][N - 1] = 1; + for (int i = 0; i < N - 1; i++) { + dp[i][i] = 1; + dp[i][i + 1] = str[i] == str[i + 1] ? 1 : 2; + } + for (int L = N - 3; L >= 0; L--) { + for (int R = L + 2; R < N; R++) { + dp[L][R] = R - L + 1; + for (int k = L + 1; k <= R; k++) { + dp[L][R] = Math.min(dp[L][R], dp[L][k - 1] + dp[k][R] - (str[L] == str[k] ? 1 : 0)); + } + } + } + return dp[0][N - 1]; + } + +} diff --git a/体系学习班/class47/Code02_RestoreWays.java b/体系学习班/class47/Code02_RestoreWays.java new file mode 100644 index 0000000..c9c583a --- /dev/null +++ b/体系学习班/class47/Code02_RestoreWays.java @@ -0,0 +1,246 @@ +package class47; + +// 整型数组arr长度为n(3 <= n <= 10^4),最初每个数字是<=200的正数且满足如下条件: +// 1. 0位置的要求:arr[0]<=arr[1] +// 2. n-1位置的要求:arr[n-1]<=arr[n-2] +// 3. 中间i位置的要求:arr[i]<=max(arr[i-1],arr[i+1]) +// 但是在arr有些数字丢失了,比如k位置的数字之前是正数,丢失之后k位置的数字为0 +// 请你根据上述条件,计算可能有多少种不同的arr可以满足以上条件 +// 比如 [6,0,9] 只有还原成 [6,9,9]满足全部三个条件,所以返回1种,即[6,9,9]达标 +public class Code02_RestoreWays { + + public static int ways0(int[] arr) { + return process0(arr, 0); + } + + public static int process0(int[] arr, int index) { + if (index == arr.length) { + return isValid(arr) ? 1 : 0; + } else { + if (arr[index] != 0) { + return process0(arr, index + 1); + } else { + int ways = 0; + for (int v = 1; v < 201; v++) { + arr[index] = v; + ways += process0(arr, index + 1); + } + arr[index] = 0; + return ways; + } + } + } + + public static boolean isValid(int[] arr) { + if (arr[0] > arr[1]) { + return false; + } + if (arr[arr.length - 1] > arr[arr.length - 2]) { + return false; + } + for (int i = 1; i < arr.length - 1; i++) { + if (arr[i] > Math.max(arr[i - 1], arr[i + 1])) { + return false; + } + } + return true; + } + + public static int ways1(int[] arr) { + int N = arr.length; + if (arr[N - 1] != 0) { + return process1(arr, N - 1, arr[N - 1], 2); + } else { + int ways = 0; + for (int v = 1; v < 201; v++) { + ways += process1(arr, N - 1, v, 2); + } + return ways; + } + } + + // 如果i位置的数字变成了v, + // 并且arr[i]和arr[i+1]的关系为s, + // s==0,代表arr[i] < arr[i+1] 右大 + // s==1,代表arr[i] == arr[i+1] 右=当前 + // s==2,代表arr[i] > arr[i+1] 右小 + // 返回0...i范围上有多少种有效的转化方式? + public static int process1(int[] arr, int i, int v, int s) { + if (i == 0) { // 0...i 只剩一个数了,0...0 + return ((s == 0 || s == 1) && (arr[0] == 0 || v == arr[0])) ? 1 : 0; + } + // i > 0 + if (arr[i] != 0 && v != arr[i]) { + return 0; + } + // i>0 ,并且, i位置的数真的可以变成V, + int ways = 0; + if (s == 0 || s == 1) { // [i] -> V <= [i+1] + for (int pre = 1; pre < 201; pre++) { + ways += process1(arr, i - 1, pre, pre < v ? 0 : (pre == v ? 1 : 2)); + } + } else { // ? 当前 > 右 当前 <= max{左,右} + for (int pre = v; pre < 201; pre++) { + ways += process1(arr, i - 1, pre, pre == v ? 1 : 2); + } + } + return ways; + } + + public static int zuo(int[] arr, int i, int v, int s) { + if (i == 0) { // 0...i 只剩一个数了,0...0 + return ((s == 0 || s == 1) && (arr[0] == 0 || v == arr[0])) ? 1 : 0; + } + // i > 0 + if (arr[i] != 0 && v != arr[i]) { + return 0; + } + // i>0 ,并且, i位置的数真的可以变成V, + int ways = 0; + if (s == 0 || s == 1) { // [i] -> V <= [i+1] + for (int pre = 1; pre < v; pre++) { + ways += zuo(arr, i - 1, pre, 0); + } + } + ways += zuo(arr, i - 1, v, 1); + for (int pre = v + 1; pre < 201; pre++) { + ways += zuo(arr, i - 1, pre, 2); + } + return ways; + } + + public static int ways2(int[] arr) { + int N = arr.length; + int[][][] dp = new int[N][201][3]; + if (arr[0] != 0) { + dp[0][arr[0]][0] = 1; + dp[0][arr[0]][1] = 1; + } else { + for (int v = 1; v < 201; v++) { + dp[0][v][0] = 1; + dp[0][v][1] = 1; + } + } + for (int i = 1; i < N; i++) { + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + if (arr[i] == 0 || v == arr[i]) { + if (s == 0 || s == 1) { + for (int pre = 1; pre < v; pre++) { + dp[i][v][s] += dp[i - 1][pre][0]; + } + } + dp[i][v][s] += dp[i - 1][v][1]; + for (int pre = v + 1; pre < 201; pre++) { + dp[i][v][s] += dp[i - 1][pre][2]; + } + } + } + } + } + if (arr[N - 1] != 0) { + return dp[N - 1][arr[N - 1]][2]; + } else { + int ways = 0; + for (int v = 1; v < 201; v++) { + ways += dp[N - 1][v][2]; + } + return ways; + } + } + + public static int ways3(int[] arr) { + int N = arr.length; + int[][][] dp = new int[N][201][3]; + if (arr[0] != 0) { + dp[0][arr[0]][0] = 1; + dp[0][arr[0]][1] = 1; + } else { + for (int v = 1; v < 201; v++) { + dp[0][v][0] = 1; + dp[0][v][1] = 1; + } + } + int[][] presum = new int[201][3]; + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + presum[v][s] = presum[v - 1][s] + dp[0][v][s]; + } + } + for (int i = 1; i < N; i++) { + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + if (arr[i] == 0 || v == arr[i]) { + if (s == 0 || s == 1) { + dp[i][v][s] += sum(1, v - 1, 0, presum); + } + dp[i][v][s] += dp[i - 1][v][1]; + dp[i][v][s] += sum(v + 1, 200, 2, presum); + } + } + } + for (int v = 1; v < 201; v++) { + for (int s = 0; s < 3; s++) { + presum[v][s] = presum[v - 1][s] + dp[i][v][s]; + } + } + } + if (arr[N - 1] != 0) { + return dp[N - 1][arr[N - 1]][2]; + } else { + return sum(1, 200, 2, presum); + } + } + + public static int sum(int begin, int end, int relation, int[][] presum) { + return presum[end][relation] - presum[begin - 1][relation]; + } + + // for test + public static int[] generateRandomArray(int len) { + int[] ans = new int[len]; + for (int i = 0; i < ans.length; i++) { + if (Math.random() < 0.5) { + ans[i] = 0; + } else { + ans[i] = (int) (Math.random() * 200) + 1; + } + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + System.out.println("arr size : " + arr.length); + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 4; + int testTime = 15; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * len) + 2; + int[] arr = generateRandomArray(N); + int ans0 = ways0(arr); + int ans1 = ways1(arr); + int ans2 = ways2(arr); + int ans3 = ways3(arr); + if (ans0 != ans1 || ans2 != ans3 || ans0 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("功能测试结束"); + System.out.println("==========="); + int N = 100000; + int[] arr = generateRandomArray(N); + long begin = System.currentTimeMillis(); + ways3(arr); + long end = System.currentTimeMillis(); + System.out.println("run time : " + (end - begin) + " ms"); + } + +} diff --git a/体系学习班/class47/Code03_DinicAlgorithm.java b/体系学习班/class47/Code03_DinicAlgorithm.java new file mode 100644 index 0000000..efed284 --- /dev/null +++ b/体系学习班/class47/Code03_DinicAlgorithm.java @@ -0,0 +1,139 @@ +// 本题测试链接: +// https://lightoj.com/problem/internet-bandwidth +// 这是一道DinicAlgorithm算法的题 +// 把如下代码粘贴进网页所提供的java编译器环境中 +// 不需要修改任何内容可以直接通过 +// 请看网页上的题目描述并结合main函数的写法去了解这个模板的用法 + +package class47; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Scanner; + +public class Code03_DinicAlgorithm { + + public static class Edge { + public int from; + public int to; + public int available; + + public Edge(int a, int b, int c) { + from = a; + to = b; + available = c; + } + } + + public static class Dinic { + private int N; + private ArrayList> nexts; + private ArrayList edges; + private int[] depth; + private int[] cur; + + public Dinic(int nums) { + N = nums + 1; + nexts = new ArrayList<>(); + for (int i = 0; i <= N; i++) { + nexts.add(new ArrayList<>()); + } + edges = new ArrayList<>(); + depth = new int[N]; + cur = new int[N]; + } + + public void addEdge(int u, int v, int r) { + int m = edges.size(); + edges.add(new Edge(u, v, r)); + nexts.get(u).add(m); + edges.add(new Edge(v, u, 0)); + nexts.get(v).add(m + 1); + } + + public int maxFlow(int s, int t) { + int flow = 0; + while (bfs(s, t)) { + Arrays.fill(cur, 0); + flow += dfs(s, t, Integer.MAX_VALUE); + Arrays.fill(depth, 0); + } + return flow; + } + + private boolean bfs(int s, int t) { + LinkedList queue = new LinkedList<>(); + queue.addFirst(s); + boolean[] visited = new boolean[N]; + visited[s] = true; + while (!queue.isEmpty()) { + int u = queue.pollLast(); + for (int i = 0; i < nexts.get(u).size(); i++) { + Edge e = edges.get(nexts.get(u).get(i)); + int v = e.to; + if (!visited[v] && e.available > 0) { + visited[v] = true; + depth[v] = depth[u] + 1; + if (v == t) { + break; + } + queue.addFirst(v); + } + } + } + return visited[t]; + } + + // 当前来到了s点,s可变 + // 最终目标是t,t固定参数 + // r,收到的任务 + // 收集到的流,作为结果返回,ans <= r + private int dfs(int s, int t, int r) { + if (s == t || r == 0) { + return r; + } + int f = 0; + int flow = 0; + // s点从哪条边开始试 -> cur[s] + for (; cur[s] < nexts.get(s).size(); cur[s]++) { + int ei = nexts.get(s).get(cur[s]); + Edge e = edges.get(ei); + Edge o = edges.get(ei ^ 1); + if (depth[e.to] == depth[s] + 1 && (f = dfs(e.to, t, Math.min(e.available, r))) != 0) { + e.available -= f; + o.available += f; + flow += f; + r -= f; + if (r <= 0) { + break; + } + } + } + return flow; + } + } + + public static void main(String[] args) { + Scanner cin = new Scanner(System.in); + int cases = cin.nextInt(); + for (int i = 1; i <= cases; i++) { + int n = cin.nextInt(); + int s = cin.nextInt(); + int t = cin.nextInt(); + int m = cin.nextInt(); + Dinic dinic = new Dinic(n); + for (int j = 0; j < m; j++) { + int from = cin.nextInt(); + int to = cin.nextInt(); + int weight = cin.nextInt(); + dinic.addEdge(from, to, weight); + dinic.addEdge(to, from, weight); + } + int ans = dinic.maxFlow(s, t); + System.out.println("Case " + i + ": " + ans); + } + cin.close(); + } + +} \ No newline at end of file diff --git a/大厂刷题班/class01/Code01_CordCoverMaxPoint.java b/大厂刷题班/class01/Code01_CordCoverMaxPoint.java new file mode 100644 index 0000000..a95eaf4 --- /dev/null +++ b/大厂刷题班/class01/Code01_CordCoverMaxPoint.java @@ -0,0 +1,87 @@ +package class01; + +import java.util.Arrays; + +public class Code01_CordCoverMaxPoint { + + public static int maxPoint1(int[] arr, int L) { + int res = 1; + for (int i = 0; i < arr.length; i++) { + int nearest = nearestIndex(arr, i, arr[i] - L); + res = Math.max(res, i - nearest + 1); + } + return res; + } + + public static int nearestIndex(int[] arr, int R, int value) { + int L = 0; + int index = R; + while (L <= R) { + int mid = L + ((R - L) >> 1); + if (arr[mid] >= value) { + index = mid; + R = mid - 1; + } else { + L = mid + 1; + } + } + return index; + } + + public static int maxPoint2(int[] arr, int L) { + int left = 0; + int right = 0; + int N = arr.length; + int max = 0; + while (left < N) { + while (right < N && arr[right] - arr[left] <= L) { + right++; + } + max = Math.max(max, right - (left++)); + } + return max; + } + + // for test + public static int test(int[] arr, int L) { + int max = 0; + for (int i = 0; i < arr.length; i++) { + int pre = i - 1; + while (pre >= 0 && arr[i] - arr[pre] <= L) { + pre--; + } + max = Math.max(max, i - pre); + } + return max; + } + + // for test + public static int[] generateArray(int len, int max) { + int[] ans = new int[(int) (Math.random() * len) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * max); + } + Arrays.sort(ans); + return ans; + } + + public static void main(String[] args) { + int len = 100; + int max = 1000; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int L = (int) (Math.random() * max); + int[] arr = generateArray(len, max); + int ans1 = maxPoint1(arr, L); + int ans2 = maxPoint2(arr, L); + int ans3 = test(arr, L); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("oops!"); + break; + } + } + + } + +} diff --git a/大厂刷题班/class01/Code02_CountFiles.java b/大厂刷题班/class01/Code02_CountFiles.java new file mode 100644 index 0000000..f09d132 --- /dev/null +++ b/大厂刷题班/class01/Code02_CountFiles.java @@ -0,0 +1,40 @@ +package class01; + +import java.io.File; +import java.util.Stack; + +public class Code02_CountFiles { + + // 注意这个函数也会统计隐藏文件 + public static int getFileNumber(String folderPath) { + File root = new File(folderPath); + if (!root.isDirectory() && !root.isFile()) { + return 0; + } + if (root.isFile()) { + return 1; + } + Stack stack = new Stack<>(); + stack.add(root); + int files = 0; + while (!stack.isEmpty()) { + File folder = stack.pop(); + for (File next : folder.listFiles()) { + if (next.isFile()) { + files++; + } + if (next.isDirectory()) { + stack.push(next); + } + } + } + return files; + } + + public static void main(String[] args) { + // 你可以自己更改目录 + String path = "/Users/zuochengyun/Desktop/"; + System.out.println(getFileNumber(path)); + } + +} diff --git a/大厂刷题班/class01/Code03_Near2Power.java b/大厂刷题班/class01/Code03_Near2Power.java new file mode 100644 index 0000000..43aca3b --- /dev/null +++ b/大厂刷题班/class01/Code03_Near2Power.java @@ -0,0 +1,22 @@ +package class01; + +public class Code03_Near2Power { + + // 已知n是正数 + // 返回大于等于,且最接近n的,2的某次方的值 + public static final int tableSizeFor(int n) { + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : n + 1; + } + + public static void main(String[] args) { + int cap = 120; + System.out.println(tableSizeFor(cap)); + } + +} diff --git a/大厂刷题班/class01/Code04_MinSwapStep.java b/大厂刷题班/class01/Code04_MinSwapStep.java new file mode 100644 index 0000000..d659451 --- /dev/null +++ b/大厂刷题班/class01/Code04_MinSwapStep.java @@ -0,0 +1,74 @@ +package class01; + +public class Code04_MinSwapStep { + + // 一个数组中只有两种字符'G'和'B', + // 可以让所有的G都放在左侧,所有的B都放在右侧 + // 或者可以让所有的G都放在右侧,所有的B都放在左侧 + // 但是只能在相邻字符之间进行交换操作,请问请问至少需要交换几次, + public static int minSteps1(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int step1 = 0; + int gi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'G') { + step1 += i - (gi++); + } + } + int step2 = 0; + int bi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'B') { + step2 += i - (bi++); + } + } + return Math.min(step1, step2); + } + + // 可以让G在左,或者在右 + public static int minSteps2(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int step1 = 0; + int step2 = 0; + int gi = 0; + int bi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'G') { // 当前的G,去左边 方案1 + step1 += i - (gi++); + } else {// 当前的B,去左边 方案2 + step2 += i - (bi++); + } + } + return Math.min(step1, step2); + } + + // 为了测试 + public static String randomString(int maxLen) { + char[] str = new char[(int) (Math.random() * maxLen)]; + for (int i = 0; i < str.length; i++) { + str[i] = Math.random() < 0.5 ? 'G' : 'B'; + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int maxLen = 100; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + String str = randomString(maxLen); + int ans1 = minSteps1(str); + int ans2 = minSteps2(str); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } +} \ No newline at end of file diff --git a/大厂刷题班/class01/Code05_LongestIncreasingPath.java b/大厂刷题班/class01/Code05_LongestIncreasingPath.java new file mode 100644 index 0000000..682f6b5 --- /dev/null +++ b/大厂刷题班/class01/Code05_LongestIncreasingPath.java @@ -0,0 +1,54 @@ +package class01; + +public class Code05_LongestIncreasingPath { + + public static int longestIncreasingPath1(int[][] matrix) { + int ans = 0; + int N = matrix.length; + int M = matrix[0].length; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + ans = Math.max(ans, process1(matrix, i, j)); + } + } + return ans; + } + + // 从m[i][j]开始走,走出来的最长递增链,返回! + public static int process1(int[][] m, int i, int j) { + int up = i > 0 && m[i][j] < m[i - 1][j] ? process1(m, i - 1, j) : 0; + int down = i < (m.length - 1) && m[i][j] < m[i + 1][j] ? process1(m, i + 1, j) : 0; + int left = j > 0 && m[i][j] < m[i][j - 1] ? process1(m, i, j - 1) : 0; + int right = j < (m[0].length - 1) && m[i][j] < m[i][j + 1] ? process1(m, i, j + 1) : 0; + return Math.max(Math.max(up, down), Math.max(left, right)) + 1; + } + + public static int longestIncreasingPath2(int[][] matrix) { + int ans = 0; + int N = matrix.length; + int M = matrix[0].length; + int[][] dp = new int[N][M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + ans = Math.max(ans, process2(matrix, i, j, dp)); + } + } + return ans; + } + + // 从m[i][j]开始走,走出来的最长递增链,返回! + public static int process2(int[][] m, int i, int j, int[][] dp) { + if (dp[i][j] != 0) { + return dp[i][j]; + } + // (i,j)不越界 + int up = i > 0 && m[i][j] < m[i - 1][j] ? process2(m, i - 1, j, dp) : 0; + int down = i < (m.length - 1) && m[i][j] < m[i + 1][j] ? process2(m, i + 1, j, dp) : 0; + int left = j > 0 && m[i][j] < m[i][j - 1] ? process2(m, i, j - 1, dp) : 0; + int right = j < (m[0].length - 1) && m[i][j] < m[i][j + 1] ? process2(m, i, j + 1, dp) : 0; + int ans = Math.max(Math.max(up, down), Math.max(left, right)) + 1; + dp[i][j] = ans; + return ans; + } + +} diff --git a/大厂刷题班/class01/Code06_AOE.java b/大厂刷题班/class01/Code06_AOE.java new file mode 100644 index 0000000..6b8e72c --- /dev/null +++ b/大厂刷题班/class01/Code06_AOE.java @@ -0,0 +1,304 @@ +package class01; + +import java.util.Arrays; + +/* + * 给定两个数组x和hp,长度都是N。 + * x数组一定是有序的,x[i]表示i号怪兽在x轴上的位置 + * hp数组不要求有序,hp[i]表示i号怪兽的血量 + * 为了方便起见,可以认为x数组和hp数组中没有负数。 + * 再给定一个正数range,表示如果法师释放技能的范围长度(直径!) + * 被打到的每只怪兽损失1点血量。 + * 返回要把所有怪兽血量清空,至少需要释放多少次aoe技能? + * 三个参数:int[] x, int[] hp, int range + * 返回:int 次数 + * */ +public class Code06_AOE { + + // 纯暴力解法 + // 太容易超时 + // 只能小样本量使用 + public static int minAoe1(int[] x, int[] hp, int range) { + boolean allClear = true; + for (int i = 0; i < hp.length; i++) { + if (hp[i] > 0) { + allClear = false; + break; + } + } + if (allClear) { + return 0; + } else { + int ans = Integer.MAX_VALUE; + for (int left = 0; left < x.length; left++) { + if (hasHp(x, hp, left, range)) { + minusOneHp(x, hp, left, range); + ans = Math.min(ans, 1 + minAoe1(x, hp, range)); + addOneHp(x, hp, left, range); + } + } + return ans; + } + } + + public static boolean hasHp(int[] x, int[] hp, int left, int range) { + for (int index = left; index < x.length && x[index] - x[left] <= range; index++) { + if (hp[index] > 0) { + return true; + } + } + return false; + } + + public static void minusOneHp(int[] x, int[] hp, int left, int range) { + for (int index = left; index < x.length && x[index] - x[left] <= range; index++) { + hp[index]--; + } + } + + public static void addOneHp(int[] x, int[] hp, int left, int range) { + for (int index = left; index < x.length && x[index] - x[left] <= range; index++) { + hp[index]++; + } + } + + // 为了验证 + // 不用线段树,但是贪心的思路,和课上一样 + // 1) 总是用技能的最左边缘刮死当前最左侧的没死的怪物 + // 2) 然后向右找下一个没死的怪物,重复步骤1) + public static int minAoe2(int[] x, int[] hp, int range) { + // 举个例子: + // 如果怪兽情况如下, + // 怪兽所在,x数组 : 2 3 5 6 7 9 + // 怪兽血量,hp数组 : 2 4 1 2 3 1 + // 怪兽编号 : 0 1 2 3 4 5 + // 技能直径,range = 2 + int n = x.length; + int[] cover = new int[n]; + // 首先求cover数组, + // 如果技能左边界就在0号怪兽,那么技能到2号怪兽就覆盖不到了 + // 所以cover[0] = 2; + // 如果技能左边界就在1号怪兽,那么技能到3号怪兽就覆盖不到了 + // 所以cover[1] = 3; + // 如果技能左边界就在2号怪兽,那么技能到5号怪兽就覆盖不到了 + // 所以cover[2] = 5; + // 如果技能左边界就在3号怪兽,那么技能到5号怪兽就覆盖不到了 + // 所以cover[3] = 5; + // 如果技能左边界就在4号怪兽,那么技能到6号怪兽(越界位置)就覆盖不到了 + // 所以cover[4] = 6(越界位置); + // 如果技能左边界就在5号怪兽,那么技能到6号怪兽(越界位置)就覆盖不到了 + // 所以cover[5] = 6(越界位置); + // 综上: + // 如果怪兽情况如下, + // 怪兽所在,x数组 : 2 3 5 6 7 9 + // 怪兽血量,hp数组 : 2 4 1 2 3 1 + // 怪兽编号 : 0 1 2 3 4 5 + // cover数组情况 : 2 3 5 5 6 6 + // 技能直径,range = 2 + // cover[i] = j,表示如果技能左边界在i怪兽,那么技能会影响i...j-1号所有的怪兽 + // 就是如下的for循环,在求cover数组 + int r = 0; + for (int i = 0; i < n; i++) { + while (r < n && x[r] - x[i] <= range) { + r++; + } + cover[i] = r; + } + int ans = 0; + for (int i = 0; i < n; i++) { + // 假设来到i号怪兽了 + // 如果i号怪兽的血量>0,说明i号怪兽没死 + // 根据我们课上讲的贪心: + // 我们要让技能的左边界,刮死当前的i号怪兽 + // 这样能够让技能尽可能的往右释放,去尽可能的打掉右侧的怪兽 + // 此时cover[i],正好的告诉我们,技能影响多大范围。 + // 比如当前来到100号怪兽,血量30 + // 假设cover[100] == 200 + // 说明,技能左边界在100位置,可以影响100号到199号怪兽的血量。 + // 为了打死100号怪兽,我们释放技能30次, + // 释放的时候,100号到199号怪兽都掉血,30点 + // 然后继续向右寻找没死的怪兽,像课上讲的一样 + if (hp[i] > 0) { + int minus = hp[i]; + for (int index = i; index < cover[i]; index++) { + hp[index] -= minus; + } + ans += minus; + } + } + return ans; + } + + // 正式方法 + // 关键点就是: + // 1) 线段树 + // 2) 总是用技能的最左边缘刮死当前最左侧的没死的怪物 + // 3) 然后向右找下一个没死的怪物,重复步骤2) + public static int minAoe3(int[] x, int[] hp, int range) { + int n = x.length; + int[] cover = new int[n]; + int r = 0; + // cover[i] : 如果i位置是技能的最左侧,技能往右的range范围内,最右影响到哪 + for (int i = 0; i < n; i++) { + while (r < n && x[r] - x[i] <= range) { + r++; + } + cover[i] = r - 1; + } + SegmentTree st = new SegmentTree(hp); + st.build(1, n, 1); + int ans = 0; + for (int i = 1; i <= n; i++) { + int leftHP = st.query(i, i, 1, n, 1); + if (leftHP > 0) { + ans += leftHP; + st.add(i, cover[i - 1] + 1, -leftHP, 1, n, 1); + } + } + return ans; + } + + public static class SegmentTree { + private int MAXN; + private int[] arr; + private int[] sum; + private int[] lazy; + + public SegmentTree(int[] origin) { + MAXN = origin.length + 1; + arr = new int[MAXN]; + for (int i = 1; i < MAXN; i++) { + arr[i] = origin[i - 1]; + } + sum = new int[MAXN << 2]; + lazy = new int[MAXN << 2]; + } + + private void pushUp(int rt) { + sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; + } + + private void pushDown(int rt, int ln, int rn) { + if (lazy[rt] != 0) { + lazy[rt << 1] += lazy[rt]; + sum[rt << 1] += lazy[rt] * ln; + lazy[rt << 1 | 1] += lazy[rt]; + sum[rt << 1 | 1] += lazy[rt] * rn; + lazy[rt] = 0; + } + } + + public void build(int l, int r, int rt) { + if (l == r) { + sum[rt] = arr[l]; + return; + } + int mid = (l + r) >> 1; + build(l, mid, rt << 1); + build(mid + 1, r, rt << 1 | 1); + pushUp(rt); + } + + public void add(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + sum[rt] += C * (r - l + 1); + lazy[rt] += C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + add(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + add(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return sum[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans += query(L, R, l, mid, rt << 1); + } + if (R > mid) { + ans += query(L, R, mid + 1, r, rt << 1 | 1); + } + return ans; + } + + } + + // 为了测试 + public static int[] randomArray(int n, int valueMax) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * valueMax) + 1; + } + return ans; + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + int N = arr.length; + int[] ans = new int[N]; + for (int i = 0; i < N; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 50; + int X = 500; + int H = 60; + int R = 10; + int testTime = 50000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N) + 1; + int[] x2 = randomArray(len, X); + Arrays.sort(x2); + int[] hp2 = randomArray(len, H); + int[] x3 = copyArray(x2); + int[] hp3 = copyArray(hp2); + int range = (int) (Math.random() * R) + 1; + int ans2 = minAoe2(x2, hp2, range); + int ans3 = minAoe3(x3, hp3, range); + if (ans2 != ans3) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + + N = 500000; + long start; + long end; + int[] x2 = randomArray(N, N); + Arrays.sort(x2); + int[] hp2 = new int[N]; + for (int i = 0; i < N; i++) { + hp2[i] = i * 5 + 10; + } + int[] x3 = copyArray(x2); + int[] hp3 = copyArray(hp2); + int range = 10000; + + start = System.currentTimeMillis(); + System.out.println(minAoe2(x2, hp2, range)); + end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + + start = System.currentTimeMillis(); + System.out.println(minAoe3(x3, hp3, range)); + end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + " 毫秒"); + } + +} diff --git a/大厂刷题班/class01/Code07_TargetSum.java b/大厂刷题班/class01/Code07_TargetSum.java new file mode 100644 index 0000000..a91f560 --- /dev/null +++ b/大厂刷题班/class01/Code07_TargetSum.java @@ -0,0 +1,127 @@ +package class01; + +import java.util.HashMap; + +// leetcode 494题 +public class Code07_TargetSum { + + public static int findTargetSumWays1(int[] arr, int s) { + return process1(arr, 0, s); + } + + // 可以自由使用arr[index....]所有的数字! + // 搞出rest这个数,方法数是多少?返回 + // index == 7 rest = 13 + // map "7_13" 256 + public static int process1(int[] arr, int index, int rest) { + if (index == arr.length) { // 没数了! + return rest == 0 ? 1 : 0; + } + // 还有数!arr[index] arr[index+1 ... ] + return process1(arr, index + 1, rest - arr[index]) + process1(arr, index + 1, rest + arr[index]); + } + + public static int findTargetSumWays2(int[] arr, int s) { + return process2(arr, 0, s, new HashMap<>()); + } + + public static int process2(int[] arr, int index, int rest, HashMap> dp) { + if (dp.containsKey(index) && dp.get(index).containsKey(rest)) { + return dp.get(index).get(rest); + } + // 否则,没命中! + int ans = 0; + if (index == arr.length) { + ans = rest == 0 ? 1 : 0; + } else { + ans = process2(arr, index + 1, rest - arr[index], dp) + process2(arr, index + 1, rest + arr[index], dp); + } + if (!dp.containsKey(index)) { + dp.put(index, new HashMap<>()); + } + dp.get(index).put(rest, ans); + return ans; + } + + // 优化点一 : + // 你可以认为arr中都是非负数 + // 因为即便是arr中有负数,比如[3,-4,2] + // 因为你能在每个数前面用+或者-号 + // 所以[3,-4,2]其实和[3,4,2]达成一样的效果 + // 那么我们就全把arr变成非负数,不会影响结果的 + // 优化点二 : + // 如果arr都是非负数,并且所有数的累加和是sum + // 那么如果target> 1); + } + + // 求非负数组nums有多少个子集,累加和是s + // 二维动态规划 + // 不用空间压缩 + public static int subset1(int[] nums, int s) { + if (s < 0) { + return 0; + } + int n = nums.length; + // dp[i][j] : nums前缀长度为i的所有子集,有多少累加和是j? + int[][] dp = new int[n + 1][s + 1]; + // nums前缀长度为0的所有子集,有多少累加和是0?一个:空集 + dp[0][0] = 1; + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= s; j++) { + dp[i][j] = dp[i - 1][j]; + if (j - nums[i - 1] >= 0) { + dp[i][j] += dp[i - 1][j - nums[i - 1]]; + } + } + } + return dp[n][s]; + } + + // 求非负数组nums有多少个子集,累加和是s + // 二维动态规划 + // 用空间压缩: + // 核心就是for循环里面的:for (int i = s; i >= n; i--) { + // 为啥不枚举所有可能的累加和?只枚举 n...s 这些累加和? + // 因为如果 i - n < 0,dp[i]怎么更新?和上一步的dp[i]一样!所以不用更新 + // 如果 i - n >= 0,dp[i]怎么更新?上一步的dp[i] + 上一步dp[i - n]的值,这才需要更新 + public static int subset2(int[] nums, int s) { + if (s < 0) { + return 0; + } + int[] dp = new int[s + 1]; + dp[0] = 1; + for (int n : nums) { + for (int i = s; i >= n; i--) { + dp[i] += dp[i - n]; + } + } + return dp[s]; + } + +} diff --git a/大厂刷题班/class02/Code01_ChooseWork.java b/大厂刷题班/class02/Code01_ChooseWork.java new file mode 100644 index 0000000..4630a33 --- /dev/null +++ b/大厂刷题班/class02/Code01_ChooseWork.java @@ -0,0 +1,48 @@ +package class02; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.TreeMap; + +public class Code01_ChooseWork { + + public static class Job { + public int money; + public int hard; + + public Job(int m, int h) { + money = m; + hard = h; + } + } + + public static class JobComparator implements Comparator { + @Override + public int compare(Job o1, Job o2) { + return o1.hard != o2.hard ? (o1.hard - o2.hard) : (o2.money - o1.money); + } + } + + public static int[] getMoneys(Job[] job, int[] ability) { + Arrays.sort(job, new JobComparator()); + // key : 难度 value:报酬 + TreeMap map = new TreeMap<>(); + map.put(job[0].hard, job[0].money); + // pre : 上一份进入map的工作 + Job pre = job[0]; + for (int i = 1; i < job.length; i++) { + if (job[i].hard != pre.hard && job[i].money > pre.money) { + pre = job[i]; + map.put(pre.hard, pre.money); + } + } + int[] ans = new int[ability.length]; + for (int i = 0; i < ability.length; i++) { + // ability[i] 当前人的能力 <= ability[i] 且离它最近的 + Integer key = map.floorKey(ability[i]); + ans[i] = key != null ? map.get(key) : 0; + } + return ans; + } + +} diff --git a/大厂刷题班/class02/Code02_Cola.java b/大厂刷题班/class02/Code02_Cola.java new file mode 100644 index 0000000..c41d0fb --- /dev/null +++ b/大厂刷题班/class02/Code02_Cola.java @@ -0,0 +1,151 @@ +package class02; + +public class Code02_Cola { + /* + * 买饮料 时间限制: 3000MS 内存限制: 589824KB 题目描述: + * 游游今年就要毕业了,和同学们在携程上定制了日本毕业旅行。愉快的一天行程结束后大家回到了酒店房间,这时候同学们都很口渴, + * 石头剪刀布选出游游去楼下的自动贩卖机给大家买可乐。 贩卖机只支持硬币支付,且收退都只支持10 ,50,100 + * 三种面额。一次购买行为只能出一瓶可乐,且每次购买后总是找零最小枚数的硬币。(例如投入100圆,可乐30圆,则找零50圆一枚,10圆两枚) + * 游游需要购买的可乐数量是 m,其中手头拥有的 10,50,100 面额硬币的枚数分别是 a,b,c,可乐的价格是x(x是10的倍数)。 + * 如果游游优先使用大面额购买且钱是够的情况下,请计算出需要投入硬币次数? 输入描述 依次输入, 需要可乐的数量为 m 10元的张数为 a 50元的张数为 b + * 100元的张树为 c 1瓶可乐的价格为 x 输出描述 输出当前金额下需要投入硬币的次数 + * 例如需要购买2瓶可乐,每瓶可乐250圆,手里有100圆3枚,50圆4枚,10圆1枚。 购买第1瓶投递100圆3枚,找50圆 购买第2瓶投递50圆5枚 + * 所以是总共需要操作8次金额投递操作 样例输入 2 1 4 3 250 样例输出 8 + */ + + // 暴力尝试,为了验证正式方法而已 + public static int right(int m, int a, int b, int c, int x) { + int[] qian = { 100, 50, 10 }; + int[] zhang = { c, b, a }; + int puts = 0; + while (m != 0) { + int cur = buy(qian, zhang, x); + if (cur == -1) { + return -1; + } + puts += cur; + m--; + } + return puts; + } + + public static int buy(int[] qian, int[] zhang, int rest) { + int first = -1; + for (int i = 0; i < 3; i++) { + if (zhang[i] != 0) { + first = i; + break; + } + } + if (first == -1) { + return -1; + } + if (qian[first] >= rest) { + zhang[first]--; + giveRest(qian, zhang, first + 1, qian[first] - rest, 1); + return 1; + } else { + zhang[first]--; + int next = buy(qian, zhang, rest - qian[first]); + if (next == -1) { + return -1; + } + return 1 + next; + } + } + + // 正式的方法 + // 要买的可乐数量,m + // 100元有a张 + // 50元有b张 + // 10元有c张 + // 可乐单价x + public static int putTimes(int m, int a, int b, int c, int x) { + // 0 1 2 + int[] qian = { 100, 50, 10 }; + int[] zhang = { c, b, a }; + // 总共需要多少次投币 + int puts = 0; + // 之前面值的钱还剩下多少总钱数 + int preQianRest = 0; + // 之前面值的钱还剩下多少总张数 + int preQianZhang = 0; + for (int i = 0; i < 3 && m != 0; i++) { + // 要用之前剩下的钱、当前面值的钱,共同买第一瓶可乐 + // 之前的面值剩下多少钱,是preQianRest + // 之前的面值剩下多少张,是preQianZhang + // 之所以之前的面值会剩下来,一定是剩下的钱,一直攒不出一瓶可乐的单价 + // 当前的面值付出一些钱+之前剩下的钱,此时有可能凑出一瓶可乐来 + // 那么当前面值参与搞定第一瓶可乐,需要掏出多少张呢?就是curQianFirstBuyZhang + int curQianFirstBuyZhang = (x - preQianRest + qian[i] - 1) / qian[i]; + if (zhang[i] >= curQianFirstBuyZhang) { // 如果之前的钱和当前面值的钱,能凑出第一瓶可乐 + // 凑出来了一瓶可乐也可能存在找钱的情况, + giveRest(qian, zhang, i + 1, (preQianRest + qian[i] * curQianFirstBuyZhang) - x, 1); + puts += curQianFirstBuyZhang + preQianZhang; + zhang[i] -= curQianFirstBuyZhang; + m--; + } else { // 如果之前的钱和当前面值的钱,不能凑出第一瓶可乐 + preQianRest += qian[i] * zhang[i]; + preQianZhang += zhang[i]; + continue; + } + // 凑出第一瓶可乐之后,当前的面值有可能能继续买更多的可乐 + // 以下过程就是后续的可乐怎么用当前面值的钱来买 + // 用当前面值的钱,买一瓶可乐需要几张 + int curQianBuyOneColaZhang = (x + qian[i] - 1) / qian[i]; + // 用当前面值的钱,一共可以搞定几瓶可乐 + int curQianBuyColas = Math.min(zhang[i] / curQianBuyOneColaZhang, m); + // 用当前面值的钱,每搞定一瓶可乐,收货机会吐出多少零钱 + int oneTimeRest = qian[i] * curQianBuyOneColaZhang - x; + // 每次买一瓶可乐,吐出的找零总钱数是oneTimeRest + // 一共买的可乐数是curQianBuyColas,所以把零钱去提升后面几种面值的硬币数, + // 就是giveRest的含义 + giveRest(qian, zhang, i + 1, oneTimeRest, curQianBuyColas); + // 当前面值去搞定可乐这件事,一共投了几次币 + puts += curQianBuyOneColaZhang * curQianBuyColas; + // 还剩下多少瓶可乐需要去搞定,继续用后面的面值搞定去吧 + m -= curQianBuyColas; + // 当前面值可能剩下若干张,要参与到后续买可乐的过程中去, + // 所以要更新preQianRest和preQianZhang + zhang[i] -= curQianBuyOneColaZhang * curQianBuyColas; + preQianRest = qian[i] * zhang[i]; + preQianZhang = zhang[i]; + } + return m == 0 ? puts : -1; + } + + public static void giveRest(int[] qian, int[] zhang, int i, int oneTimeRest, int times) { + for (; i < 3; i++) { + zhang[i] += (oneTimeRest / qian[i]) * times; + oneTimeRest %= qian[i]; + } + } + + public static void main(String[] args) { + int testTime = 1000; + int zhangMax = 10; + int colaMax = 10; + int priceMax = 20; + System.out.println("如果错误会打印错误数据,否则就是正确"); + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int m = (int) (Math.random() * colaMax); + int a = (int) (Math.random() * zhangMax); + int b = (int) (Math.random() * zhangMax); + int c = (int) (Math.random() * zhangMax); + int x = ((int) (Math.random() * priceMax) + 1) * 10; + int ans1 = putTimes(m, a, b, c, x); + int ans2 = right(m, a, b, c, x); + if (ans1 != ans2) { + System.out.println("int m = " + m + ";"); + System.out.println("int a = " + a + ";"); + System.out.println("int b = " + b + ";"); + System.out.println("int c = " + c + ";"); + System.out.println("int x = " + x + ";"); + break; + } + } + System.out.println("test end"); + } + +} diff --git a/大厂刷题班/class02/Code03_ReceiveAndPrintOrderLine.java b/大厂刷题班/class02/Code03_ReceiveAndPrintOrderLine.java new file mode 100644 index 0000000..7db9ba3 --- /dev/null +++ b/大厂刷题班/class02/Code03_ReceiveAndPrintOrderLine.java @@ -0,0 +1,89 @@ +package class02; + +import java.util.HashMap; + +public class Code03_ReceiveAndPrintOrderLine { + + public static class Node { + public String info; + public Node next; + + public Node(String str) { + info = str; + } + } + + public static class MessageBox { + private HashMap headMap; + private HashMap tailMap; + private int waitPoint; + + public MessageBox() { + headMap = new HashMap(); + tailMap = new HashMap(); + waitPoint = 1; + } + + // 消息的编号,info消息的内容, 消息一定从1开始 + public void receive(int num, String info) { + if (num < 1) { + return; + } + Node cur = new Node(info); + // num~num + headMap.put(num, cur); + tailMap.put(num, cur); + // 建立了num~num这个连续区间的头和尾 + // 查询有没有某个连续区间以num-1结尾 + if (tailMap.containsKey(num - 1)) { + tailMap.get(num - 1).next = cur; + tailMap.remove(num - 1); + headMap.remove(num); + } + // 查询有没有某个连续区间以num+1开头的 + if (headMap.containsKey(num + 1)) { + cur.next = headMap.get(num + 1); + tailMap.remove(num); + headMap.remove(num + 1); + } + if (num == waitPoint) { + print(); + } + } + + private void print() { + Node node = headMap.get(waitPoint); + headMap.remove(waitPoint); + while (node != null) { + System.out.print(node.info + " "); + node = node.next; + waitPoint++; + } + tailMap.remove(waitPoint-1); + System.out.println(); + } + + } + + public static void main(String[] args) { + // MessageBox only receive 1~N + MessageBox box = new MessageBox(); + // 1.... + System.out.println("这是2来到的时候"); + box.receive(2,"B"); // - 2" + System.out.println("这是1来到的时候"); + box.receive(1,"A"); // 1 2 -> print, trigger is 1 + box.receive(4,"D"); // - 4 + box.receive(5,"E"); // - 4 5 + box.receive(7,"G"); // - 4 5 - 7 + box.receive(8,"H"); // - 4 5 - 7 8 + box.receive(6,"F"); // - 4 5 6 7 8 + box.receive(3,"C"); // 3 4 5 6 7 8 -> print, trigger is 3 + box.receive(9,"I"); // 9 -> print, trigger is 9 + box.receive(10,"J"); // 10 -> print, trigger is 10 + box.receive(12,"L"); // - 12 + box.receive(13,"M"); // - 12 13 + box.receive(11,"K"); // 11 12 13 -> print, trigger is 11 + + } +} diff --git a/大厂刷题班/class02/Code04_Drive.java b/大厂刷题班/class02/Code04_Drive.java new file mode 100644 index 0000000..a5a3a07 --- /dev/null +++ b/大厂刷题班/class02/Code04_Drive.java @@ -0,0 +1,116 @@ +package class02; + +import java.util.Arrays; + +public class Code04_Drive { + + // 课上的现场版本 + // income -> N * 2 的矩阵 N是偶数! + // 0 [9, 13] + // 1 [45,60] + public static int maxMoney1(int[][] income) { + if (income == null || income.length < 2 || (income.length & 1) != 0) { + return 0; + } + int N = income.length; // 司机数量一定是偶数,所以才能平分,A N /2 B N/2 + int M = N >> 1; // M = N / 2 要去A区域的人 + return process1(income, 0, M); + } + + // index.....所有的司机,往A和B区域分配! + // A区域还有rest个名额! + // 返回把index...司机,分配完,并且最终A和B区域同样多的情况下,index...这些司机,整体收入最大是多少! + public static int process1(int[][] income, int index, int rest) { + if (index == income.length) { + return 0; + } + // 还剩下司机! + if (income.length - index == rest) { + return income[index][0] + process1(income, index + 1, rest - 1); + } + if (rest == 0) { + return income[index][1] + process1(income, index + 1, rest); + } + // 当前司机,可以去A,或者去B + int p1 = income[index][0] + process1(income, index + 1, rest - 1); + int p2 = income[index][1] + process1(income, index + 1, rest); + return Math.max(p1, p2); + } + + // 严格位置依赖的动态规划版本 + public static int maxMoney2(int[][] income) { + int N = income.length; + int M = N >> 1; + int[][] dp = new int[N + 1][M + 1]; + for (int i = N - 1; i >= 0; i--) { + for (int j = 0; j <= M; j++) { + if (N - i == j) { + dp[i][j] = income[i][0] + dp[i + 1][j - 1]; + } else if (j == 0) { + dp[i][j] = income[i][1] + dp[i + 1][j]; + } else { + int p1 = income[i][0] + dp[i + 1][j - 1]; + int p2 = income[i][1] + dp[i + 1][j]; + dp[i][j] = Math.max(p1, p2); + } + } + } + return dp[0][M]; + } + + // 这题有贪心策略 : + // 假设一共有10个司机,思路是先让所有司机去A,得到一个总收益 + // 然后看看哪5个司机改换门庭(去B),可以获得最大的额外收益 + // 这道题有贪心策略,打了我的脸 + // 但是我课上提到的技巧请大家重视 + // 根据数据量猜解法可以省去大量的多余分析,节省时间 + // 这里感谢卢圣文同学 + public static int maxMoney3(int[][] income) { + int N = income.length; + int[] arr = new int[N]; + int sum = 0; + for (int i = 0; i < N; i++) { + arr[i] = income[i][1] - income[i][0]; + sum += income[i][0]; + } + Arrays.sort(arr); + int M = N >> 1; + for (int i = N - 1; i >= M; i--) { + sum += arr[i]; + } + return sum; + } + + // 返回随机len*2大小的正数矩阵 + // 值在0~value-1之间 + public static int[][] randomMatrix(int len, int value) { + int[][] ans = new int[len << 1][2]; + for (int i = 0; i < ans.length; i++) { + ans[i][0] = (int) (Math.random() * value); + ans[i][1] = (int) (Math.random() * value); + } + return ans; + } + + public static void main(String[] args) { + int N = 10; + int value = 100; + int testTime = 500; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N) + 1; + int[][] matrix = randomMatrix(len, value); + int ans1 = maxMoney1(matrix); + int ans2 = maxMoney2(matrix); + int ans3 = maxMoney3(matrix); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class02/Code05_SetAll.java b/大厂刷题班/class02/Code05_SetAll.java new file mode 100644 index 0000000..329964a --- /dev/null +++ b/大厂刷题班/class02/Code05_SetAll.java @@ -0,0 +1,48 @@ +package class02; + +import java.util.HashMap; + +public class Code05_SetAll { + + public static class MyValue { + public V value; + public long time; + + public MyValue(V v, long t) { + value = v; + time = t; + } + } + + public static class MyHashMap { + private HashMap> map; + private long time; + private MyValue setAll; + + public MyHashMap() { + map = new HashMap<>(); + time = 0; + setAll = new MyValue(null, -1); + } + + public void put(K key, V value) { + map.put(key, new MyValue(value, time++)); + } + + public void setAll(V value) { + setAll = new MyValue(value, time++); + } + + public V get(K key) { + if (!map.containsKey(key)) { + return null; + } + if (map.get(key).time > setAll.time) { + return map.get(key).value; + } else { + return setAll.value; + } + } + } + +} diff --git a/大厂刷题班/class02/Code06_MinLengthForSort.java b/大厂刷题班/class02/Code06_MinLengthForSort.java new file mode 100644 index 0000000..5c3f74b --- /dev/null +++ b/大厂刷题班/class02/Code06_MinLengthForSort.java @@ -0,0 +1,30 @@ +package class02; + +// 本题测试链接 : https://leetcode.com/problems/shortest-unsorted-continuous-subarray/ +public class Code06_MinLengthForSort { + + public static int findUnsortedSubarray(int[] nums) { + if (nums == null || nums.length < 2) { + return 0; + } + int N = nums.length; + int right = -1; + int max = Integer.MIN_VALUE; + for (int i = 0; i < N; i++) { + if (max > nums[i]) { + right = i; + } + max = Math.max(max, nums[i]); + } + int min = Integer.MAX_VALUE; + int left = N; + for (int i = N - 1; i >= 0; i--) { + if (min < nums[i]) { + left = i; + } + min = Math.min(min, nums[i]); + } + return Math.max(0, right - left + 1); + } + +} diff --git a/大厂刷题班/class03/Code01_LongestSubstringWithoutRepeatingCharacters.java b/大厂刷题班/class03/Code01_LongestSubstringWithoutRepeatingCharacters.java new file mode 100644 index 0000000..120a441 --- /dev/null +++ b/大厂刷题班/class03/Code01_LongestSubstringWithoutRepeatingCharacters.java @@ -0,0 +1,27 @@ +package class03; + +// 本题测试链接 : https://leetcode.com/problems/longest-substring-without-repeating-characters/ +public class Code01_LongestSubstringWithoutRepeatingCharacters { + + public static int lengthOfLongestSubstring(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int[] map = new int[256]; + for (int i = 0; i < 256; i++) { + map[i] = -1; + } + map[str[0]] = 0; + int N = str.length; + int ans = 1; + int pre = 1; + for (int i = 1; i < N; i++) { + pre = Math.min(i - map[str[i]], pre + 1); + ans = Math.max(ans, pre); + map[str[i]] = i; + } + return ans; + } + +} diff --git a/大厂刷题班/class03/Code02_HowManyTypes.java b/大厂刷题班/class03/Code02_HowManyTypes.java new file mode 100644 index 0000000..9d40789 --- /dev/null +++ b/大厂刷题班/class03/Code02_HowManyTypes.java @@ -0,0 +1,82 @@ +package class03; + +import java.util.HashSet; + +public class Code02_HowManyTypes { + + /* + * 只由小写字母(a~z)组成的一批字符串,都放在字符类型的数组String[] arr中, + * 如果其中某两个字符串,所含有的字符种类完全一样,就将两个字符串算作一类 比如:baacba和bac就算作一类 + * 虽然长度不一样,但是所含字符的种类完全一样(a、b、c) 返回arr中有多少类? + * + */ + + public static int types1(String[] arr) { + HashSet types = new HashSet<>(); + for (String str : arr) { + char[] chs = str.toCharArray(); + boolean[] map = new boolean[26]; + for (int i = 0; i < chs.length; i++) { + map[chs[i] - 'a'] = true; + } + String key = ""; + for (int i = 0; i < 26; i++) { + if (map[i]) { + key += String.valueOf((char) (i + 'a')); + } + } + types.add(key); + } + return types.size(); + } + + public static int types2(String[] arr) { + HashSet types = new HashSet<>(); + for (String str : arr) { + char[] chs = str.toCharArray(); + int key = 0; + for(int i = 0 ; i < chs.length;i++) { + key |= (1 << (chs[i] - 'a')); + } + types.add(key); + } + return types.size(); + } + + // for test + public static String[] getRandomStringArray(int possibilities, int strMaxSize, int arrMaxSize) { + String[] ans = new String[(int) (Math.random() * arrMaxSize) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = getRandomString(possibilities, strMaxSize); + } + return ans; + } + + // for test + public static String getRandomString(int possibilities, int strMaxSize) { + char[] ans = new char[(int) (Math.random() * strMaxSize) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (char) ((int) (Math.random() * possibilities) + 'a'); + } + return String.valueOf(ans); + } + + public static void main(String[] args) { + int possibilities = 5; + int strMaxSize = 10; + int arrMaxSize = 100; + int testTimes = 500000; + System.out.println("test begin, test time : " + testTimes); + for (int i = 0; i < testTimes; i++) { + String[] arr = getRandomStringArray(possibilities, strMaxSize, arrMaxSize); + int ans1 = types1(arr); + int ans2 = types2(arr); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + + } + +} diff --git a/大厂刷题班/class03/Code03_Largest1BorderedSquare.java b/大厂刷题班/class03/Code03_Largest1BorderedSquare.java new file mode 100644 index 0000000..9d1f053 --- /dev/null +++ b/大厂刷题班/class03/Code03_Largest1BorderedSquare.java @@ -0,0 +1,59 @@ +package class03; + +// 本题测试链接 : https://leetcode.com/problems/largest-1-bordered-square/ +public class Code03_Largest1BorderedSquare { + + public static int largest1BorderedSquare(int[][] m) { + int[][] right = new int[m.length][m[0].length]; + int[][] down = new int[m.length][m[0].length]; + setBorderMap(m, right, down); + for (int size = Math.min(m.length, m[0].length); size != 0; size--) { + if (hasSizeOfBorder(size, right, down)) { + return size * size; + } + } + return 0; + } + + public static void setBorderMap(int[][] m, int[][] right, int[][] down) { + int r = m.length; + int c = m[0].length; + if (m[r - 1][c - 1] == 1) { + right[r - 1][c - 1] = 1; + down[r - 1][c - 1] = 1; + } + for (int i = r - 2; i != -1; i--) { + if (m[i][c - 1] == 1) { + right[i][c - 1] = 1; + down[i][c - 1] = down[i + 1][c - 1] + 1; + } + } + for (int i = c - 2; i != -1; i--) { + if (m[r - 1][i] == 1) { + right[r - 1][i] = right[r - 1][i + 1] + 1; + down[r - 1][i] = 1; + } + } + for (int i = r - 2; i != -1; i--) { + for (int j = c - 2; j != -1; j--) { + if (m[i][j] == 1) { + right[i][j] = right[i][j + 1] + 1; + down[i][j] = down[i + 1][j] + 1; + } + } + } + } + + public static boolean hasSizeOfBorder(int size, int[][] right, int[][] down) { + for (int i = 0; i != right.length - size + 1; i++) { + for (int j = 0; j != right[0].length - size + 1; j++) { + if (right[i][j] >= size && down[i][j] >= size && right[i + size - 1][j] >= size + && down[i][j + size - 1] >= size) { + return true; + } + } + } + return false; + } + +} diff --git a/大厂刷题班/class03/Code04_MaxPairNumber.java b/大厂刷题班/class03/Code04_MaxPairNumber.java new file mode 100644 index 0000000..a77909c --- /dev/null +++ b/大厂刷题班/class03/Code04_MaxPairNumber.java @@ -0,0 +1,126 @@ +package class03; + +import java.util.Arrays; + +// 给定一个数组arr,代表每个人的能力值。再给定一个非负数k。 +// 如果两个人能力差值正好为k,那么可以凑在一起比赛,一局比赛只有两个人 +// 返回最多可以同时有多少场比赛 +public class Code04_MaxPairNumber { + + // 暴力解 + public static int maxPairNum1(int[] arr, int k) { + if (k < 0) { + return -1; + } + return process1(arr, 0, k); + } + + public static int process1(int[] arr, int index, int k) { + int ans = 0; + if (index == arr.length) { + for (int i = 1; i < arr.length; i += 2) { + if (arr[i] - arr[i - 1] == k) { + ans++; + } + } + } else { + for (int r = index; r < arr.length; r++) { + swap(arr, index, r); + ans = Math.max(ans, process1(arr, index + 1, k)); + swap(arr, index, r); + } + } + return ans; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 时间复杂度O(N*logN) + public static int maxPairNum2(int[] arr, int k) { + if (k < 0 || arr == null || arr.length < 2) { + return 0; + } + Arrays.sort(arr); + int ans = 0; + int N = arr.length; + int L = 0; + int R = 0; + boolean[] usedR = new boolean[N]; + while (L < N && R < N) { + if (usedR[L]) { + L++; + } else if (L >= R) { + R++; + } else { // 不止一个数,而且都没用过! + int distance = arr[R] - arr[L]; + if (distance == k) { + ans++; + usedR[R++] = true; + L++; + } else if (distance < k) { + R++; + } else { + L++; + } + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * value); + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + public static void main(String[] args) { + int maxLen = 10; + int maxValue = 20; + int maxK = 5; + int testTime = 1000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * (maxLen + 1)); + int[] arr = randomArray(N, maxValue); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int k = (int) (Math.random() * (maxK + 1)); + int ans1 = maxPairNum1(arr1, k); + int ans2 = maxPairNum2(arr2, k); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(k); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + } + +} diff --git a/大厂刷题班/class03/Code05_BoatsToSavePeople.java b/大厂刷题班/class03/Code05_BoatsToSavePeople.java new file mode 100644 index 0000000..d5cc68f --- /dev/null +++ b/大厂刷题班/class03/Code05_BoatsToSavePeople.java @@ -0,0 +1,74 @@ +package class03; + +import java.util.Arrays; + +// 给定一个正数数组arr,代表若干人的体重 +// 再给定一个正数limit,表示所有船共同拥有的载重量 +// 每艘船最多坐两人,且不能超过载重 +// 想让所有的人同时过河,并且用最好的分配方法让船尽量少 +// 返回最少的船数 +// 测试链接 : https://leetcode.com/problems/boats-to-save-people/ +public class Code05_BoatsToSavePeople { + + public static int numRescueBoats1(int[] arr, int limit) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + Arrays.sort(arr); + if (arr[N - 1] > limit) { + return -1; + } + int lessR = -1; + for (int i = N - 1; i >= 0; i--) { + if (arr[i] <= (limit / 2)) { + lessR = i; + break; + } + } + if (lessR == -1) { + return N; + } + int L = lessR; + int R = lessR + 1; + int noUsed = 0; + while (L >= 0) { + int solved = 0; + while (R < N && arr[L] + arr[R] <= limit) { + R++; + solved++; + } + if (solved == 0) { + noUsed++; + L--; + } else { + L = Math.max(-1, L - solved); + } + } + int all = lessR + 1; + int used = all - noUsed; + int moreUnsolved = (N - all) - used; + return used + ((noUsed + 1) >> 1) + moreUnsolved; + } + + // 首尾双指针的解法 + public static int numRescueBoats2(int[] people, int limit) { + Arrays.sort(people); + int ans = 0; + int l = 0; + int r = people.length - 1; + int sum = 0; + while (l <= r) { + sum = l == r ? people[l] : people[l] + people[r]; + if (sum > limit) { + r--; + } else { + l++; + r--; + } + ans++; + } + return ans; + } + +} diff --git a/大厂刷题班/class03/Code06_ClosestSubsequenceSum.java b/大厂刷题班/class03/Code06_ClosestSubsequenceSum.java new file mode 100644 index 0000000..1cb2c0b --- /dev/null +++ b/大厂刷题班/class03/Code06_ClosestSubsequenceSum.java @@ -0,0 +1,46 @@ +package class03; + +import java.util.Arrays; + +// 本题测试链接 : https://leetcode.com/problems/closest-subsequence-sum/ +// 本题数据量描述: +// 1 <= nums.length <= 40 +// -10^7 <= nums[i] <= 10^7 +// -10^9 <= goal <= 10^9 +// 通过这个数据量描述可知,需要用到分治,因为数组长度不大 +// 而值很大,用动态规划的话,表会爆 +public class Code06_ClosestSubsequenceSum { + + public static int[] l = new int[1 << 20]; + public static int[] r = new int[1 << 20]; + + public static int minAbsDifference(int[] nums, int goal) { + if (nums == null || nums.length == 0) { + return goal; + } + int le = process(nums, 0, nums.length >> 1, 0, 0, l); + int re = process(nums, nums.length >> 1, nums.length, 0, 0, r); + Arrays.sort(l, 0, le); + Arrays.sort(r, 0, re--); + int ans = Math.abs(goal); + for (int i = 0; i < le; i++) { + int rest = goal - l[i]; + while (re > 0 && Math.abs(rest - r[re - 1]) <= Math.abs(rest - r[re])) { + re--; + } + ans = Math.min(ans, Math.abs(rest - r[re])); + } + return ans; + } + + public static int process(int[] nums, int index, int end, int sum, int fill, int[] arr) { + if (index == end) { + arr[fill++] = sum; + } else { + fill = process(nums, index + 1, end, sum, fill, arr); + fill = process(nums, index + 1, end, sum + nums[index], fill, arr); + } + return fill; + } + +} diff --git a/大厂刷题班/class03/Code07_FreedomTrail.java b/大厂刷题班/class03/Code07_FreedomTrail.java new file mode 100644 index 0000000..a3ef36d --- /dev/null +++ b/大厂刷题班/class03/Code07_FreedomTrail.java @@ -0,0 +1,62 @@ +package class03; + +import java.util.ArrayList; +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.com/problems/freedom-trail/ +public class Code07_FreedomTrail { + + public static int findRotateSteps(String r, String k) { + char[] ring = r.toCharArray(); + int N = ring.length; + HashMap> map = new HashMap<>(); + for (int i = 0; i < N; i++) { + if (!map.containsKey(ring[i])) { + map.put(ring[i], new ArrayList<>()); + } + map.get(ring[i]).add(i); + } + char[] str = k.toCharArray(); + int M = str.length; + int[][] dp = new int[N][M + 1]; + // hashmap + // dp[][] == -1 : 表示没算过! + for (int i = 0; i < N; i++) { + for (int j = 0; j <= M; j++) { + dp[i][j] = -1; + } + } + return process(0, 0, str, map, N, dp); + } + + // 电话里:指针指着的上一个按键preButton + // 目标里:此时要搞定哪个字符?keyIndex + // map : key 一种字符 value: 哪些位置拥有这个字符 + // N: 电话大小 + // f(0, 0, aim, map, N) + public static int process(int preButton, int index, char[] str, HashMap> map, int N, + int[][] dp) { + if (dp[preButton][index] != -1) { + return dp[preButton][index]; + } + int ans = Integer.MAX_VALUE; + if (index == str.length) { + ans = 0; + } else { + // 还有字符需要搞定呢! + char cur = str[index]; + ArrayList nextPositions = map.get(cur); + for (int next : nextPositions) { + int cost = dial(preButton, next, N) + 1 + process(next, index + 1, str, map, N, dp); + ans = Math.min(ans, cost); + } + } + dp[preButton][index] = ans; + return ans; + } + + public static int dial(int i1, int i2, int size) { + return Math.min(Math.abs(i1 - i2), Math.min(i1, i2) + size - Math.max(i1, i2)); + } + +} diff --git a/大厂刷题班/class03/Code08_DistanceKNodes.java b/大厂刷题班/class03/Code08_DistanceKNodes.java new file mode 100644 index 0000000..30de2e8 --- /dev/null +++ b/大厂刷题班/class03/Code08_DistanceKNodes.java @@ -0,0 +1,105 @@ +package class03; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class Code08_DistanceKNodes { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + public static List distanceKNodes(Node root, Node target, int K) { + HashMap parents = new HashMap<>(); + parents.put(root, null); + createParentMap(root, parents); + Queue queue = new LinkedList<>(); + HashSet visited = new HashSet<>(); + queue.offer(target); + visited.add(target); + int curLevel = 0; + List ans = new ArrayList<>(); + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + Node cur = queue.poll(); + if (curLevel == K) { + ans.add(cur); + } + if (cur.left != null && !visited.contains(cur.left)) { + visited.add(cur.left); + queue.offer(cur.left); + } + if (cur.right != null && !visited.contains(cur.right)) { + visited.add(cur.right); + queue.offer(cur.right); + } + if (parents.get(cur) != null && !visited.contains(parents.get(cur))) { + visited.add(parents.get(cur)); + queue.offer(parents.get(cur)); + } + } + curLevel++; + if (curLevel > K) { + break; + } + } + return ans; + } + + public static void createParentMap(Node cur, HashMap parents) { + if (cur == null) { + return; + } + if (cur.left != null) { + parents.put(cur.left, cur); + createParentMap(cur.left, parents); + } + if (cur.right != null) { + parents.put(cur.right, cur); + createParentMap(cur.right, parents); + } + } + + public static void main(String[] args) { + Node n0 = new Node(0); + Node n1 = new Node(1); + Node n2 = new Node(2); + Node n3 = new Node(3); + Node n4 = new Node(4); + Node n5 = new Node(5); + Node n6 = new Node(6); + Node n7 = new Node(7); + Node n8 = new Node(8); + + n3.left = n5; + n3.right = n1; + n5.left = n6; + n5.right = n2; + n1.left = n0; + n1.right = n8; + n2.left = n7; + n2.right = n4; + + Node root = n3; + Node target = n5; + int K = 2; + + List ans = distanceKNodes(root, target, K); + for (Node o1 : ans) { + System.out.println(o1.value); + } + + } + +} diff --git a/大厂刷题班/class04/Code01_QueryHobby.java b/大厂刷题班/class04/Code01_QueryHobby.java new file mode 100644 index 0000000..20babf4 --- /dev/null +++ b/大厂刷题班/class04/Code01_QueryHobby.java @@ -0,0 +1,115 @@ +package class04; + +import java.util.ArrayList; +import java.util.HashMap; + +public class Code01_QueryHobby { + + /* + * 今日头条原题 + * + * 数组为{3, 2, 2, 3, 1},查询为(0, 3, 2)。意思是在数组里下标0~3这个范围上,有几个2?返回2。 + * 假设给你一个数组arr,对这个数组的查询非常频繁,请返回所有查询的结果 + * + */ + + public static class QueryBox1 { + private int[] arr; + + public QueryBox1(int[] array) { + arr = new int[array.length]; + for (int i = 0; i < arr.length; i++) { + arr[i] = array[i]; + } + } + + public int query(int L, int R, int v) { + int ans = 0; + for (; L <= R; L++) { + if (arr[L] == v) { + ans++; + } + } + return ans; + } + } + + public static class QueryBox2 { + private HashMap> map; + + public QueryBox2(int[] arr) { + map = new HashMap<>(); + for (int i = 0; i < arr.length; i++) { + if (!map.containsKey(arr[i])) { + map.put(arr[i], new ArrayList<>()); + } + map.get(arr[i]).add(i); + } + } + + public int query(int L, int R, int value) { + if (!map.containsKey(value)) { + return 0; + } + ArrayList indexArr = map.get(value); + // 查询 < L 的下标有几个 + int a = countLess(indexArr, L); + // 查询 < R+1 的下标有几个 + int b = countLess(indexArr, R + 1); + return b - a; + } + + // 在有序数组arr中,用二分的方法数出 arr, int limit) { + int L = 0; + int R = arr.size() - 1; + int mostRight = -1; + while (L <= R) { + int mid = L + ((R - L) >> 1); + if (arr.get(mid) < limit) { + mostRight = mid; + L = mid + 1; + } else { + R = mid - 1; + } + } + return mostRight + 1; + } + + } + + public static int[] generateRandomArray(int len, int value) { + int[] ans = new int[(int) (Math.random() * len) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * value) + 1; + } + return ans; + } + + public static void main(String[] args) { + int len = 300; + int value = 20; + int testTimes = 1000; + int queryTimes = 1000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + int[] arr = generateRandomArray(len, value); + int N = arr.length; + QueryBox1 box1 = new QueryBox1(arr); + QueryBox2 box2 = new QueryBox2(arr); + for (int j = 0; j < queryTimes; j++) { + int a = (int) (Math.random() * N); + int b = (int) (Math.random() * N); + int L = Math.min(a, b); + int R = Math.max(a, b); + int v = (int) (Math.random() * value) + 1; + if (box1.query(L, R, v) != box2.query(L, R, v)) { + System.out.println("Oops!"); + } + } + } + System.out.println("test end"); + } + +} diff --git a/大厂刷题班/class04/Code02_SubArrayMaxSum.java b/大厂刷题班/class04/Code02_SubArrayMaxSum.java new file mode 100644 index 0000000..25477fa --- /dev/null +++ b/大厂刷题班/class04/Code02_SubArrayMaxSum.java @@ -0,0 +1,35 @@ +package class04; + +// 本题测试链接 : https://leetcode.com/problems/maximum-subarray/ +public class Code02_SubArrayMaxSum { + + public static int maxSubArray(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + int cur = 0; + for (int i = 0; i < arr.length; i++) { + cur += arr[i]; + max = Math.max(max, cur); + cur = cur < 0 ? 0 : cur; + } + return max; + } + + public static int maxSubArray2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + // 上一步,dp的值 + // dp[0] + int pre = arr[0]; + int max = arr[0]; + for (int i = 1; i < arr.length; i++) { + pre = Math.max(arr[i], arr[i] + pre); + max = Math.max(max, pre); + } + return max; + } + +} diff --git a/大厂刷题班/class04/Code03_SubMatrixMaxSum.java b/大厂刷题班/class04/Code03_SubMatrixMaxSum.java new file mode 100644 index 0000000..2940774 --- /dev/null +++ b/大厂刷题班/class04/Code03_SubMatrixMaxSum.java @@ -0,0 +1,75 @@ +package class04; + +public class Code03_SubMatrixMaxSum { + + public static int maxSum(int[][] m) { + if (m == null || m.length == 0 || m[0].length == 0) { + return 0; + } + // O(N^2 * M) + int N = m.length; + int M = m[0].length; + int max = Integer.MIN_VALUE; + for (int i = 0; i < N; i++) { + // i~j + int[] s = new int[M]; + for (int j = i; j < N; j++) { + for (int k = 0; k < M; k++) { + s[k] += m[j][k]; + } + max = Math.max(max, maxSubArray(s)); + } + } + return max; + } + + public static int maxSubArray(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + int cur = 0; + for (int i = 0; i < arr.length; i++) { + cur += arr[i]; + max = Math.max(max, cur); + cur = cur < 0 ? 0 : cur; + } + return max; + } + + // 本题测试链接 : https://leetcode-cn.com/problems/max-submatrix-lcci/ + public static int[] getMaxMatrix(int[][] m) { + int N = m.length; + int M = m[0].length; + int max = Integer.MIN_VALUE; + int cur = 0; + int a = 0; + int b = 0; + int c = 0; + int d = 0; + for (int i = 0; i < N; i++) { + int[] s = new int[M]; + for (int j = i; j < N; j++) { + cur = 0; + int begin = 0; + for (int k = 0; k < M; k++) { + s[k] += m[j][k]; + cur += s[k]; + if (max < cur) { + max = cur; + a = i; + b = begin; + c = j; + d = k; + } + if (cur < 0) { + cur = 0; + begin = k + 1; + } + } + } + } + return new int[] { a, b, c, d }; + } + +} diff --git a/大厂刷题班/class04/Code04_SubArrayMaxSumFollowUp.java b/大厂刷题班/class04/Code04_SubArrayMaxSumFollowUp.java new file mode 100644 index 0000000..0d5cccd --- /dev/null +++ b/大厂刷题班/class04/Code04_SubArrayMaxSumFollowUp.java @@ -0,0 +1,59 @@ +package class04; + +// 在线测试链接 : https://leetcode.com/problems/house-robber/ +public class Code04_SubArrayMaxSumFollowUp { + + public static int rob1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0]; + } + int[] dp = new int[arr.length]; + // dp[i] : arr[0..i]挑选,满足不相邻设定的情况下,随意挑选,最大的累加和 + dp[0] = arr[0]; + dp[1] = Math.max(arr[0], arr[1]); + for (int i = 2; i < arr.length; i++) { + int p1 = dp[i - 1]; + int p2 = arr[i] + Math.max(dp[i - 2], 0); + dp[i] = Math.max(p1, p2); + } + return dp[arr.length - 1]; + } + + // 给定一个数组arr,在不能取相邻数的情况下,返回所有组合中的最大累加和 + // 思路: + // 定义dp[i] : 表示arr[0...i]范围上,在不能取相邻数的情况下,返回所有组合中的最大累加和 + // 在arr[0...i]范围上,在不能取相邻数的情况下,得到的最大累加和,可能性分类: + // 可能性 1) 选出的组合,不包含arr[i]。那么dp[i] = dp[i-1] + // 比如,arr[0...i] = {3,4,-4},最大累加和是不包含i位置数的时候 + // + // 可能性 2) 选出的组合,只包含arr[i]。那么dp[i] = arr[i] + // 比如,arr[0...i] = {-3,-4,4},最大累加和是只包含i位置数的时候 + // + // 可能性 3) 选出的组合,包含arr[i], 且包含arr[0...i-2]范围上的累加和。那么dp[i] = arr[i] + dp[i-2] + // 比如,arr[0...i] = {3,1,4},最大累加和是3和4组成的7,因为相邻不能选,所以i-1位置的数要跳过 + // + // 综上所述:dp[i] = Max { dp[i-1], arr[i] , arr[i] + dp[i-2] } + public static int rob2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + if (N == 1) { + return arr[0]; + } + if (N == 2) { + return Math.max(arr[0], arr[1]); + } + int[] dp = new int[N]; + dp[0] = arr[0]; + dp[1] = Math.max(arr[0], arr[1]); + for (int i = 2; i < N; i++) { + dp[i] = Math.max(Math.max(dp[i - 1], arr[i]), arr[i] + dp[i - 2]); + } + return dp[N - 1]; + } + +} diff --git a/大厂刷题班/class04/Code05_CandyProblem.java b/大厂刷题班/class04/Code05_CandyProblem.java new file mode 100644 index 0000000..694aab2 --- /dev/null +++ b/大厂刷题班/class04/Code05_CandyProblem.java @@ -0,0 +1,145 @@ +package class04; + +// 测试链接 : https://leetcode.com/problems/candy/ +public class Code05_CandyProblem { + + // 这是原问题的优良解 + // 时间复杂度O(N),额外空间复杂度O(N) + public static int candy1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + int[] left = new int[N]; + for (int i = 1; i < N; i++) { + if (arr[i - 1] < arr[i]) { + left[i] = left[i - 1] + 1; + } + } + int[] right = new int[N]; + for (int i = N - 2; i >= 0; i--) { + if (arr[i] > arr[i + 1]) { + right[i] = right[i + 1] + 1; + } + } + int ans = 0; + for (int i = 0; i < N; i++) { + ans += Math.max(left[i], right[i]); + } + return ans + N; + } + + // 这是原问题空间优化后的解 + // 时间复杂度O(N),额外空间复杂度O(1) + public static int candy2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int index = nextMinIndex2(arr, 0); + int res = rightCands(arr, 0, index++); + int lbase = 1; + int next = 0; + int rcands = 0; + int rbase = 0; + while (index != arr.length) { + if (arr[index] > arr[index - 1]) { + res += ++lbase; + index++; + } else if (arr[index] < arr[index - 1]) { + next = nextMinIndex2(arr, index - 1); + rcands = rightCands(arr, index - 1, next++); + rbase = next - index + 1; + res += rcands + (rbase > lbase ? -lbase : -rbase); + lbase = 1; + index = next; + } else { + res += 1; + lbase = 1; + index++; + } + } + return res; + } + + public static int nextMinIndex2(int[] arr, int start) { + for (int i = start; i != arr.length - 1; i++) { + if (arr[i] <= arr[i + 1]) { + return i; + } + } + return arr.length - 1; + } + + public static int rightCands(int[] arr, int left, int right) { + int n = right - left + 1; + return n + n * (n - 1) / 2; + } + + // 这是进阶问题的最优解,不要提交这个 + // 时间复杂度O(N), 额外空间复杂度O(1) + public static int candy3(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int index = nextMinIndex3(arr, 0); + int[] data = rightCandsAndBase(arr, 0, index++); + int res = data[0]; + int lbase = 1; + int same = 1; + int next = 0; + while (index != arr.length) { + if (arr[index] > arr[index - 1]) { + res += ++lbase; + same = 1; + index++; + } else if (arr[index] < arr[index - 1]) { + next = nextMinIndex3(arr, index - 1); + data = rightCandsAndBase(arr, index - 1, next++); + if (data[1] <= lbase) { + res += data[0] - data[1]; + } else { + res += -lbase * same + data[0] - data[1] + data[1] * same; + } + index = next; + lbase = 1; + same = 1; + } else { + res += lbase; + same++; + index++; + } + } + return res; + } + + public static int nextMinIndex3(int[] arr, int start) { + for (int i = start; i != arr.length - 1; i++) { + if (arr[i] < arr[i + 1]) { + return i; + } + } + return arr.length - 1; + } + + public static int[] rightCandsAndBase(int[] arr, int left, int right) { + int base = 1; + int cands = 1; + for (int i = right - 1; i >= left; i--) { + if (arr[i] == arr[i + 1]) { + cands += base; + } else { + cands += ++base; + } + } + return new int[] { cands, base }; + } + + public static void main(String[] args) { + int[] test1 = { 3, 0, 5, 5, 4, 4, 0 }; + System.out.println(candy2(test1)); + + int[] test2 = { 3, 0, 5, 5, 4, 4, 0 }; + System.out.println(candy3(test2)); + } + +} diff --git a/大厂刷题班/class04/Code06_MakeNo.java b/大厂刷题班/class04/Code06_MakeNo.java new file mode 100644 index 0000000..2a4af46 --- /dev/null +++ b/大厂刷题班/class04/Code06_MakeNo.java @@ -0,0 +1,59 @@ +package class04; + +public class Code06_MakeNo { + + // 生成长度为size的达标数组 + // 达标:对于任意的 i 等长奇数达标来 + // base -> 等长偶数达标来 + int[] ans = new int[size]; + int index = 0; + for (; index < halfSize; index++) { + ans[index] = base[index] * 2 - 1; + } + for (int i = 0; index < size; index++, i++) { + ans[index] = base[i] * 2; + } + return ans; + } + + // 检验函数 + public static boolean isValid(int[] arr) { + int N = arr.length; + for (int i = 0; i < N; i++) { + for (int k = i + 1; k < N; k++) { + for (int j = k + 1; j < N; j++) { + if (arr[i] + arr[j] == 2 * arr[k]) { + return false; + } + } + } + } + return true; + } + + public static void main(String[] args) { + System.out.println("test begin"); + for (int N = 1; N < 1000; N++) { + int[] arr = makeNo(N); + if (!isValid(arr)) { + System.out.println("Oops!"); + } + } + System.out.println("test end"); + System.out.println(isValid(makeNo(1042))); + System.out.println(isValid(makeNo(2981))); + } + +} diff --git a/大厂刷题班/class04/Code07_InterleavingString.java b/大厂刷题班/class04/Code07_InterleavingString.java new file mode 100644 index 0000000..f53e113 --- /dev/null +++ b/大厂刷题班/class04/Code07_InterleavingString.java @@ -0,0 +1,53 @@ +package class04; + +// 本题测试链接 : https://leetcode.com/problems/interleaving-string/ +public class Code07_InterleavingString { + + public static boolean isInterleave(String s1, String s2, String s3) { + if (s1 == null || s2 == null || s3 == null) { + return false; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + char[] str3 = s3.toCharArray(); + if (str3.length != str1.length + str2.length) { + return false; + } + boolean[][] dp = new boolean[str1.length + 1][str2.length + 1]; + dp[0][0] = true; + for (int i = 1; i <= str1.length; i++) { + if (str1[i - 1] != str3[i - 1]) { + break; + } + dp[i][0] = true; + } + for (int j = 1; j <= str2.length; j++) { + if (str2[j - 1] != str3[j - 1]) { + break; + } + dp[0][j] = true; + } + for (int i = 1; i <= str1.length; i++) { + for (int j = 1; j <= str2.length; j++) { + if ( + (str1[i - 1] == str3[i + j - 1] && dp[i - 1][j]) + + + + + || + + + + (str2[j - 1] == str3[i + j - 1] && dp[i][j - 1]) + + + ) { + dp[i][j] = true; + } + } + } + return dp[str1.length][str2.length]; + } + +} diff --git a/大厂刷题班/class04/Code08_TheSkylineProblem.java b/大厂刷题班/class04/Code08_TheSkylineProblem.java new file mode 100644 index 0000000..29f3ccd --- /dev/null +++ b/大厂刷题班/class04/Code08_TheSkylineProblem.java @@ -0,0 +1,73 @@ +package class04; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; + +// 本题测试链接 : https://leetcode.com/problems/the-skyline-problem/ +public class Code08_TheSkylineProblem { + + public static class Node { + public int x; + public boolean isAdd; + public int h; + + public Node(int x, boolean isAdd, int h) { + this.x = x; + this.isAdd = isAdd; + this.h = h; + } + } + + public static class NodeComparator implements Comparator { + @Override + public int compare(Node o1, Node o2) { + return o1.x - o2.x; + } + } + + public static List> getSkyline(int[][] matrix) { + Node[] nodes = new Node[matrix.length * 2]; + for (int i = 0; i < matrix.length; i++) { + nodes[i * 2] = new Node(matrix[i][0], true, matrix[i][2]); + nodes[i * 2 + 1] = new Node(matrix[i][1], false, matrix[i][2]); + } + Arrays.sort(nodes, new NodeComparator()); + // key 高度 value 次数 + TreeMap mapHeightTimes = new TreeMap<>(); + TreeMap mapXHeight = new TreeMap<>(); + for (int i = 0; i < nodes.length; i++) { + if (nodes[i].isAdd) { + if (!mapHeightTimes.containsKey(nodes[i].h)) { + mapHeightTimes.put(nodes[i].h, 1); + } else { + mapHeightTimes.put(nodes[i].h, mapHeightTimes.get(nodes[i].h) + 1); + } + } else { + if (mapHeightTimes.get(nodes[i].h) == 1) { + mapHeightTimes.remove(nodes[i].h); + } else { + mapHeightTimes.put(nodes[i].h, mapHeightTimes.get(nodes[i].h) - 1); + } + } + if (mapHeightTimes.isEmpty()) { + mapXHeight.put(nodes[i].x, 0); + } else { + mapXHeight.put(nodes[i].x, mapHeightTimes.lastKey()); + } + } + List> ans = new ArrayList<>(); + for (Entry entry : mapXHeight.entrySet()) { + int curX = entry.getKey(); + int curMaxHeight = entry.getValue(); + if (ans.isEmpty() || ans.get(ans.size() - 1).get(1) != curMaxHeight) { + ans.add(new ArrayList<>(Arrays.asList(curX, curMaxHeight))); + } + } + return ans; + } + +} diff --git a/大厂刷题班/class05/Code01_ConstructBinarySearchTreeFromPreorderTraversal.java b/大厂刷题班/class05/Code01_ConstructBinarySearchTreeFromPreorderTraversal.java new file mode 100644 index 0000000..3eaf865 --- /dev/null +++ b/大厂刷题班/class05/Code01_ConstructBinarySearchTreeFromPreorderTraversal.java @@ -0,0 +1,115 @@ +package class05; + +import java.util.Stack; + +// 本题测试链接 : https://leetcode.com/problems/construct-binary-search-tree-from-preorder-traversal/ +public class Code01_ConstructBinarySearchTreeFromPreorderTraversal { + + // 不用提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode() { + } + + public TreeNode(int val) { + this.val = val; + } + + public TreeNode(int val, TreeNode left, TreeNode right) { + this.val = val; + this.left = left; + this.right = right; + } + } + + // 提交下面的方法 + public static TreeNode bstFromPreorder1(int[] pre) { + if (pre == null || pre.length == 0) { + return null; + } + return process1(pre, 0, pre.length - 1); + } + + public static TreeNode process1(int[] pre, int L, int R) { + if (L > R) { + return null; + } + int firstBig = L + 1; + for (; firstBig <= R; firstBig++) { + if (pre[firstBig] > pre[L]) { + break; + } + } + TreeNode head = new TreeNode(pre[L]); + head.left = process1(pre, L + 1, firstBig - 1); + head.right = process1(pre, firstBig, R); + return head; + } + + // 已经是时间复杂度最优的方法了,但是常数项还能优化 + public static TreeNode bstFromPreorder2(int[] pre) { + if (pre == null || pre.length == 0) { + return null; + } + int N = pre.length; + int[] nearBig = new int[N]; + for (int i = 0; i < N; i++) { + nearBig[i] = -1; + } + Stack stack = new Stack<>(); + for (int i = 0; i < N; i++) { + while (!stack.isEmpty() && pre[stack.peek()] < pre[i]) { + nearBig[stack.pop()] = i; + } + stack.push(i); + } + return process2(pre, 0, N - 1, nearBig); + } + + public static TreeNode process2(int[] pre, int L, int R, int[] nearBig) { + if (L > R) { + return null; + } + int firstBig = (nearBig[L] == -1 || nearBig[L] > R) ? R + 1 : nearBig[L]; + TreeNode head = new TreeNode(pre[L]); + head.left = process2(pre, L + 1, firstBig - 1, nearBig); + head.right = process2(pre, firstBig, R, nearBig); + return head; + } + + // 最优解 + public static TreeNode bstFromPreorder3(int[] pre) { + if (pre == null || pre.length == 0) { + return null; + } + int N = pre.length; + int[] nearBig = new int[N]; + for (int i = 0; i < N; i++) { + nearBig[i] = -1; + } + int[] stack = new int[N]; + int size = 0; + for (int i = 0; i < N; i++) { + while (size != 0 && pre[stack[size - 1]] < pre[i]) { + nearBig[stack[--size]] = i; + } + stack[size++] = i; + } + return process3(pre, 0, N - 1, nearBig); + } + + public static TreeNode process3(int[] pre, int L, int R, int[] nearBig) { + if (L > R) { + return null; + } + int firstBig = (nearBig[L] == -1 || nearBig[L] > R) ? R + 1 : nearBig[L]; + TreeNode head = new TreeNode(pre[L]); + head.left = process3(pre, L + 1, firstBig - 1, nearBig); + head.right = process3(pre, firstBig, R, nearBig); + return head; + } + +} diff --git a/大厂刷题班/class05/Code02_LeftRightSameTreeNumber.java b/大厂刷题班/class05/Code02_LeftRightSameTreeNumber.java new file mode 100644 index 0000000..7b53125 --- /dev/null +++ b/大厂刷题班/class05/Code02_LeftRightSameTreeNumber.java @@ -0,0 +1,95 @@ +package class05; + +// 如果一个节点X,它左树结构和右树结构完全一样,那么我们说以X为头的子树是相等子树 +// 给定一棵二叉树的头节点head,返回head整棵树上有多少棵相等子树 +public class Code02_LeftRightSameTreeNumber { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + // 时间复杂度O(N * logN) + public static int sameNumber1(Node head) { + if (head == null) { + return 0; + } + return sameNumber1(head.left) + sameNumber1(head.right) + (same(head.left, head.right) ? 1 : 0); + } + + public static boolean same(Node h1, Node h2) { + if (h1 == null ^ h2 == null) { + return false; + } + if (h1 == null && h2 == null) { + return true; + } + // 两个都不为空 + return h1.value == h2.value && same(h1.left, h2.left) && same(h1.right, h2.right); + } + + // 时间复杂度O(N) + public static int sameNumber2(Node head) { + String algorithm = "SHA-256"; + Hash hash = new Hash(algorithm); + return process(head, hash).ans; + } + + public static class Info { + public int ans; + public String str; + + public Info(int a, String s) { + ans = a; + str = s; + } + } + + public static Info process(Node head, Hash hash) { + if (head == null) { + return new Info(0, hash.hashCode("#,")); + } + Info l = process(head.left, hash); + Info r = process(head.right, hash); + int ans = (l.str.equals(r.str) ? 1 : 0) + l.ans + r.ans; + String str = hash.hashCode(String.valueOf(head.value) + "," + l.str + r.str); + return new Info(ans, str); + } + + public static Node randomBinaryTree(int restLevel, int maxValue) { + if (restLevel == 0) { + return null; + } + Node head = Math.random() < 0.2 ? null : new Node((int) (Math.random() * maxValue)); + if (head != null) { + head.left = randomBinaryTree(restLevel - 1, maxValue); + head.right = randomBinaryTree(restLevel - 1, maxValue); + } + return head; + } + + public static void main(String[] args) { + int maxLevel = 8; + int maxValue = 4; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + Node head = randomBinaryTree(maxLevel, maxValue); + int ans1 = sameNumber1(head); + int ans2 = sameNumber2(head); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + + } + +} diff --git a/大厂刷题班/class05/Code03_EditCost.java b/大厂刷题班/class05/Code03_EditCost.java new file mode 100644 index 0000000..0079c51 --- /dev/null +++ b/大厂刷题班/class05/Code03_EditCost.java @@ -0,0 +1,89 @@ +package class05; + +public class Code03_EditCost { + + public static int minCost1(String s1, String s2, int ic, int dc, int rc) { + if (s1 == null || s2 == null) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length + 1; + int M = str2.length + 1; + int[][] dp = new int[N][M]; + // dp[0][0] = 0 + for (int i = 1; i < N; i++) { + dp[i][0] = dc * i; + } + for (int j = 1; j < M; j++) { + dp[0][j] = ic * j; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + dp[i][j] = dp[i - 1][j - 1] + (str1[i - 1] == str2[j - 1] ? 0 : rc); + dp[i][j] = Math.min(dp[i][j], dp[i][j - 1] + ic); + dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + dc); + } + } + return dp[N - 1][M - 1]; + } + + public static int minCost2(String str1, String str2, int ic, int dc, int rc) { + if (str1 == null || str2 == null) { + return 0; + } + char[] chs1 = str1.toCharArray(); + char[] chs2 = str2.toCharArray(); + char[] longs = chs1.length >= chs2.length ? chs1 : chs2; + char[] shorts = chs1.length < chs2.length ? chs1 : chs2; + if (chs1.length < chs2.length) { + int tmp = ic; + ic = dc; + dc = tmp; + } + int[] dp = new int[shorts.length + 1]; + for (int i = 1; i <= shorts.length; i++) { + dp[i] = ic * i; + } + for (int i = 1; i <= longs.length; i++) { + int pre = dp[0]; + dp[0] = dc * i; + for (int j = 1; j <= shorts.length; j++) { + int tmp = dp[j]; + if (longs[i - 1] == shorts[j - 1]) { + dp[j] = pre; + } else { + dp[j] = pre + rc; + } + dp[j] = Math.min(dp[j], dp[j - 1] + ic); + dp[j] = Math.min(dp[j], tmp + dc); + pre = tmp; + } + } + return dp[shorts.length]; + } + + public static void main(String[] args) { + String str1 = "ab12cd3"; + String str2 = "abcdf"; + System.out.println(minCost1(str1, str2, 5, 3, 2)); + System.out.println(minCost2(str1, str2, 5, 3, 2)); + + str1 = "abcdf"; + str2 = "ab12cd3"; + System.out.println(minCost1(str1, str2, 3, 2, 4)); + System.out.println(minCost2(str1, str2, 3, 2, 4)); + + str1 = ""; + str2 = "ab12cd3"; + System.out.println(minCost1(str1, str2, 1, 7, 5)); + System.out.println(minCost2(str1, str2, 1, 7, 5)); + + str1 = "abcdf"; + str2 = ""; + System.out.println(minCost1(str1, str2, 2, 9, 8)); + System.out.println(minCost2(str1, str2, 2, 9, 8)); + + } + +} diff --git a/大厂刷题班/class05/Code04_DeleteMinCost.java b/大厂刷题班/class05/Code04_DeleteMinCost.java new file mode 100644 index 0000000..7aebee3 --- /dev/null +++ b/大厂刷题班/class05/Code04_DeleteMinCost.java @@ -0,0 +1,258 @@ +package class05; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +public class Code04_DeleteMinCost { + + // 题目: + // 给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串? + // 比如 s1 = "abcde",s2 = "axbc" + // 返回 1 + + // 解法一 + // 求出str2所有的子序列,然后按照长度排序,长度大的排在前面。 + // 然后考察哪个子序列字符串和s1的某个子串相等(KMP),答案就出来了。 + // 分析: + // 因为题目原本的样本数据中,有特别说明s2的长度很小。所以这么做也没有太大问题,也几乎不会超时。 + // 但是如果某一次考试给定的s2长度远大于s1,这么做就不合适了。 + public static int minCost1(String s1, String s2) { + List s2Subs = new ArrayList<>(); + process(s2.toCharArray(), 0, "", s2Subs); + s2Subs.sort(new LenComp()); + for (String str : s2Subs) { + if (s1.indexOf(str) != -1) { // indexOf底层和KMP算法代价几乎一样,也可以用KMP代替 + return s2.length() - str.length(); + } + } + return s2.length(); + } + + public static void process(char[] str2, int index, String path, List list) { + if (index == str2.length) { + list.add(path); + return; + } + process(str2, index + 1, path, list); + process(str2, index + 1, path + str2[index], list); + } + + // x字符串只通过删除的方式,变到y字符串 + // 返回至少要删几个字符 + // 如果变不成,返回Integer.Max + public static int onlyDelete(char[] x, char[] y) { + if (x.length < y.length) { + return Integer.MAX_VALUE; + } + int N = x.length; + int M = y.length; + int[][] dp = new int[N + 1][M + 1]; + for (int i = 0; i <= N; i++) { + for (int j = 0; j <= M; j++) { + dp[i][j] = Integer.MAX_VALUE; + } + } + dp[0][0] = 0; + // dp[i][j]表示前缀长度 + for (int i = 1; i <= N; i++) { + dp[i][0] = i; + } + for (int xlen = 1; xlen <= N; xlen++) { + for (int ylen = 1; ylen <= Math.min(M, xlen); ylen++) { + if (dp[xlen - 1][ylen] != Integer.MAX_VALUE) { + dp[xlen][ylen] = dp[xlen - 1][ylen] + 1; + } + if (x[xlen - 1] == y[ylen - 1] && dp[xlen - 1][ylen - 1] != Integer.MAX_VALUE) { + dp[xlen][ylen] = Math.min(dp[xlen][ylen], dp[xlen - 1][ylen - 1]); + } + } + } + return dp[N][M]; + } + + public static class LenComp implements Comparator { + + @Override + public int compare(String o1, String o2) { + return o2.length() - o1.length(); + } + + } + + // 解法二 + // 生成所有s1的子串 + // 然后考察每个子串和s2的编辑距离(假设编辑距离只有删除动作且删除一个字符的代价为1) + // 如果s1的长度较小,s2长度较大,这个方法比较合适 + public static int minCost2(String s1, String s2) { + if (s1.length() == 0 || s2.length() == 0) { + return s2.length(); + } + int ans = Integer.MAX_VALUE; + char[] str2 = s2.toCharArray(); + for (int start = 0; start < s1.length(); start++) { + for (int end = start + 1; end <= s1.length(); end++) { + // str1[start....end] + // substring -> [ 0,1 ) + ans = Math.min(ans, distance(str2, s1.substring(start, end).toCharArray())); + } + } + return ans == Integer.MAX_VALUE ? s2.length() : ans; + } + + // 求str2到s1sub的编辑距离 + // 假设编辑距离只有删除动作且删除一个字符的代价为1 + public static int distance(char[] str2, char[] s1sub) { + int row = str2.length; + int col = s1sub.length; + int[][] dp = new int[row][col]; + // dp[i][j]的含义: + // str2[0..i]仅通过删除行为变成s1sub[0..j]的最小代价 + // 可能性一: + // str2[0..i]变的过程中,不保留最后一个字符(str2[i]), + // 那么就是通过str2[0..i-1]变成s1sub[0..j]之后,再最后删掉str2[i]即可 -> dp[i][j] = dp[i-1][j] + 1 + // 可能性二: + // str2[0..i]变的过程中,想保留最后一个字符(str2[i]),然后变成s1sub[0..j], + // 这要求str2[i] == s1sub[j]才有这种可能, 然后str2[0..i-1]变成s1sub[0..j-1]即可 + // 也就是str2[i] == s1sub[j] 的条件下,dp[i][j] = dp[i-1][j-1] + dp[0][0] = str2[0] == s1sub[0] ? 0 : Integer.MAX_VALUE; + for (int j = 1; j < col; j++) { + dp[0][j] = Integer.MAX_VALUE; + } + for (int i = 1; i < row; i++) { + dp[i][0] = (dp[i - 1][0] != Integer.MAX_VALUE || str2[i] == s1sub[0]) ? i : Integer.MAX_VALUE; + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + dp[i][j] = Integer.MAX_VALUE; + if (dp[i - 1][j] != Integer.MAX_VALUE) { + dp[i][j] = dp[i - 1][j] + 1; + } + if (str2[i] == s1sub[j] && dp[i - 1][j - 1] != Integer.MAX_VALUE) { + dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - 1]); + } + + } + } + return dp[row - 1][col - 1]; + } + + // 解法二的优化 + public static int minCost3(String s1, String s2) { + if (s1.length() == 0 || s2.length() == 0) { + return s2.length(); + } + char[] str2 = s2.toCharArray(); + char[] str1 = s1.toCharArray(); + int M = str2.length; + int N = str1.length; + int[][] dp = new int[M][N]; + int ans = M; + for (int start = 0; start < N; start++) { // 开始的列数 + dp[0][start] = str2[0] == str1[start] ? 0 : M; + for (int row = 1; row < M; row++) { + dp[row][start] = (str2[row] == str1[start] || dp[row - 1][start] != M) ? row : M; + } + ans = Math.min(ans, dp[M - 1][start]); + // 以上已经把start列,填好 + // 以下要把dp[...][start+1....N-1]的信息填好 + // start...end end - start +2 + for (int end = start + 1; end < N && end - start < M; end++) { + // 0... first-1 行 不用管 + int first = end - start; + dp[first][end] = (str2[first] == str1[end] && dp[first - 1][end - 1] == 0) ? 0 : M; + for (int row = first + 1; row < M; row++) { + dp[row][end] = M; + if (dp[row - 1][end] != M) { + dp[row][end] = dp[row - 1][end] + 1; + } + if (dp[row - 1][end - 1] != M && str2[row] == str1[end]) { + dp[row][end] = Math.min(dp[row][end], dp[row - 1][end - 1]); + } + } + ans = Math.min(ans, dp[M - 1][end]); + } + } + return ans; + } + + // 来自学生的做法,时间复杂度O(N * M平方) + // 复杂度和方法三一样,但是思路截然不同 + public static int minCost4(String s1, String s2) { + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + HashMap> map1 = new HashMap<>(); + for (int i = 0; i < str1.length; i++) { + ArrayList list = map1.getOrDefault(str1[i], new ArrayList()); + list.add(i); + map1.put(str1[i], list); + } + int ans = 0; + // 假设删除后的str2必以i位置开头 + // 那么查找i位置在str1上一共有几个,并对str1上的每个位置开始遍历 + // 再次遍历str2一次,看存在对应str1中i后续连续子串可容纳的最长长度 + for (int i = 0; i < str2.length; i++) { + if (map1.containsKey(str2[i])) { + ArrayList keyList = map1.get(str2[i]); + for (int j = 0; j < keyList.size(); j++) { + int cur1 = keyList.get(j) + 1; + int cur2 = i + 1; + int count = 1; + for (int k = cur2; k < str2.length && cur1 < str1.length; k++) { + if (str2[k] == str1[cur1]) { + cur1++; + count++; + } + } + ans = Math.max(ans, count); + } + } + } + return s2.length() - ans; + } + + public static String generateRandomString(int l, int v) { + int len = (int) (Math.random() * l); + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ('a' + (int) (Math.random() * v)); + } + return String.valueOf(str); + } + + public static void main(String[] args) { + + char[] x = { 'a', 'b', 'c', 'd' }; + char[] y = { 'a', 'd' }; + + System.out.println(onlyDelete(x, y)); + + int str1Len = 20; + int str2Len = 10; + int v = 5; + int testTime = 10000; + boolean pass = true; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + String str1 = generateRandomString(str1Len, v); + String str2 = generateRandomString(str2Len, v); + int ans1 = minCost1(str1, str2); + int ans2 = minCost2(str1, str2); + int ans3 = minCost3(str1, str2); + int ans4 = minCost4(str1, str2); + if (ans1 != ans2 || ans3 != ans4 || ans1 != ans3) { + pass = false; + System.out.println(str1); + System.out.println(str2); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println(ans4); + break; + } + } + System.out.println("test pass : " + pass); + } + +} diff --git a/大厂刷题班/class05/Hash.java b/大厂刷题班/class05/Hash.java new file mode 100644 index 0000000..c995ac9 --- /dev/null +++ b/大厂刷题班/class05/Hash.java @@ -0,0 +1,49 @@ +package class05; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +// 这个包的jar,你需要自己下载一下,导入到项目中 +import javax.xml.bind.DatatypeConverter; + +public class Hash { + + private MessageDigest hash; + + public Hash(String algorithm) { + try { + hash = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public String hashCode(String input) { + return DatatypeConverter.printHexBinary(hash.digest(input.getBytes())).toUpperCase(); + } + + public static void main(String[] args) { + System.out.println("支持的算法 : "); + for (String str : Security.getAlgorithms("MessageDigest")) { + System.out.println(str); + } + System.out.println("======="); + + String algorithm = "SHA-256"; + Hash hash = new Hash(algorithm); + + String input1 = "zuochengyunzuochengyun1"; + String input2 = "zuochengyunzuochengyun2"; + String input3 = "zuochengyunzuochengyun3"; + String input4 = "zuochengyunzuochengyun4"; + String input5 = "zuochengyunzuochengyun5"; + System.out.println(hash.hashCode(input1)); + System.out.println(hash.hashCode(input2)); + System.out.println(hash.hashCode(input3)); + System.out.println(hash.hashCode(input4)); + System.out.println(hash.hashCode(input5)); + + } + +} diff --git a/大厂刷题班/class06/Code01_MaxXOR.java b/大厂刷题班/class06/Code01_MaxXOR.java new file mode 100644 index 0000000..d6ee49d --- /dev/null +++ b/大厂刷题班/class06/Code01_MaxXOR.java @@ -0,0 +1,130 @@ +package class06; + +public class Code01_MaxXOR { + + // O(N^2) + public static int maxXorSubarray1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + // 准备一个前缀异或和数组eor + // eor[i] = arr[0...i]的异或结果 + int[] eor = new int[arr.length]; + eor[0] = arr[0]; + // 生成eor数组,eor[i]代表arr[0..i]的异或和 + for (int i = 1; i < arr.length; i++) { + eor[i] = eor[i - 1] ^ arr[i]; + } + int max = Integer.MIN_VALUE; + for (int j = 0; j < arr.length; j++) { + for (int i = 0; i <= j; i++) { // 依次尝试arr[0..j]、arr[1..j]..arr[i..j]..arr[j..j] + max = Math.max(max, i == 0 ? eor[j] : eor[j] ^ eor[i - 1]); + } + } + return max; + } + + // 前缀树的Node结构 + // nexts[0] -> 0方向的路 + // nexts[1] -> 1方向的路 + // nexts[0] == null 0方向上没路! + // nexts[0] != null 0方向有路,可以跳下一个节点 + // nexts[1] == null 1方向上没路! + // nexts[1] != null 1方向有路,可以跳下一个节点 + public static class Node { + public Node[] nexts = new Node[2]; + } + + // 基于本题,定制前缀树的实现 + public static class NumTrie { + // 头节点 + public Node head = new Node(); + + public void add(int newNum) { + Node cur = head; + for (int move = 31; move >= 0; move--) { + int path = ((newNum >> move) & 1); + cur.nexts[path] = cur.nexts[path] == null ? new Node() : cur.nexts[path]; + cur = cur.nexts[path]; + } + } + + // 该结构之前收集了一票数字,并且建好了前缀树 + // num和 谁 ^ 最大的结果(把结果返回) + public int maxXor(int num) { + Node cur = head; + int ans = 0; + for (int move = 31; move >= 0; move--) { + // 取出num中第move位的状态,path只有两种值0就1,整数 + int path = (num >> move) & 1; + // 期待遇到的东西 + int best = move == 31 ? path : (path ^ 1); + // 实际遇到的东西 + best = cur.nexts[best] != null ? best : (best ^ 1); + // (path ^ best) 当前位位异或完的结果 + ans |= (path ^ best) << move; + cur = cur.nexts[best]; + } + return ans; + } + } + + // O(N) + public static int maxXorSubarray2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + // 0~i整体异或和 + int xor = 0; + NumTrie numTrie = new NumTrie(); + numTrie.add(0); + for (int i = 0; i < arr.length; i++) { + xor ^= arr[i]; // 0 ~ i + max = Math.max(max, numTrie.maxXor(xor)); + numTrie.add(xor); + } + return max; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 30; + int maxValue = 50; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int comp = maxXorSubarray1(arr); + int res = maxXorSubarray2(arr); + if (res != comp) { + succeed = false; + printArray(arr); + System.out.println(res); + System.out.println(comp); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } +} diff --git a/大厂刷题班/class06/Code02_MaximumXorOfTwoNumbersInAnArray.java b/大厂刷题班/class06/Code02_MaximumXorOfTwoNumbersInAnArray.java new file mode 100644 index 0000000..4ee4c6e --- /dev/null +++ b/大厂刷题班/class06/Code02_MaximumXorOfTwoNumbersInAnArray.java @@ -0,0 +1,50 @@ +package class06; + +// 本题测试链接 : https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/ +public class Code02_MaximumXorOfTwoNumbersInAnArray { + + public static class Node { + public Node[] nexts = new Node[2]; + } + + public static class NumTrie { + public Node head = new Node(); + + public void add(int newNum) { + Node cur = head; + for (int move = 31; move >= 0; move--) { + int path = ((newNum >> move) & 1); + cur.nexts[path] = cur.nexts[path] == null ? new Node() : cur.nexts[path]; + cur = cur.nexts[path]; + } + } + + public int maxXor(int sum) { + Node cur = head; + int res = 0; + for (int move = 31; move >= 0; move--) { + int path = (sum >> move) & 1; + int best = move == 31 ? path : (path ^ 1); + best = cur.nexts[best] != null ? best : (best ^ 1); + res |= (path ^ best) << move; + cur = cur.nexts[best]; + } + return res; + } + } + + public static int findMaximumXOR(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int max = Integer.MIN_VALUE; + NumTrie numTrie = new NumTrie(); + numTrie.add(arr[0]); + for (int i = 1; i < arr.length; i++) { + max = Math.max(max, numTrie.maxXor(arr[i])); + numTrie.add(arr[i]); + } + return max; + } + +} diff --git a/大厂刷题班/class06/Code03_MaximumXorWithAnElementFromArray.java b/大厂刷题班/class06/Code03_MaximumXorWithAnElementFromArray.java new file mode 100644 index 0000000..7ad2f29 --- /dev/null +++ b/大厂刷题班/class06/Code03_MaximumXorWithAnElementFromArray.java @@ -0,0 +1,67 @@ +package class06; + +// 测试链接 : https://leetcode.com/problems/maximum-xor-with-an-element-from-array/ +public class Code03_MaximumXorWithAnElementFromArray { + + public static int[] maximizeXor(int[] nums, int[][] queries) { + int N = nums.length; + NumTrie trie = new NumTrie(); + for (int i = 0; i < N; i++) { + trie.add(nums[i]); + } + int M = queries.length; + int[] ans = new int[M]; + for (int i = 0; i < M; i++) { + ans[i] = trie.maxXorWithXBehindM(queries[i][0], queries[i][1]); + } + return ans; + } + + public static class Node { + public int min; + public Node[] nexts; + + public Node() { + min = Integer.MAX_VALUE; + nexts = new Node[2]; + } + } + + public static class NumTrie { + public Node head = new Node(); + + public void add(int num) { + Node cur = head; + head.min = Math.min(head.min, num); + for (int move = 30; move >= 0; move--) { + int path = ((num >> move) & 1); + cur.nexts[path] = cur.nexts[path] == null ? new Node() : cur.nexts[path]; + cur = cur.nexts[path]; + cur.min = Math.min(cur.min, num); + } + } + + // 这个结构中,已经收集了一票数字 + // 请返回哪个数字与X异或的结果最大,返回最大结果 + // 但是,只有<=m的数字,可以被考虑 + public int maxXorWithXBehindM(int x, int m) { + if (head.min > m) { + return -1; + } + // 一定存在某个数可以和x结合 + Node cur = head; + int ans = 0; + for (int move = 30; move >= 0; move--) { + int path = (x >> move) & 1; + // 期待遇到的东西 + int best = (path ^ 1); + best ^= (cur.nexts[best] == null || cur.nexts[best].min > m) ? 1 : 0; + // best变成了实际遇到的 + ans |= (path ^ best) << move; + cur = cur.nexts[best]; + } + return ans; + } + } + +} diff --git a/大厂刷题班/class06/Code04_MostXorZero.java b/大厂刷题班/class06/Code04_MostXorZero.java new file mode 100644 index 0000000..a73e5c5 --- /dev/null +++ b/大厂刷题班/class06/Code04_MostXorZero.java @@ -0,0 +1,122 @@ +package class06; + +import java.util.ArrayList; +import java.util.HashMap; + +public class Code04_MostXorZero { + + // 暴力方法 + public static int comparator(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + int[] eor = new int[N]; + eor[0] = arr[0]; + for (int i = 1; i < N; i++) { + eor[i] = eor[i - 1] ^ arr[i]; + } + return process(eor, 1, new ArrayList<>()); + } + + // index去决定:前一坨部分,结不结束! + // 如果结束!就把index放入到parts里去 + // 如果不结束,就不放 + public static int process(int[] eor, int index, ArrayList parts) { + int ans = 0; + if (index == eor.length) { + parts.add(eor.length); + ans = eorZeroParts(eor, parts); + parts.remove(parts.size() - 1); + } else { + int p1 = process(eor, index + 1, parts); + parts.add(index); + int p2 = process(eor, index + 1, parts); + parts.remove(parts.size() - 1); + ans = Math.max(p1, p2); + } + return ans; + } + + public static int eorZeroParts(int[] eor, ArrayList parts) { + int L = 0; + int ans = 0; + for (Integer end : parts) { + if ((eor[end - 1] ^ (L == 0 ? 0 : eor[L - 1])) == 0) { + ans++; + } + L = end; + } + return ans; + } + + // 时间复杂度O(N)的方法 + public static int mostXor(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + int[] dp = new int[N]; + + // key 某一个前缀异或和 + // value 这个前缀异或和上次出现的位置(最晚!) + HashMap map = new HashMap<>(); + map.put(0, -1); + // 0~i整体的异或和 + int xor = 0; + for (int i = 0; i < N; i++) { + xor ^= arr[i]; + if (map.containsKey(xor)) { // 可能性2 + int pre = map.get(xor); + dp[i] = pre == -1 ? 1 : (dp[pre] + 1); + } + if (i > 0) { + dp[i] = Math.max(dp[i - 1], dp[i]); + } + map.put(xor, i); + } + return dp[N - 1]; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 150000; + int maxSize = 12; + int maxValue = 5; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int res = mostXor(arr); + int comp = comparator(arr); + if (res != comp) { + succeed = false; + printArray(arr); + System.out.println(res); + System.out.println(comp); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/大厂刷题班/class06/Code05_Nim.java b/大厂刷题班/class06/Code05_Nim.java new file mode 100644 index 0000000..621ecc0 --- /dev/null +++ b/大厂刷题班/class06/Code05_Nim.java @@ -0,0 +1,18 @@ +package class06; + +public class Code05_Nim { + + // 保证arr是正数数组 + public static void printWinner(int[] arr) { + int eor = 0; + for (int num : arr) { + eor ^= num; + } + if (eor == 0) { + System.out.println("后手赢"); + } else { + System.out.println("先手赢"); + } + } + +} diff --git a/大厂刷题班/class07/Code01_MaxAndValue.java b/大厂刷题班/class07/Code01_MaxAndValue.java new file mode 100644 index 0000000..fb9bda9 --- /dev/null +++ b/大厂刷题班/class07/Code01_MaxAndValue.java @@ -0,0 +1,102 @@ +package class07; + +// 给定一个正数组成的数组,长度一定大于1,求数组中哪两个数与的结果最大 +public class Code01_MaxAndValue { + + // O(N^2)的暴力解 + public static int maxAndValue1(int[] arr) { + int N = arr.length; + int max = Integer.MIN_VALUE; + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + max = Math.max(max, arr[i] & arr[j]); + } + } + return max; + } + + // O(N)的解 + // 因为是正数,所以不用考虑符号位(31位) + // 首先来到30位,假设剩余的数字有N个(整体),看看这一位是1的数,有几个 + // 如果有0个、或者1个 + // 说明不管怎么在数组中选择,任何两个数&的结果在第30位上都不可能有1了 + // 答案在第30位上的状态一定是0, + // 保留剩余的N个数,继续考察第29位,谁也不淘汰(因为谁也不行,干脆接受30位上没有1的事实) + // 如果有2个, + // 说明答案就是这两个数(直接返回答案),因为别的数在第30位都没有1,就这两个数有。 + // 如果有>2个,比如K个 + // 说明答案一定只用在这K个数中去选择某两个数,因为别的数在第30位都没有1,就这K个数有。 + // 答案在第30位上的状态一定是1, + // 只把这K个数作为剩余的数,继续考察第29位,其他数都淘汰掉 + // ..... + // 现在来到i位,假设剩余的数字有M个,看看这一位是1的数,有几个 + // 如果有0个、或者1个 + // 说明不管怎么在M个数中选择,任何两个数&的结果在第i位上都不可能有1了 + // 答案在第i位上的状态一定是0, + // 保留剩余的M个数,继续考察第i-1位 + // 如果有2个, + // 说明答案就是这两个数(直接返回答案),因为别的数在第i位都没有1,就这两个数有。 + // 如果有>2个,比如K个 + // 说明答案一定只用在这K个数中去选择某两个数,因为别的数在第i位都没有1,就这K个数有。 + // 答案在第i位上的状态一定是1, + // 只把这K个数作为剩余的数,继续考察第i-1位,其他数都淘汰掉 + public static int maxAndValue2(int[] arr) { + // arr[0...M-1] arr[M....] + int M = arr.length; + int ans = 0; + for (int bit = 30; bit >= 0; bit--) { + // arr[0...M-1] arr[M...] + int i = 0; + int tmp = M; + while (i < M) { // arr[0...M-1] + if ((arr[i] & (1 << bit)) == 0) { + swap(arr, i, --M); + } else { + i++; + } + } + if (M == 2) { // arr[0,1] + return arr[0] & arr[1]; + } + if (M < 2) { + M = tmp; + } else { // > 2个数 bit位上有1 + ans |= (1 << bit); + } + } + return ans; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + public static int[] randomArray(int size, int range) { + int[] arr = new int[size]; + for (int i = 0; i < size; i++) { + arr[i] = (int) (Math.random() * range) + 1; + } + return arr; + } + + public static void main(String[] args) { + int maxSize = 50; + int range = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * maxSize) + 2; + int[] arr = randomArray(size, range); + int ans1 = maxAndValue1(arr); + int ans2 = maxAndValue2(arr); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + + } + +} diff --git a/大厂刷题班/class07/Code02_MinCameraCover.java b/大厂刷题班/class07/Code02_MinCameraCover.java new file mode 100644 index 0000000..ebde2ee --- /dev/null +++ b/大厂刷题班/class07/Code02_MinCameraCover.java @@ -0,0 +1,113 @@ +package class07; + +// 本题测试链接 : https://leetcode.com/problems/binary-tree-cameras/ +public class Code02_MinCameraCover { + + public static class TreeNode { + public int value; + public TreeNode left; + public TreeNode right; + } + + public static int minCameraCover1(TreeNode root) { + Info data = process1(root); + return (int) Math.min(data.uncovered + 1, Math.min(data.coveredNoCamera, data.coveredHasCamera)); + } + + // 潜台词:x是头节点,x下方的点都被覆盖的情况下 + public static class Info { + public long uncovered; // x没有被覆盖,x为头的树至少需要几个相机 + public long coveredNoCamera; // x被相机覆盖,但是x没相机,x为头的树至少需要几个相机 + public long coveredHasCamera; // x被相机覆盖了,并且x上放了相机,x为头的树至少需要几个相机 + + public Info(long un, long no, long has) { + uncovered = un; + coveredNoCamera = no; + coveredHasCamera = has; + } + } + + // 所有可能性都穷尽了 + public static Info process1(TreeNode X) { + if (X == null) { // base case + return new Info(Integer.MAX_VALUE, 0, Integer.MAX_VALUE); + } + + Info left = process1(X.left); + Info right = process1(X.right); + // x uncovered x自己不被覆盖,x下方所有节点,都被覆盖 + // 左孩子: 左孩子没被覆盖,左孩子以下的点都被覆盖 + // 左孩子被覆盖但没相机,左孩子以下的点都被覆盖 + // 左孩子被覆盖也有相机,左孩子以下的点都被覆盖 + long uncovered = left.coveredNoCamera + right.coveredNoCamera; + + // x下方的点都被covered,x也被cover,但x上没相机 + long coveredNoCamera = Math.min( + // 1) + left.coveredHasCamera + right.coveredHasCamera, + + Math.min( + // 2) + left.coveredHasCamera + right.coveredNoCamera, + + // 3) + left.coveredNoCamera + right.coveredHasCamera)); + + + + + // x下方的点都被covered,x也被cover,且x上有相机 + long coveredHasCamera = + Math.min(left.uncovered, Math.min(left.coveredNoCamera, left.coveredHasCamera)) + + + Math.min(right.uncovered, Math.min(right.coveredNoCamera, right.coveredHasCamera)) + + + 1; + + return new Info(uncovered, coveredNoCamera, coveredHasCamera); + } + + public static int minCameraCover2(TreeNode root) { + Data data = process2(root); + return data.cameras + (data.status == Status.UNCOVERED ? 1 : 0); + } + + // 以x为头,x下方的节点都是被covered,x自己的状况,分三种 + public static enum Status { + UNCOVERED, COVERED_NO_CAMERA, COVERED_HAS_CAMERA + } + + // 以x为头,x下方的节点都是被covered,得到的最优解中: + // x是什么状态,在这种状态下,需要至少几个相机 + public static class Data { + public Status status; + public int cameras; + + public Data(Status status, int cameras) { + this.status = status; + this.cameras = cameras; + } + } + + public static Data process2(TreeNode X) { + if (X == null) { + return new Data(Status.COVERED_NO_CAMERA, 0); + } + Data left = process2(X.left); + Data right = process2(X.right); + int cameras = left.cameras + right.cameras; + + // 左、或右,哪怕有一个没覆盖 + if (left.status == Status.UNCOVERED || right.status == Status.UNCOVERED) { + return new Data(Status.COVERED_HAS_CAMERA, cameras + 1); + } + + // 左右孩子,不存在没被覆盖的情况 + if (left.status == Status.COVERED_HAS_CAMERA || right.status == Status.COVERED_HAS_CAMERA) { + return new Data(Status.COVERED_NO_CAMERA, cameras); + } + // 左右孩子,不存在没被覆盖的情况,也都没有相机 + return new Data(Status.UNCOVERED, cameras); + } + +} diff --git a/大厂刷题班/class07/Code03_MaxGap.java b/大厂刷题班/class07/Code03_MaxGap.java new file mode 100644 index 0000000..486c6d3 --- /dev/null +++ b/大厂刷题班/class07/Code03_MaxGap.java @@ -0,0 +1,46 @@ +package class07; + +// 测试链接 : https://leetcode.com/problems/maximum-gap/ +public class Code03_MaxGap { + + public static int maximumGap(int[] nums) { + if (nums == null || nums.length < 2) { + return 0; + } + int len = nums.length; + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (int i = 0; i < len; i++) { + min = Math.min(min, nums[i]); + max = Math.max(max, nums[i]); + } + if (min == max) { + return 0; + } + boolean[] hasNum = new boolean[len + 1]; + int[] maxs = new int[len + 1]; + int[] mins = new int[len + 1]; + int bid = 0; + for (int i = 0; i < len; i++) { + bid = bucket(nums[i], len, min, max); + mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i]; + maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i]; + hasNum[bid] = true; + } + int res = 0; + int lastMax = maxs[0]; + int i = 1; + for (; i <= len; i++) { + if (hasNum[i]) { + res = Math.max(res, mins[i] - lastMax); + lastMax = maxs[i]; + } + } + return res; + } + + public static int bucket(long num, long len, long min, long max) { + return (int) ((num - min) * len / (max - min)); + } + +} diff --git a/大厂刷题班/class07/Code04_Power2Diffs.java b/大厂刷题班/class07/Code04_Power2Diffs.java new file mode 100644 index 0000000..ab6c553 --- /dev/null +++ b/大厂刷题班/class07/Code04_Power2Diffs.java @@ -0,0 +1,97 @@ +package class07; + +import java.util.Arrays; +import java.util.HashSet; + +public class Code04_Power2Diffs { + + /* + * 给定一个有序数组arr,其中值可能为正、负、0。 返回arr中每个数都平方之后不同的结果有多少种? + * + * 给定一个数组arr,先递减然后递增,返回arr中有多少个绝对值不同的数字? + * + */ + + // 时间复杂度O(N),额外空间复杂度O(N) + public static int diff1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + HashSet set = new HashSet<>(); + for (int cur : arr) { + set.add(cur * cur); + } + return set.size(); + } + + // 时间复杂度O(N),额外空间复杂度O(1) + public static int diff2(int[] arr) { + int N = arr.length; + int L = 0; + int R = N - 1; + int count = 0; + int leftAbs = 0; + int rightAbs = 0; + while (L <= R) { + count++; + leftAbs = Math.abs(arr[L]); + rightAbs = Math.abs(arr[R]); + if (leftAbs < rightAbs) { + while (R >= 0 && Math.abs(arr[R]) == rightAbs) { + R--; + } + } else if (leftAbs > rightAbs) { + while (L < N && Math.abs(arr[L]) == leftAbs) { + L++; + } + } else { + while (L < N && Math.abs(arr[L]) == leftAbs) { + L++; + } + while (R >= 0 && Math.abs(arr[R]) == rightAbs) { + R--; + } + } + } + return count; + } + + // for test + public static int[] randomSortedArray(int len, int value) { + int[] ans = new int[(int) (Math.random() * len) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * value) - (int) (Math.random() * value); + } + Arrays.sort(ans); + return ans; + } + + // for test + public static void printArray(int[] arr) { + for (int cur : arr) { + System.out.print(cur + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 100; + int value = 500; + int testTimes = 200000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + int[] arr = randomSortedArray(len, value); + int ans1 = diff1(arr); + int ans2 = diff2(arr); + if (ans1 != ans2) { + printArray(arr); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + break; + } + } + System.out.println("test finish"); + } + +} diff --git a/大厂刷题班/class07/Code05_WorldBreak.java b/大厂刷题班/class07/Code05_WorldBreak.java new file mode 100644 index 0000000..134073a --- /dev/null +++ b/大厂刷题班/class07/Code05_WorldBreak.java @@ -0,0 +1,237 @@ +package class07; + +import java.util.HashSet; + +public class Code05_WorldBreak { + /* + * + * 假设所有字符都是小写字母. 大字符串是str. arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次. + * 使用arr中的单词有多少种拼接str的方式. 返回方法数. + * + */ + + public static int ways(String str, String[] arr) { + HashSet set = new HashSet<>(); + for (String candidate : arr) { + set.add(candidate); + } + return process(str, 0, set); + } + + // 所有的可分解字符串,都已经放在了set中 + // str[i....] 能够被set中的贴纸分解的话,返回分解的方法数 + public static int process(String str, int i, HashSet set) { + if (i == str.length()) { // 没字符串需要分解了! + return 1; + } + // i....还有字符串需要分解 + int ways = 0; + // [i ... end] 前缀串 每一个前缀串 + for (int end = i; end < str.length(); end++) { + String pre = str.substring(i, end + 1);// [) + if (set.contains(pre)) { + ways += process(str, end + 1, set); + } + } + return ways; + } + + public static int ways1(String str, String[] arr) { + if (str == null || str.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + HashSet map = new HashSet<>(); + for (String s : arr) { + map.add(s); + } + return f(str, map, 0); + } + + public static int f(String str, HashSet map, int index) { + if (index == str.length()) { + return 1; + } + int ways = 0; + for (int end = index; end < str.length(); end++) { + if (map.contains(str.substring(index, end + 1))) { + ways += f(str, map, end + 1); + } + } + return ways; + } + + public static int ways2(String str, String[] arr) { + if (str == null || str.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + HashSet map = new HashSet<>(); + for (String s : arr) { + map.add(s); + } + int N = str.length(); + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + for (int end = i; end < N; end++) { + if (map.contains(str.substring(i, end + 1))) { + dp[i] += dp[end + 1]; + } + } + } + return dp[0]; + } + + public static class Node { + public boolean end; + public Node[] nexts; + + public Node() { + end = false; + nexts = new Node[26]; + } + } + + public static int ways3(String str, String[] arr) { + if (str == null || str.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + Node root = new Node(); + for (String s : arr) { + char[] chs = s.toCharArray(); + Node node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new Node(); + } + node = node.nexts[index]; + } + node.end = true; + } + return g(str.toCharArray(), root, 0); + } + + // str[i...] 被分解的方法数,返回 + + public static int g(char[] str, Node root, int i) { + if (i == str.length) { + return 1; + } + int ways = 0; + Node cur = root; + // i...end + for (int end = i; end < str.length; end++) { + int path = str[end] - 'a'; + if (cur.nexts[path] == null) { + break; + } + cur = cur.nexts[path]; + if (cur.end) { // i...end + ways += g(str, root, end + 1); + } + } + return ways; + } + + public static int ways4(String s, String[] arr) { + if (s == null || s.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + Node root = new Node(); + for (String str : arr) { + char[] chs = str.toCharArray(); + Node node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new Node(); + } + node = node.nexts[index]; + } + node.end = true; + } + char[] str = s.toCharArray(); + int N = str.length; + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + Node cur = root; + for (int end = i; end < N; end++) { + int path = str[end] - 'a'; + if (cur.nexts[path] == null) { + break; + } + cur = cur.nexts[path]; + if (cur.end) { + dp[i] += dp[end + 1]; + } + } + } + return dp[0]; + } + + // 以下的逻辑都是为了测试 + public static class RandomSample { + public String str; + public String[] arr; + + public RandomSample(String s, String[] a) { + str = s; + arr = a; + } + } + + // 随机样本产生器 + public static RandomSample generateRandomSample(char[] candidates, int num, int len, int joint) { + String[] seeds = randomSeeds(candidates, num, len); + HashSet set = new HashSet<>(); + for (String str : seeds) { + set.add(str); + } + String[] arr = new String[set.size()]; + int index = 0; + for (String str : set) { + arr[index++] = str; + } + StringBuilder all = new StringBuilder(); + for (int i = 0; i < joint; i++) { + all.append(arr[(int) (Math.random() * arr.length)]); + } + return new RandomSample(all.toString(), arr); + } + + public static String[] randomSeeds(char[] candidates, int num, int len) { + String[] arr = new String[(int) (Math.random() * num) + 1]; + for (int i = 0; i < arr.length; i++) { + char[] str = new char[(int) (Math.random() * len) + 1]; + for (int j = 0; j < str.length; j++) { + str[j] = candidates[(int) (Math.random() * candidates.length)]; + } + arr[i] = String.valueOf(str); + } + return arr; + } + + public static void main(String[] args) { + char[] candidates = { 'a', 'b' }; + int num = 20; + int len = 4; + int joint = 5; + int testTimes = 30000; + boolean testResult = true; + for (int i = 0; i < testTimes; i++) { + RandomSample sample = generateRandomSample(candidates, num, len, joint); + int ans1 = ways1(sample.str, sample.arr); + int ans2 = ways2(sample.str, sample.arr); + int ans3 = ways3(sample.str, sample.arr); + int ans4 = ways4(sample.str, sample.arr); + if (ans1 != ans2 || ans3 != ans4 || ans2 != ans4) { + testResult = false; + } + } + System.out.println(testTimes + "次随机测试是否通过:" + testResult); + } + +} diff --git a/大厂刷题班/class07/Code06_SplitStringMaxValue.java b/大厂刷题班/class07/Code06_SplitStringMaxValue.java new file mode 100644 index 0000000..c6e76cc --- /dev/null +++ b/大厂刷题班/class07/Code06_SplitStringMaxValue.java @@ -0,0 +1,137 @@ +package class07; + +import java.util.HashMap; + +public class Code06_SplitStringMaxValue { + + // 暴力解 + public static int maxRecord1(String str, int K, String[] parts, int[] record) { + if (str == null || str.length() == 0) { + return 0; + } + HashMap records = new HashMap<>(); + for (int i = 0; i < parts.length; i++) { + records.put(parts[i], record[i]); + } + return process(str, 0, K, records); + } + + public static int process(String str, int index, int rest, HashMap records) { + if (rest < 0) { + return -1; + } + if (index == str.length()) { + return rest == 0 ? 0 : -1; + } + int ans = -1; + for (int end = index; end < str.length(); end++) { + String first = str.substring(index, end + 1); + int next = records.containsKey(first) ? process(str, end + 1, rest - 1, records) : -1; + if (next != -1) { + ans = Math.max(ans, records.get(first) + next); + } + } + return ans; + } + + // 动态规划解 + public static int maxRecord2(String str, int K, String[] parts, int[] record) { + if (str == null || str.length() == 0) { + return 0; + } + HashMap records = new HashMap<>(); + for (int i = 0; i < parts.length; i++) { + records.put(parts[i], record[i]); + } + int N = str.length(); + int[][] dp = new int[N + 1][K + 1]; + for (int rest = 1; rest <= K; rest++) { + dp[N][rest] = -1; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= K; rest++) { + int ans = -1; + for (int end = index; end < N; end++) { + String first = str.substring(index, end + 1); + int next = rest > 0 && records.containsKey(first) ? dp[end + 1][rest - 1] : -1; + if (next != -1) { + ans = Math.max(ans, records.get(first) + next); + } + } + dp[index][rest] = ans; + } + } + return dp[0][K]; + } + + // 动态规划解 + 前缀树优化 + public static int maxRecord3(String s, int K, String[] parts, int[] record) { + if (s == null || s.length() == 0) { + return 0; + } + TrieNode root = rootNode(parts, record); + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N + 1][K + 1]; + for (int rest = 1; rest <= K; rest++) { + dp[N][rest] = -1; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= K; rest++) { + int ans = -1; + TrieNode cur = root; + for (int end = index; end < N; end++) { + int path = str[end] - 'a'; + if (cur.nexts[path] == null) { + break; + } + cur = cur.nexts[path]; + int next = rest > 0 && cur.value != -1 ? dp[end + 1][rest - 1] : -1; + if (next != -1) { + ans = Math.max(ans, cur.value + next); + } + } + dp[index][rest] = ans; + } + } + return dp[0][K]; + } + + public static class TrieNode { + public TrieNode[] nexts; + public int value; + + public TrieNode() { + nexts = new TrieNode[26]; + value = -1; + } + } + + public static TrieNode rootNode(String[] parts, int[] record) { + TrieNode root = new TrieNode(); + for (int i = 0; i < parts.length; i++) { + char[] str = parts[i].toCharArray(); + TrieNode cur = root; + for (int j = 0; j < str.length; j++) { + int path = str[j] - 'a'; + if (cur.nexts[path] == null) { + cur.nexts[path] = new TrieNode(); + } + cur = cur.nexts[path]; + } + cur.value = record[i]; + } + return root; + } + + public static void main(String[] args) { + String str = "abcdefg"; + int K = 3; + String[] parts = { "abc", "def", "g", "ab", "cd", "efg", "defg" }; + int[] record = { 1, 1, 1, 3, 3, 3, 2 }; + System.out.println(maxRecord1(str, K, parts, record)); + System.out.println(maxRecord2(str, K, parts, record)); + System.out.println(maxRecord3(str, K, parts, record)); + } + +} diff --git a/大厂刷题班/class08/Code01_ExpressionCompute.java b/大厂刷题班/class08/Code01_ExpressionCompute.java new file mode 100644 index 0000000..6d1192b --- /dev/null +++ b/大厂刷题班/class08/Code01_ExpressionCompute.java @@ -0,0 +1,71 @@ +package class08; + +import java.util.LinkedList; + +// 本题测试链接 : https://leetcode.com/problems/basic-calculator-iii/ +public class Code01_ExpressionCompute { + + public static int calculate(String str) { + return f(str.toCharArray(), 0)[0]; + } + + // 请从str[i...]往下算,遇到字符串终止位置或者右括号,就停止 + // 返回两个值,长度为2的数组 + // 0) 负责的这一段的结果是多少 + // 1) 负责的这一段计算到了哪个位置 + public static int[] f(char[] str, int i) { + LinkedList que = new LinkedList(); + int cur = 0; + int[] bra = null; + // 从i出发,开始撸串 + while (i < str.length && str[i] != ')') { + if (str[i] >= '0' && str[i] <= '9') { + cur = cur * 10 + str[i++] - '0'; + } else if (str[i] != '(') { // 遇到的是运算符号 + addNum(que, cur); + que.addLast(String.valueOf(str[i++])); + cur = 0; + } else { // 遇到左括号了 + bra = f(str, i + 1); + cur = bra[0]; + i = bra[1] + 1; + } + } + addNum(que, cur); + return new int[] { getNum(que), i }; + } + + public static void addNum(LinkedList que, int num) { + if (!que.isEmpty()) { + int cur = 0; + String top = que.pollLast(); + if (top.equals("+") || top.equals("-")) { + que.addLast(top); + } else { + cur = Integer.valueOf(que.pollLast()); + num = top.equals("*") ? (cur * num) : (cur / num); + } + } + que.addLast(String.valueOf(num)); + } + + public static int getNum(LinkedList que) { + int res = 0; + boolean add = true; + String cur = null; + int num = 0; + while (!que.isEmpty()) { + cur = que.pollFirst(); + if (cur.equals("+")) { + add = true; + } else if (cur.equals("-")) { + add = false; + } else { + num = Integer.valueOf(cur); + res += add ? num : (-num); + } + } + return res; + } + +} diff --git a/大厂刷题班/class08/Code02_ContainerWithMostWater.java b/大厂刷题班/class08/Code02_ContainerWithMostWater.java new file mode 100644 index 0000000..9053a24 --- /dev/null +++ b/大厂刷题班/class08/Code02_ContainerWithMostWater.java @@ -0,0 +1,32 @@ +package class08; + +// 本题测试链接 : https://leetcode.com/problems/container-with-most-water/ +public class Code02_ContainerWithMostWater { + + public static int maxArea1(int[] h) { + int max = 0; + int N = h.length; + for (int i = 0; i < N; i++) { // h[i] + for (int j = i + 1; j < N; j++) { // h[j] + max = Math.max(max, Math.min(h[i], h[j]) * (j - i)); + } + } + return max; + } + + public static int maxArea2(int[] h) { + int max = 0; + int l = 0; + int r = h.length - 1; + while (l < r) { + max = Math.max(max, Math.min(h[l], h[r]) * (r - l)); + if (h[l] > h[r]) { + r--; + } else { + l++; + } + } + return max; + } + +} diff --git a/大厂刷题班/class08/Code03_FindWordInMatrix.java b/大厂刷题班/class08/Code03_FindWordInMatrix.java new file mode 100644 index 0000000..e985c43 --- /dev/null +++ b/大厂刷题班/class08/Code03_FindWordInMatrix.java @@ -0,0 +1,159 @@ +package class08; + +/* + * 给定一个char[][] matrix,也就是char类型的二维数组,再给定一个字符串word, + * 可以从任何一个某个位置出发,可以走上下左右,能不能找到word? + * 比如: + * char[][] m = { + * { 'a', 'b', 'z' }, + * { 'c', 'd', 'o' }, + * { 'f', 'e', 'o' }, + * }; + * word = "zooe" + * 是可以找到的 + * + * 设定1:可以走重复路的情况下,返回能不能找到 + * 比如,word = "zoooz",是可以找到的,z -> o -> o -> o -> z,因为允许走一条路径中已经走过的字符 + * + * 设定2:不可以走重复路的情况下,返回能不能找到 + * 比如,word = "zoooz",是不可以找到的,因为允许走一条路径中已经走过的字符不能重复走 + * + * 写出两种设定下的code + * + * */ +public class Code03_FindWordInMatrix { + + // 可以走重复的设定 + public static boolean findWord1(char[][] m, String word) { + if (word == null || word.equals("")) { + return true; + } + if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) { + return false; + } + char[] w = word.toCharArray(); + int N = m.length; + int M = m[0].length; + int len = w.length; + // dp[i][j][k]表示:必须以m[i][j]这个字符结尾的情况下,能不能找到w[0...k]这个前缀串 + boolean[][][] dp = new boolean[N][M][len]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + dp[i][j][0] = m[i][j] == w[0]; + } + } + for (int k = 1; k < len; k++) { + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + dp[i][j][k] = (m[i][j] == w[k] && checkPrevious(dp, i, j, k)); + } + } + } + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (dp[i][j][len - 1]) { + return true; + } + } + } + return false; + } + + // 可以走重复路 + // 从m[i][j]这个字符出发,能不能找到str[k...]这个后缀串 + public static boolean canLoop(char[][] m, int i, int j, char[] str, int k) { + if (k == str.length) { + return true; + } + if (i == -1 || i == m.length || j == -1 || j == m[0].length || m[i][j] != str[k]) { + return false; + } + // 不越界!m[i][j] == str[k] 对的上的! + // str[k+1....] + boolean ans = false; + if (canLoop(m, i + 1, j, str, k + 1) || canLoop(m, i - 1, j, str, k + 1) || canLoop(m, i, j + 1, str, k + 1) + || canLoop(m, i, j - 1, str, k + 1)) { + ans = true; + } + return ans; + } + + // 不能走重复路 + // 从m[i][j]这个字符出发,能不能找到str[k...]这个后缀串 + public static boolean noLoop(char[][] m, int i, int j, char[] str, int k) { + if (k == str.length) { + return true; + } + if (i == -1 || i == m.length || j == -1 || j == m[0].length || m[i][j] != str[k]) { + return false; + } + // 不越界!也不是回头路!m[i][j] == str[k] 也对的上! + m[i][j] = 0; + boolean ans = false; + if (noLoop(m, i + 1, j, str, k + 1) || noLoop(m, i - 1, j, str, k + 1) || noLoop(m, i, j + 1, str, k + 1) + || noLoop(m, i, j - 1, str, k + 1)) { + ans = true; + } + m[i][j] = str[k]; + return ans; + } + + public static boolean checkPrevious(boolean[][][] dp, int i, int j, int k) { + boolean up = i > 0 ? (dp[i - 1][j][k - 1]) : false; + boolean down = i < dp.length - 1 ? (dp[i + 1][j][k - 1]) : false; + boolean left = j > 0 ? (dp[i][j - 1][k - 1]) : false; + boolean right = j < dp[0].length - 1 ? (dp[i][j + 1][k - 1]) : false; + return up || down || left || right; + } + + // 不可以走重复路的设定 + public static boolean findWord2(char[][] m, String word) { + if (word == null || word.equals("")) { + return true; + } + if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) { + return false; + } + char[] w = word.toCharArray(); + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[0].length; j++) { + if (process(m, i, j, w, 0)) { + return true; + } + } + } + return false; + } + + // 从m[i][j]这个字符出发,能不能找到w[k...]这个后缀串 + public static boolean process(char[][] m, int i, int j, char[] str, int k) { + if (k == str.length) { + return true; + } + if (i == -1 || i == m.length || j == -1 || j == m[0].length || m[i][j] == 0 || m[i][j] != str[k]) { + return false; + } + m[i][j] = 0; + boolean ans = false; + if (process(m, i + 1, j, str, k + 1) || process(m, i - 1, j, str, k + 1) || process(m, i, j + 1, str, k + 1) + || process(m, i, j - 1, str, k + 1)) { + ans = true; + } + m[i][j] = str[k]; + return ans; + } + + public static void main(String[] args) { + char[][] m = { { 'a', 'b', 'z' }, { 'c', 'd', 'o' }, { 'f', 'e', 'o' }, }; + String word1 = "zoooz"; + String word2 = "zoo"; + // 可以走重复路的设定 + System.out.println(findWord1(m, word1)); + System.out.println(findWord1(m, word2)); + // 不可以走重复路的设定 + System.out.println(findWord2(m, word1)); + System.out.println(findWord2(m, word2)); + + } + +} diff --git a/大厂刷题班/class08/Code04_SnakeGame.java b/大厂刷题班/class08/Code04_SnakeGame.java new file mode 100644 index 0000000..1aa5316 --- /dev/null +++ b/大厂刷题班/class08/Code04_SnakeGame.java @@ -0,0 +1,188 @@ +package class08; + +import java.util.Arrays; + +public class Code04_SnakeGame { + + public static int walk1(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return 0; + } + int res = Integer.MIN_VALUE; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + int[] ans = process(matrix, i, j); + res = Math.max(res, Math.max(ans[0], ans[1])); + } + } + return res; + } + + public static int zuo(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return 0; + } + int ans = 0; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + Info cur = f(matrix, i, j); + ans = Math.max(ans, Math.max(cur.no, cur.yes)); + } + } + return ans; + } + + public static class Info { + public int no; + public int yes; + + public Info(int n, int y) { + no = n; + yes = y; + } + } + + // 蛇从某一个最左列,且最优的空降点降落 + // 沿途走到(i,j)必须停! + // 返回,一次能力也不用,获得的最大成长值 + // 返回,用了一次能力,获得的最大成长值 + // 如果蛇从某一个最左列,且最优的空降点降落,不用能力,怎么都到不了(i,j),那么no = -1 + // 如果蛇从某一个最左列,且最优的空降点降落,用了一次能力,怎么都到不了(i,j),那么yes = -1 + public static Info f(int[][] matrix, int i, int j) { + if (j == 0) { // 最左列 + int no = Math.max(matrix[i][0], -1); + int yes = Math.max(-matrix[i][0], -1); + return new Info(no, yes); + } + // j > 0 不在最左列 + int preNo = -1; + int preYes = -1; + Info pre = f(matrix, i, j - 1); + preNo = Math.max(pre.no, preNo); + preYes = Math.max(pre.yes, preYes); + if (i > 0) { + pre = f(matrix, i - 1, j - 1); + preNo = Math.max(pre.no, preNo); + preYes = Math.max(pre.yes, preYes); + } + if (i < matrix.length - 1) { + pre = f(matrix, i + 1, j - 1); + preNo = Math.max(pre.no, preNo); + preYes = Math.max(pre.yes, preYes); + } + int no = preNo == -1 ? -1 : (Math.max(-1, preNo + matrix[i][j])); + // 能力只有一次,是之前用的! + int p1 = preYes == -1 ? -1 : (Math.max(-1, preYes + matrix[i][j])); + // 能力只有一次,就当前用! + int p2 = preNo == -1 ? -1 : (Math.max(-1, preNo - matrix[i][j])); + int yes = Math.max(Math.max(p1, p2), -1); + return new Info(no, yes); + } + + // 从假想的最优左侧到达(i,j)的旅程中 + // 0) 在没有使用过能力的情况下,返回路径最大和,没有可能到达的话,返回负 + // 1) 在使用过能力的情况下,返回路径最大和,没有可能到达的话,返回负 + public static int[] process(int[][] m, int i, int j) { + if (j == 0) { // (i,j)就是最左侧的位置 + return new int[] { m[i][j], -m[i][j] }; + } + int[] preAns = process(m, i, j - 1); + // 所有的路中,完全不使用能力的情况下,能够到达的最好长度是多大 + int preUnuse = preAns[0]; + // 所有的路中,使用过一次能力的情况下,能够到达的最好长度是多大 + int preUse = preAns[1]; + if (i - 1 >= 0) { + preAns = process(m, i - 1, j - 1); + preUnuse = Math.max(preUnuse, preAns[0]); + preUse = Math.max(preUse, preAns[1]); + } + if (i + 1 < m.length) { + preAns = process(m, i + 1, j - 1); + preUnuse = Math.max(preUnuse, preAns[0]); + preUse = Math.max(preUse, preAns[1]); + } + // preUnuse 之前旅程,没用过能力 + // preUse 之前旅程,已经使用过能力了 + int no = -1; // 之前没使用过能力,当前位置也不使用能力,的最优解 + int yes = -1; // 不管是之前使用能力,还是当前使用了能力,请保证能力只使用一次,最优解 + if (preUnuse >= 0) { + no = m[i][j] + preUnuse; + yes = -m[i][j] + preUnuse; + } + if (preUse >= 0) { + yes = Math.max(yes, m[i][j] + preUse); + } + return new int[] { no, yes }; + } + + public static int walk2(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + int[][][] dp = new int[matrix.length][matrix[0].length][2]; + for (int i = 0; i < dp.length; i++) { + dp[i][0][0] = matrix[i][0]; + dp[i][0][1] = -matrix[i][0]; + max = Math.max(max, Math.max(dp[i][0][0], dp[i][0][1])); + } + for (int j = 1; j < matrix[0].length; j++) { + for (int i = 0; i < matrix.length; i++) { + int preUnuse = dp[i][j - 1][0]; + int preUse = dp[i][j - 1][1]; + if (i - 1 >= 0) { + preUnuse = Math.max(preUnuse, dp[i - 1][j - 1][0]); + preUse = Math.max(preUse, dp[i - 1][j - 1][1]); + } + if (i + 1 < matrix.length) { + preUnuse = Math.max(preUnuse, dp[i + 1][j - 1][0]); + preUse = Math.max(preUse, dp[i + 1][j - 1][1]); + } + dp[i][j][0] = -1; + dp[i][j][1] = -1; + if (preUnuse >= 0) { + dp[i][j][0] = matrix[i][j] + preUnuse; + dp[i][j][1] = -matrix[i][j] + preUnuse; + } + if (preUse >= 0) { + dp[i][j][1] = Math.max(dp[i][j][1], matrix[i][j] + preUse); + } + max = Math.max(max, Math.max(dp[i][j][0], dp[i][j][1])); + } + } + return max; + } + + public static int[][] generateRandomArray(int row, int col, int value) { + int[][] arr = new int[row][col]; + for (int i = 0; i < arr.length; i++) { + for (int j = 0; j < arr[0].length; j++) { + arr[i][j] = (int) (Math.random() * value) * (Math.random() > 0.5 ? -1 : 1); + } + } + return arr; + } + + public static void main(String[] args) { + int N = 7; + int M = 7; + int V = 10; + int times = 1000000; + for (int i = 0; i < times; i++) { + int r = (int) (Math.random() * (N + 1)); + int c = (int) (Math.random() * (M + 1)); + int[][] matrix = generateRandomArray(r, c, V); + int ans1 = zuo(matrix); + int ans2 = walk2(matrix); + if (ans1 != ans2) { + for (int j = 0; j < matrix.length; j++) { + System.out.println(Arrays.toString(matrix[j])); + } + System.out.println("Oops ans1: " + ans1 + " ans2:" + ans2); + break; + } + } + System.out.println("finish"); + } + +} diff --git a/大厂刷题班/class09/Code01_LightProblem.java b/大厂刷题班/class09/Code01_LightProblem.java new file mode 100644 index 0000000..c42f962 --- /dev/null +++ b/大厂刷题班/class09/Code01_LightProblem.java @@ -0,0 +1,384 @@ +package class09; + +/* + * 给定一个数组arr,长度为N,arr中的值不是0就是1 + * arr[i]表示第i栈灯的状态,0代表灭灯,1代表亮灯 + * 每一栈灯都有开关,但是按下i号灯的开关,会同时改变i-1、i、i+2栈灯的状态 + * 问题一: + * 如果N栈灯排成一条直线,请问最少按下多少次开关,能让灯都亮起来 + * 排成一条直线说明: + * i为中间位置时,i号灯的开关能影响i-1、i和i+1 + * 0号灯的开关只能影响0和1位置的灯 + * N-1号灯的开关只能影响N-2和N-1位置的灯 + * + * 问题二: + * 如果N栈灯排成一个圈,请问最少按下多少次开关,能让灯都亮起来 + * 排成一个圈说明: + * i为中间位置时,i号灯的开关能影响i-1、i和i+1 + * 0号灯的开关能影响N-1、0和1位置的灯 + * N-1号灯的开关能影响N-2、N-1和0位置的灯 + * + * */ +public class Code01_LightProblem { + + // 无环改灯问题的暴力版本 + public static int noLoopRight(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0] == 1 ? 0 : 1; + } + if (arr.length == 2) { + return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + return f1(arr, 0); + } + + public static int f1(int[] arr, int i) { + if (i == arr.length) { + return valid(arr) ? 0 : Integer.MAX_VALUE; + } + int p1 = f1(arr, i + 1); + change1(arr, i); + int p2 = f1(arr, i + 1); + change1(arr, i); + p2 = (p2 == Integer.MAX_VALUE) ? p2 : (p2 + 1); + return Math.min(p1, p2); + } + + public static void change1(int[] arr, int i) { + if (i == 0) { + arr[0] ^= 1; + arr[1] ^= 1; + } else if (i == arr.length - 1) { + arr[i - 1] ^= 1; + arr[i] ^= 1; + } else { + arr[i - 1] ^= 1; + arr[i] ^= 1; + arr[i + 1] ^= 1; + } + } + + public static boolean valid(int[] arr) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] == 0) { + return false; + } + } + return true; + } + + // 无环改灯问题的递归版本 + public static int noLoopMinStep1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0] ^ 1; + } + if (arr.length == 2) { + return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + // 不变0位置的状态 + int p1 = process1(arr, 2, arr[0], arr[1]); + // 改变0位置的状态 + int p2 = process1(arr, 2, arr[0] ^ 1, arr[1] ^ 1); + if (p2 != Integer.MAX_VALUE) { + p2++; + } + return Math.min(p1, p2); + } + + // 当前在哪个位置上,做选择,nextIndex - 1 = cur ,当前! + // cur - 1 preStatus + // cur curStatus + // 0....cur-2 全亮的! + public static int process1(int[] arr, int nextIndex, int preStatus, int curStatus) { + if (nextIndex == arr.length) { // 当前来到最后一个开关的位置 + return preStatus != curStatus ? (Integer.MAX_VALUE) : (curStatus ^ 1); + } + // 没到最后一个按钮呢! + // i < arr.length + if (preStatus == 0) { // 一定要改变 + curStatus ^= 1; + int cur = arr[nextIndex] ^ 1; + int next = process1(arr, nextIndex + 1, curStatus, cur); + return next == Integer.MAX_VALUE ? next : (next + 1); + } else { // 一定不能改变 + return process1(arr, nextIndex + 1, curStatus, arr[nextIndex]); + } + } + + // 无环改灯问题的迭代版本 + public static int noLoopMinStep2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0] == 1 ? 0 : 1; + } + if (arr.length == 2) { + return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + int p1 = traceNoLoop(arr, arr[0], arr[1]); + int p2 = traceNoLoop(arr, arr[0] ^ 1, arr[1] ^ 1); + p2 = (p2 == Integer.MAX_VALUE) ? p2 : (p2 + 1); + return Math.min(p1, p2); + } + + public static int traceNoLoop(int[] arr, int preStatus, int curStatus) { + int i = 2; + int op = 0; + while (i != arr.length) { + if (preStatus == 0) { + op++; + preStatus = curStatus ^ 1; + curStatus = arr[i++] ^ 1; + } else { + preStatus = curStatus; + curStatus = arr[i++]; + } + } + return (preStatus != curStatus) ? Integer.MAX_VALUE : (op + (curStatus ^ 1)); + } + + // 有环改灯问题的暴力版本 + public static int loopRight(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0] == 1 ? 0 : 1; + } + if (arr.length == 2) { + return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + return f2(arr, 0); + } + + public static int f2(int[] arr, int i) { + if (i == arr.length) { + return valid(arr) ? 0 : Integer.MAX_VALUE; + } + int p1 = f2(arr, i + 1); + change2(arr, i); + int p2 = f2(arr, i + 1); + change2(arr, i); + p2 = (p2 == Integer.MAX_VALUE) ? p2 : (p2 + 1); + return Math.min(p1, p2); + } + + public static void change2(int[] arr, int i) { + arr[lastIndex(i, arr.length)] ^= 1; + arr[i] ^= 1; + arr[nextIndex(i, arr.length)] ^= 1; + } + + public static int lastIndex(int i, int N) { + return i == 0 ? (N - 1) : (i - 1); + } + + public static int nextIndex(int i, int N) { + return i == N - 1 ? 0 : (i + 1); + } + + // 有环改灯问题的递归版本 + public static int loopMinStep1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0] == 1 ? 0 : 1; + } + if (arr.length == 2) { + return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + if (arr.length == 3) { + return (arr[0] != arr[1] || arr[0] != arr[2]) ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + // 0不变,1不变 + int p1 = process2(arr, 3, arr[1], arr[2], arr[arr.length - 1], arr[0]); + // 0改变,1不变 + int p2 = process2(arr, 3, arr[1] ^ 1, arr[2], arr[arr.length - 1] ^ 1, arr[0] ^ 1); + // 0不变,1改变 + int p3 = process2(arr, 3, arr[1] ^ 1, arr[2] ^ 1, arr[arr.length - 1], arr[0] ^ 1); + // 0改变,1改变 + int p4 = process2(arr, 3, arr[1], arr[2] ^ 1, arr[arr.length - 1] ^ 1, arr[0]); + p2 = p2 != Integer.MAX_VALUE ? (p2 + 1) : p2; + p3 = p3 != Integer.MAX_VALUE ? (p3 + 1) : p3; + p4 = p4 != Integer.MAX_VALUE ? (p4 + 2) : p4; + return Math.min(Math.min(p1, p2), Math.min(p3, p4)); + } + + + // 下一个位置是,nextIndex + // 当前位置是,nextIndex - 1 -> curIndex + // 上一个位置是, nextIndex - 2 -> preIndex preStatus + // 当前位置是,nextIndex - 1, curStatus + // endStatus, N-1位置的状态 + // firstStatus, 0位置的状态 + // 返回,让所有灯都亮,至少按下几次按钮 + + // 当前来到的位置(nextIndex - 1),一定不能是1!至少从2开始 + // nextIndex >= 3 + public static int process2(int[] arr, + int nextIndex, int preStatus, int curStatus, + int endStatus, int firstStatus) { + + if (nextIndex == arr.length) { // 最后一按钮! + return (endStatus != firstStatus || endStatus != preStatus) ? Integer.MAX_VALUE : (endStatus ^ 1); + } + // 当前位置,nextIndex - 1 + // 当前的状态,叫curStatus + // 如果不按下按钮,下一步的preStatus, curStatus + // 如果按下按钮,下一步的preStatus, curStatus ^ 1 + // 如果不按下按钮,下一步的curStatus, arr[nextIndex] + // 如果按下按钮,下一步的curStatus, arr[nextIndex] ^ 1 + int noNextPreStatus = 0; + int yesNextPreStatus = 0; + int noNextCurStatus =0; + int yesNextCurStatus = 0; + int noEndStatus = 0; + int yesEndStatus = 0; + if(nextIndex < arr.length - 1) {// 当前没来到N-2位置 + noNextPreStatus = curStatus; + yesNextPreStatus = curStatus ^ 1; + noNextCurStatus = arr[nextIndex]; + yesNextCurStatus = arr[nextIndex] ^ 1; + } else if(nextIndex == arr.length - 1) {// 当前来到的就是N-2位置 + noNextPreStatus = curStatus; + yesNextPreStatus = curStatus ^ 1; + noNextCurStatus = endStatus; + yesNextCurStatus = endStatus ^ 1; + noEndStatus = endStatus; + yesEndStatus = endStatus ^ 1; + } + if(preStatus == 0) { + int next = process2(arr, nextIndex + 1, yesNextPreStatus, yesNextCurStatus, + nextIndex == arr.length - 1 ? yesEndStatus : endStatus, firstStatus); + return next == Integer.MAX_VALUE ? next : (next + 1); + }else { + return process2(arr, nextIndex + 1, noNextPreStatus, noNextCurStatus, + nextIndex == arr.length - 1 ? noEndStatus : endStatus, firstStatus); + + } +// int curStay = (nextIndex == arr.length - 1) ? endStatus : arr[nextIndex]; +// int curChange = (nextIndex == arr.length - 1) ? (endStatus ^ 1) : (arr[nextIndex] ^ 1); +// int endChange = (nextIndex == arr.length - 1) ? curChange : endStatus; +// if (preStatus == 0) { +// int next = process2(arr, nextIndex + 1, curStatus ^ 1, curChange, endChange, firstStatus); +// return next == Integer.MAX_VALUE ? next : (next + 1); +// } else { +// return process2(arr, nextIndex + 1, curStatus, curStay, endStatus, firstStatus); +// } + } + + // 有环改灯问题的迭代版本 + public static int loopMinStep2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0] == 1 ? 0 : 1; + } + if (arr.length == 2) { + return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + if (arr.length == 3) { + return (arr[0] != arr[1] || arr[0] != arr[2]) ? Integer.MAX_VALUE : (arr[0] ^ 1); + } + // 0不变,1不变 + int p1 = traceLoop(arr, arr[1], arr[2], arr[arr.length - 1], arr[0]); + // 0改变,1不变 + int p2 = traceLoop(arr, arr[1] ^ 1, arr[2], arr[arr.length - 1] ^ 1, arr[0] ^ 1); + // 0不变,1改变 + int p3 = traceLoop(arr, arr[1] ^ 1, arr[2] ^ 1, arr[arr.length - 1], arr[0] ^ 1); + // 0改变,1改变 + int p4 = traceLoop(arr, arr[1], arr[2] ^ 1, arr[arr.length - 1] ^ 1, arr[0]); + p2 = p2 != Integer.MAX_VALUE ? (p2 + 1) : p2; + p3 = p3 != Integer.MAX_VALUE ? (p3 + 1) : p3; + p4 = p4 != Integer.MAX_VALUE ? (p4 + 2) : p4; + return Math.min(Math.min(p1, p2), Math.min(p3, p4)); + } + + public static int traceLoop(int[] arr, int preStatus, int curStatus, int endStatus, int firstStatus) { + int i = 3; + int op = 0; + while (i < arr.length - 1) { + if (preStatus == 0) { + op++; + preStatus = curStatus ^ 1; + curStatus = (arr[i++] ^ 1); + } else { + preStatus = curStatus; + curStatus = arr[i++]; + } + } + if (preStatus == 0) { + op++; + preStatus = curStatus ^ 1; + endStatus ^= 1; + curStatus = endStatus; + } else { + preStatus = curStatus; + curStatus = endStatus; + } + return (endStatus != firstStatus || endStatus != preStatus) ? Integer.MAX_VALUE : (op + (endStatus ^ 1)); + } + + // 生成长度为len的随机数组,值只有0和1两种值 + public static int[] randomArray(int len) { + int[] arr = new int[len]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * 2); + } + return arr; + } + + public static void main(String[] args) { + System.out.println("如果没有任何Oops打印,说明所有方法都正确"); + System.out.println("test begin"); + int testTime = 20000; + int lenMax = 12; + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * lenMax); + int[] arr = randomArray(len); + int ans1 = noLoopRight(arr); + int ans2 = noLoopMinStep1(arr); + int ans3 = noLoopMinStep2(arr); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("1 Oops!"); + } + } + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * lenMax); + int[] arr = randomArray(len); + int ans1 = loopRight(arr); + int ans2 = loopMinStep1(arr); + int ans3 = loopMinStep2(arr); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("2 Oops!"); + } + } + System.out.println("test end"); + + int len = 100000000; + System.out.println("性能测试"); + System.out.println("数组大小:" + len); + int[] arr = randomArray(len); + long start = 0; + long end = 0; + start = System.currentTimeMillis(); + noLoopMinStep2(arr); + end = System.currentTimeMillis(); + System.out.println("noLoopMinStep2 run time: " + (end - start) + "(ms)"); + + start = System.currentTimeMillis(); + loopMinStep2(arr); + end = System.currentTimeMillis(); + System.out.println("loopMinStep2 run time: " + (end - start) + "(ms)"); + } + +} diff --git a/大厂刷题班/class09/Code02_RemoveInvalidParentheses.java b/大厂刷题班/class09/Code02_RemoveInvalidParentheses.java new file mode 100644 index 0000000..f13423e --- /dev/null +++ b/大厂刷题班/class09/Code02_RemoveInvalidParentheses.java @@ -0,0 +1,64 @@ +package class09; + +import java.util.ArrayList; +import java.util.List; + +// 测试链接 : https://leetcode.com/problems/remove-invalid-parentheses/ +public class Code02_RemoveInvalidParentheses { + + // 来自leetcode投票第一的答案,实现非常好,我们来赏析一下 + public static List removeInvalidParentheses(String s) { + List ans = new ArrayList<>(); + remove(s, ans, 0, 0, new char[] { '(', ')' }); + return ans; + } + + // modifyIndex <= checkIndex + // 只查s[checkIndex....]的部分,因为之前的一定已经调整对了 + // 但是之前的部分是怎么调整对的,调整到了哪?就是modifyIndex + // 比如: + // ( ( ) ( ) ) ) ... + // 0 1 2 3 4 5 6 + // 一开始当然checkIndex = 0,modifyIndex = 0 + // 当查到6的时候,发现不对了, + // 然后可以去掉2位置、4位置的 ),都可以 + // 如果去掉2位置的 ), 那么下一步就是 + // ( ( ( ) ) ) ... + // 0 1 2 3 4 5 6 + // checkIndex = 6 ,modifyIndex = 2 + // 如果去掉4位置的 ), 那么下一步就是 + // ( ( ) ( ) ) ... + // 0 1 2 3 4 5 6 + // checkIndex = 6 ,modifyIndex = 4 + // 也就是说, + // checkIndex和modifyIndex,分别表示查的开始 和 调的开始,之前的都不用管了 par ( ) + public static void remove(String s, List ans, int checkIndex, int deleteIndex, char[] par) { + for (int count = 0, i = checkIndex; i < s.length(); i++) { + if (s.charAt(i) == par[0]) { + count++; + } + if (s.charAt(i) == par[1]) { + count--; + } + // i check计数<0的第一个位置 + if (count < 0) { + for (int j = deleteIndex; j <= i; ++j) { + // 比如 + if (s.charAt(j) == par[1] && (j == deleteIndex || s.charAt(j - 1) != par[1])) { + remove( + s.substring(0, j) + s.substring(j + 1, s.length()), + ans, i, j, par); + } + } + return; + } + } + String reversed = new StringBuilder(s).reverse().toString(); + if (par[0] == '(') { + remove(reversed, ans, 0, 0, new char[] { ')', '(' }); + } else { + ans.add(reversed); + } + } + +} diff --git a/大厂刷题班/class09/Code03_LIS.java b/大厂刷题班/class09/Code03_LIS.java new file mode 100644 index 0000000..42e197b --- /dev/null +++ b/大厂刷题班/class09/Code03_LIS.java @@ -0,0 +1,55 @@ +package class09; + +// 本题测试链接 : https://leetcode.com/problems/longest-increasing-subsequence +public class Code03_LIS { + + public static int lengthOfLIS(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + // ends数组 + // ends[i]表示 : 目前所有长度为i+1的递增子序列的最小结尾 + int[] ends = new int[arr.length]; + // 根据含义, 一开始ends[0] = arr[0] + ends[0] = arr[0]; + // ends有效区范围是0...right,right往右为无效区 + // 所以一开始right = 0, 表示有效区只有0...0范围 + int right = 0; + // 最长递增子序列的长度 + // 全局变量,抓取每一步的答案,取最大的结果 + int max = 1; + for (int i = 1; i < arr.length; i++) { + int l = 0; + int r = right; + // 在ends[l...r]范围上二分 + // 如果 当前数(arr[i]) > ends[m],砍掉左侧 + // 如果 当前数(arr[i]) <= ends[m],砍掉右侧 + // 整个二分就是在ends里寻找 >= 当前数(arr[i])的最左位置 + // 就是从while里面出来时,l所在的位置。 + // 如果ends中不存在 >= 当前数(arr[i])的情况,将返回有效区的越界位置 + // 也就是从while里面出来时,l所在的位置,是有效区的越界位置 + // 比如 : ends = { 3, 5, 9, 12, 再往右无效} + // 如果当前数为8, 从while里面出来时,l将来到2位置 + // 比如 : ends = { 3, 5, 9, 12, 再往右无效} + // 如果当前数为13, 从while里面出来时,l将来到有效区的越界位置,4位置 + while (l <= r) { + int m = (l + r) / 2; + if (arr[i] > ends[m]) { + l = m + 1; + } else { + r = m - 1; + } + } + // 从while里面出来,看l的位置 + // 如果l比right大,说明扩充了有效区,那么right变量要随之变大 + // 如果l不比right大,说明l没有来到有效区的越界位置,right不变 + right = Math.max(right, l); + // l的位置,就是当前数应该填到ends数组里的位置 + ends[l] = arr[i]; + // 更新全局变量 + max = Math.max(max, l + 1); + } + return max; + } + +} \ No newline at end of file diff --git a/大厂刷题班/class09/Code04_EnvelopesProblem.java b/大厂刷题班/class09/Code04_EnvelopesProblem.java new file mode 100644 index 0000000..ea22dd4 --- /dev/null +++ b/大厂刷题班/class09/Code04_EnvelopesProblem.java @@ -0,0 +1,60 @@ +package class09; + +import java.util.Arrays; +import java.util.Comparator; + +// 本题测试链接 : https://leetcode.com/problems/russian-doll-envelopes/ +public class Code04_EnvelopesProblem { + + public static int maxEnvelopes(int[][] matrix) { + Envelope[] arr = sort(matrix); + int[] ends = new int[matrix.length]; + ends[0] = arr[0].h; + int right = 0; + int l = 0; + int r = 0; + int m = 0; + for (int i = 1; i < arr.length; i++) { + l = 0; + r = right; + while (l <= r) { + m = (l + r) / 2; + if (arr[i].h > ends[m]) { + l = m + 1; + } else { + r = m - 1; + } + } + right = Math.max(right, l); + ends[l] = arr[i].h; + } + return right + 1; + } + + public static class Envelope { + public int l; + public int h; + + public Envelope(int weight, int hight) { + l = weight; + h = hight; + } + } + + public static class EnvelopeComparator implements Comparator { + @Override + public int compare(Envelope o1, Envelope o2) { + return o1.l != o2.l ? o1.l - o2.l : o2.h - o1.h; + } + } + + public static Envelope[] sort(int[][] matrix) { + Envelope[] res = new Envelope[matrix.length]; + for (int i = 0; i < matrix.length; i++) { + res[i] = new Envelope(matrix[i][0], matrix[i][1]); + } + Arrays.sort(res, new EnvelopeComparator()); + return res; + } + +} diff --git a/大厂刷题班/class09/Code05_IsStepSum.java b/大厂刷题班/class09/Code05_IsStepSum.java new file mode 100644 index 0000000..5bdd807 --- /dev/null +++ b/大厂刷题班/class09/Code05_IsStepSum.java @@ -0,0 +1,58 @@ +package class09; + +import java.util.HashMap; + +public class Code05_IsStepSum { + + public static boolean isStepSum(int stepSum) { + int L = 0; + int R = stepSum; + int M = 0; + int cur = 0; + while (L <= R) { + M = L + ((R - L) >> 1); + cur = stepSum(M); + if (cur == stepSum) { + return true; + } else if (cur < stepSum) { + L = M + 1; + } else { + R = M - 1; + } + } + return false; + } + + public static int stepSum(int num) { + int sum = 0; + while (num != 0) { + sum += num; + num /= 10; + } + return sum; + } + + // for test + public static HashMap generateStepSumNumberMap(int numMax) { + HashMap map = new HashMap<>(); + for (int i = 0; i <= numMax; i++) { + map.put(stepSum(i), i); + } + return map; + } + + // for test + public static void main(String[] args) { + int max = 1000000; + int maxStepSum = stepSum(max); + HashMap ans = generateStepSumNumberMap(max); + System.out.println("测试开始"); + for (int i = 0; i <= maxStepSum; i++) { + if (isStepSum(i) ^ ans.containsKey(i)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class10/Code01_JumpGame.java b/大厂刷题班/class10/Code01_JumpGame.java new file mode 100644 index 0000000..bcb2fe1 --- /dev/null +++ b/大厂刷题班/class10/Code01_JumpGame.java @@ -0,0 +1,23 @@ +package class10; + +// 本题测试链接 : https://leetcode.com/problems/jump-game-ii/ +public class Code01_JumpGame { + + public static int jump(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int step = 0; + int cur = 0; + int next = 0; + for (int i = 0; i < arr.length; i++) { + if (cur < i) { + step++; + cur = next; + } + next = Math.max(next, i + arr[i]); + } + return step; + } + +} diff --git a/大厂刷题班/class10/Code02_TopK.java b/大厂刷题班/class10/Code02_TopK.java new file mode 100644 index 0000000..d563da6 --- /dev/null +++ b/大厂刷题班/class10/Code02_TopK.java @@ -0,0 +1,154 @@ +package class10; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.TreeSet; + +// 本题测试链接:https://www.lintcode.com/problem/top-k-frequent-words-ii/ +// 以上的代码不要粘贴, 把以下的代码粘贴进java环境编辑器 +// 把类名和构造方法名改成TopK, 可以直接通过 +public class Code02_TopK { + + private Node[] heap; + private int heapSize; + // 词频表 key abc value (abc,7) + private HashMap strNodeMap; + private HashMap nodeIndexMap; + private NodeHeapComp comp; + private TreeSet treeSet; + + public Code02_TopK(int K) { + heap = new Node[K]; + heapSize = 0; + strNodeMap = new HashMap(); + nodeIndexMap = new HashMap(); + comp = new NodeHeapComp(); + treeSet = new TreeSet<>(new NodeTreeSetComp()); + } + + public static class Node { + public String str; + public int times; + + public Node(String s, int t) { + str = s; + times = t; + } + } + + public static class NodeHeapComp implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.times != o2.times ? (o1.times - o2.times) : (o2.str.compareTo(o1.str)); + } + + } + + public static class NodeTreeSetComp implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.times != o2.times ? (o2.times - o1.times) : (o1.str.compareTo(o2.str)); + } + + } + + public void add(String str) { + if (heap.length == 0) { + return; + } + // str 找到对应节点 curNode + Node curNode = null; + // 对应节点 curNode 在堆上的位置 + int preIndex = -1; + if (!strNodeMap.containsKey(str)) { + curNode = new Node(str, 1); + strNodeMap.put(str, curNode); + nodeIndexMap.put(curNode, -1); + } else { + curNode = strNodeMap.get(str); + // 要在time++之前,先在treeSet中删掉 + // 原因是因为一但times++,curNode在treeSet中的排序就失效了 + // 这种失效会导致整棵treeSet出现问题 + if (treeSet.contains(curNode)) { + treeSet.remove(curNode); + } + curNode.times++; + preIndex = nodeIndexMap.get(curNode); + } + if (preIndex == -1) { + if (heapSize == heap.length) { + if (comp.compare(heap[0], curNode) < 0) { + treeSet.remove(heap[0]); + treeSet.add(curNode); + nodeIndexMap.put(heap[0], -1); + nodeIndexMap.put(curNode, 0); + heap[0] = curNode; + heapify(0, heapSize); + } + } else { + treeSet.add(curNode); + nodeIndexMap.put(curNode, heapSize); + heap[heapSize] = curNode; + heapInsert(heapSize++); + } + } else { + treeSet.add(curNode); + heapify(preIndex, heapSize); + } + } + + public List topk() { + ArrayList ans = new ArrayList<>(); + for (Node node : treeSet) { + ans.add(node.str); + } + return ans; + } + + private void heapInsert(int index) { + while (index != 0) { + int parent = (index - 1) / 2; + if (comp.compare(heap[index], heap[parent]) < 0) { + swap(parent, index); + index = parent; + } else { + break; + } + } + } + + private void heapify(int index, int heapSize) { + int l = index * 2 + 1; + int r = index * 2 + 2; + int smallest = index; + while (l < heapSize) { + if (comp.compare(heap[l], heap[index]) < 0) { + smallest = l; + } + if (r < heapSize && comp.compare(heap[r], heap[smallest]) < 0) { + smallest = r; + } + if (smallest != index) { + swap(smallest, index); + } else { + break; + } + index = smallest; + l = index * 2 + 1; + r = index * 2 + 2; + } + } + + private void swap(int index1, int index2) { + nodeIndexMap.put(heap[index1], index2); + nodeIndexMap.put(heap[index2], index1); + Node tmp = heap[index1]; + heap[index1] = heap[index2]; + heap[index2] = tmp; + } + +} \ No newline at end of file diff --git a/大厂刷题班/class10/Code03_KInversePairs.java b/大厂刷题班/class10/Code03_KInversePairs.java new file mode 100644 index 0000000..79a6775 --- /dev/null +++ b/大厂刷题班/class10/Code03_KInversePairs.java @@ -0,0 +1,43 @@ +package class10; + +// 测试链接 : https://leetcode.com/problems/k-inverse-pairs-array/ +public class Code03_KInversePairs { + + public static int kInversePairs(int n, int k) { + if (n < 1 || k < 0) { + return 0; + } + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + int mod = 1000000007; + for (int i = 1; i <= n; i++) { + dp[i][0] = 1; + for (int j = 1; j <= k; j++) { + dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % mod; + if (j >= i) { + dp[i][j] = (dp[i][j] - dp[i - 1][j - i] + mod) % mod; + } + } + } + return dp[n][k]; + } + + public static int kInversePairs2(int n, int k) { + if (n < 1 || k < 0) { + return 0; + } + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + for (int i = 1; i <= n; i++) { + dp[i][0] = 1; + for (int j = 1; j <= k; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + if (j >= i) { + dp[i][j] -= dp[i - 1][j - i]; + } + } + } + return dp[n][k]; + } + +} diff --git a/大厂刷题班/class10/Code04_BSTtoDoubleLinkedList.java b/大厂刷题班/class10/Code04_BSTtoDoubleLinkedList.java new file mode 100644 index 0000000..b937b66 --- /dev/null +++ b/大厂刷题班/class10/Code04_BSTtoDoubleLinkedList.java @@ -0,0 +1,57 @@ +package class10; + +// 本题测试链接 : https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/ +public class Code04_BSTtoDoubleLinkedList { + + // 提交时不要提交这个类 + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int data) { + this.value = data; + } + } + + // 提交下面的代码 + public static Node treeToDoublyList(Node head) { + if (head == null) { + return null; + } + Info allInfo = process(head); + allInfo.end.right = allInfo.start; + allInfo.start.left = allInfo.end; + return allInfo.start; + } + + public static class Info { + public Node start; + public Node end; + + public Info(Node start, Node end) { + this.start = start; + this.end = end; + } + } + + public static Info process(Node X) { + if (X == null) { + return new Info(null, null); + } + Info lInfo = process(X.left); + Info rInfo = process(X.right); + if (lInfo.end != null) { + lInfo.end.right = X; + } + X.left = lInfo.end; + X.right = rInfo.start; + if (rInfo.start != null) { + rInfo.start.left = X; + } + // 整体链表的头 lInfo.start != null ? lInfo.start : X + // 整体链表的尾 rInfo.end != null ? rInfo.end : X + return new Info(lInfo.start != null ? lInfo.start : X, rInfo.end != null ? rInfo.end : X); + } + +} \ No newline at end of file diff --git a/大厂刷题班/class10/Code05_BooleanEvaluation.java b/大厂刷题班/class10/Code05_BooleanEvaluation.java new file mode 100644 index 0000000..9219a79 --- /dev/null +++ b/大厂刷题班/class10/Code05_BooleanEvaluation.java @@ -0,0 +1,156 @@ +package class10; + +// 本题测试链接 : https://leetcode-cn.com/problems/boolean-evaluation-lcci/ +public class Code05_BooleanEvaluation { + + public static int countEval0(String express, int desired) { + if (express == null || express.equals("")) { + return 0; + } + char[] exp = express.toCharArray(); + int N = exp.length; + Info[][] dp = new Info[N][N]; + Info allInfo = func(exp, 0, exp.length - 1, dp); + return desired == 1 ? allInfo.t : allInfo.f; + } + + public static class Info { + public int t; + public int f; + + public Info(int tr, int fa) { + t = tr; + f = fa; + } + } + + // 限制: + // L...R上,一定有奇数个字符 + // L位置的字符和R位置的字符,非0即1,不能是逻辑符号! + // 返回str[L...R]这一段,为true的方法数,和false的方法数 + public static Info func(char[] str, int L, int R, Info[][] dp) { + if (dp[L][R] != null) { + return dp[L][R]; + } + int t = 0; + int f = 0; + if (L == R) { + t = str[L] == '1' ? 1 : 0; + f = str[L] == '0' ? 1 : 0; + } else { // L..R >=3 + // 每一个种逻辑符号,split枚举的东西 + // 都去试试最后结合 + for (int split = L + 1; split < R; split += 2) { + Info leftInfo = func(str, L, split - 1, dp); + Info rightInfo = func(str, split + 1, R, dp); + int a = leftInfo.t; + int b = leftInfo.f; + int c = rightInfo.t; + int d = rightInfo.f; + switch (str[split]) { + case '&': + t += a * c; + f += b * c + b * d + a * d; + break; + case '|': + t += a * c + a * d + b * c; + f += b * d; + break; + case '^': + t += a * d + b * c; + f += a * c + b * d; + break; + } + } + + } + dp[L][R] = new Info(t, f); + return dp[L][R]; + } + + public static int countEval1(String express, int desired) { + if (express == null || express.equals("")) { + return 0; + } + char[] exp = express.toCharArray(); + return f(exp, desired, 0, exp.length - 1); + } + + public static int f(char[] str, int desired, int L, int R) { + if (L == R) { + if (str[L] == '1') { + return desired; + } else { + return desired ^ 1; + } + } + int res = 0; + if (desired == 1) { + for (int i = L + 1; i < R; i += 2) { + switch (str[i]) { + case '&': + res += f(str, 1, L, i - 1) * f(str, 1, i + 1, R); + break; + case '|': + res += f(str, 1, L, i - 1) * f(str, 0, i + 1, R); + res += f(str, 0, L, i - 1) * f(str, 1, i + 1, R); + res += f(str, 1, L, i - 1) * f(str, 1, i + 1, R); + break; + case '^': + res += f(str, 1, L, i - 1) * f(str, 0, i + 1, R); + res += f(str, 0, L, i - 1) * f(str, 1, i + 1, R); + break; + } + } + } else { + for (int i = L + 1; i < R; i += 2) { + switch (str[i]) { + case '&': + res += f(str, 0, L, i - 1) * f(str, 1, i + 1, R); + res += f(str, 1, L, i - 1) * f(str, 0, i + 1, R); + res += f(str, 0, L, i - 1) * f(str, 0, i + 1, R); + break; + case '|': + res += f(str, 0, L, i - 1) * f(str, 0, i + 1, R); + break; + case '^': + res += f(str, 1, L, i - 1) * f(str, 1, i + 1, R); + res += f(str, 0, L, i - 1) * f(str, 0, i + 1, R); + break; + } + } + } + return res; + } + + public static int countEval2(String express, int desired) { + if (express == null || express.equals("")) { + return 0; + } + char[] exp = express.toCharArray(); + int N = exp.length; + int[][][] dp = new int[2][N][N]; + dp[0][0][0] = exp[0] == '0' ? 1 : 0; + dp[1][0][0] = dp[0][0][0] ^ 1; + for (int i = 2; i < exp.length; i += 2) { + dp[0][i][i] = exp[i] == '1' ? 0 : 1; + dp[1][i][i] = exp[i] == '0' ? 0 : 1; + for (int j = i - 2; j >= 0; j -= 2) { + for (int k = j; k < i; k += 2) { + if (exp[k + 1] == '&') { + dp[1][j][i] += dp[1][j][k] * dp[1][k + 2][i]; + dp[0][j][i] += (dp[0][j][k] + dp[1][j][k]) * dp[0][k + 2][i] + dp[0][j][k] * dp[1][k + 2][i]; + } else if (exp[k + 1] == '|') { + dp[1][j][i] += (dp[0][j][k] + dp[1][j][k]) * dp[1][k + 2][i] + dp[1][j][k] * dp[0][k + 2][i]; + dp[0][j][i] += dp[0][j][k] * dp[0][k + 2][i]; + } else { + dp[1][j][i] += dp[0][j][k] * dp[1][k + 2][i] + dp[1][j][k] * dp[0][k + 2][i]; + dp[0][j][i] += dp[0][j][k] * dp[0][k + 2][i] + dp[1][j][k] * dp[1][k + 2][i]; + } + } + } + } + return dp[desired][0][N - 1]; + } + +} diff --git a/大厂刷题班/class11/Code01_MinimumInsertionStepsToMakeAStringPalindrome.java b/大厂刷题班/class11/Code01_MinimumInsertionStepsToMakeAStringPalindrome.java new file mode 100644 index 0000000..94e3bd8 --- /dev/null +++ b/大厂刷题班/class11/Code01_MinimumInsertionStepsToMakeAStringPalindrome.java @@ -0,0 +1,173 @@ +package class11; + +import java.util.ArrayList; +import java.util.List; + +// 本题测试链接 : https://leetcode.com/problems/minimum-insertion-steps-to-make-a-string-palindrome/ +public class Code01_MinimumInsertionStepsToMakeAStringPalindrome { + + // 测试链接只测了本题的第一问,直接提交可以通过 + public static int minInsertions(String s) { + if (s == null || s.length() < 2) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + for (int i = 0; i < N - 1; i++) { + dp[i][i + 1] = str[i] == str[i + 1] ? 0 : 1; + } + for (int i = N - 3; i >= 0; i--) { + for (int j = i + 2; j < N; j++) { + dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1; + if (str[i] == str[j]) { + dp[i][j] = Math.min(dp[i][j], dp[i + 1][j - 1]); + } + } + } + return dp[0][N - 1]; + } + + // 本题第二问,返回其中一种结果 + public static String minInsertionsOneWay(String s) { + if (s == null || s.length() < 2) { + return s; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + for (int i = 0; i < N - 1; i++) { + dp[i][i + 1] = str[i] == str[i + 1] ? 0 : 1; + } + for (int i = N - 3; i >= 0; i--) { + for (int j = i + 2; j < N; j++) { + dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1; + if (str[i] == str[j]) { + dp[i][j] = Math.min(dp[i][j], dp[i + 1][j - 1]); + } + } + } + + int L = 0; + int R = N - 1; + char[] ans = new char[N + dp[L][R]]; + int ansl = 0; + int ansr = ans.length - 1; + while (L < R) { + if (dp[L][R - 1] == dp[L][R] - 1) { + ans[ansl++] = str[R]; + ans[ansr--] = str[R--]; + } else if (dp[L + 1][R] == dp[L][R] - 1) { + ans[ansl++] = str[L]; + ans[ansr--] = str[L++]; + } else { + ans[ansl++] = str[L++]; + ans[ansr--] = str[R--]; + } + } + if (L == R) { + ans[ansl] = str[L]; + } + return String.valueOf(ans); + } + + // 本题第三问,返回所有可能的结果 + public static List minInsertionsAllWays(String s) { + List ans = new ArrayList<>(); + if (s == null || s.length() < 2) { + ans.add(s); + } else { + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + for (int i = 0; i < N - 1; i++) { + dp[i][i + 1] = str[i] == str[i + 1] ? 0 : 1; + } + for (int i = N - 3; i >= 0; i--) { + for (int j = i + 2; j < N; j++) { + dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1; + if (str[i] == str[j]) { + dp[i][j] = Math.min(dp[i][j], dp[i + 1][j - 1]); + } + } + } + int M = N + dp[0][N - 1]; + char[] path = new char[M]; + process(str, dp, 0, N - 1, path, 0, M - 1, ans); + } + return ans; + } + + // 当前来到的动态规划中的格子,(L,R) + // path .... [pl....pr] .... + public static void process(char[] str, int[][] dp, int L, int R, char[] path, int pl, int pr, List ans) { + if (L >= R) { // L > R L==R + if (L == R) { + path[pl] = str[L]; + } + ans.add(String.valueOf(path)); + } else { + if (dp[L][R - 1] == dp[L][R] - 1) { + path[pl] = str[R]; + path[pr] = str[R]; + process(str, dp, L, R - 1, path, pl + 1, pr - 1, ans); + } + if (dp[L + 1][R] == dp[L][R] - 1) { + path[pl] = str[L]; + path[pr] = str[L]; + process(str, dp, L + 1, R, path, pl + 1, pr - 1, ans); + } + if (str[L] == str[R] && (L == R - 1 || dp[L + 1][R - 1] == dp[L][R])) { + path[pl] = str[L]; + path[pr] = str[R]; + process(str, dp, L + 1, R - 1, path, pl + 1, pr - 1, ans); + } + } + } + + public static void main(String[] args) { + String s = null; + String ans2 = null; + List ans3 = null; + + System.out.println("本题第二问,返回其中一种结果测试开始"); + s = "mbadm"; + ans2 = minInsertionsOneWay(s); + System.out.println(ans2); + + s = "leetcode"; + ans2 = minInsertionsOneWay(s); + System.out.println(ans2); + + s = "aabaa"; + ans2 = minInsertionsOneWay(s); + System.out.println(ans2); + System.out.println("本题第二问,返回其中一种结果测试结束"); + + System.out.println(); + + System.out.println("本题第三问,返回所有可能的结果测试开始"); + s = "mbadm"; + ans3 = minInsertionsAllWays(s); + for (String way : ans3) { + System.out.println(way); + } + System.out.println(); + + s = "leetcode"; + ans3 = minInsertionsAllWays(s); + for (String way : ans3) { + System.out.println(way); + } + System.out.println(); + + s = "aabaa"; + ans3 = minInsertionsAllWays(s); + for (String way : ans3) { + System.out.println(way); + } + System.out.println(); + System.out.println("本题第三问,返回所有可能的结果测试结束"); + } + +} diff --git a/大厂刷题班/class11/Code02_PalindromePartitioningII.java b/大厂刷题班/class11/Code02_PalindromePartitioningII.java new file mode 100644 index 0000000..08633db --- /dev/null +++ b/大厂刷题班/class11/Code02_PalindromePartitioningII.java @@ -0,0 +1,207 @@ +package class11; + +import java.util.ArrayList; +import java.util.List; + +// 本题测试链接 : https://leetcode.com/problems/palindrome-partitioning-ii/ +public class Code02_PalindromePartitioningII { + + // 测试链接只测了本题的第一问,直接提交可以通过 + public static int minCut(String s) { + if (s == null || s.length() < 2) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + boolean[][] checkMap = createCheckMap(str, N); + int[] dp = new int[N + 1]; + dp[N] = 0; + for (int i = N - 1; i >= 0; i--) { + if (checkMap[i][N - 1]) { + dp[i] = 1; + } else { + int next = Integer.MAX_VALUE; + for (int j = i; j < N; j++) { + if (checkMap[i][j]) { + next = Math.min(next, dp[j + 1]); + } + } + dp[i] = 1 + next; + } + } + return dp[0] - 1; + } + + public static boolean[][] createCheckMap(char[] str, int N) { + boolean[][] ans = new boolean[N][N]; + for (int i = 0; i < N - 1; i++) { + ans[i][i] = true; + ans[i][i + 1] = str[i] == str[i + 1]; + } + ans[N - 1][N - 1] = true; + for (int i = N - 3; i >= 0; i--) { + for (int j = i + 2; j < N; j++) { + ans[i][j] = str[i] == str[j] && ans[i + 1][j - 1]; + } + } + return ans; + } + + // 本题第二问,返回其中一种结果 + public static List minCutOneWay(String s) { + List ans = new ArrayList<>(); + if (s == null || s.length() < 2) { + ans.add(s); + } else { + char[] str = s.toCharArray(); + int N = str.length; + boolean[][] checkMap = createCheckMap(str, N); + int[] dp = new int[N + 1]; + dp[N] = 0; + for (int i = N - 1; i >= 0; i--) { + if (checkMap[i][N - 1]) { + dp[i] = 1; + } else { + int next = Integer.MAX_VALUE; + for (int j = i; j < N; j++) { + if (checkMap[i][j]) { + next = Math.min(next, dp[j + 1]); + } + } + dp[i] = 1 + next; + } + } + // dp[i] (0....5) 回文! dp[0] == dp[6] + 1 + // (0....5) 6 + for (int i = 0, j = 1; j <= N; j++) { + if (checkMap[i][j - 1] && dp[i] == dp[j] + 1) { + ans.add(s.substring(i, j)); + i = j; + } + } + } + return ans; + } + + // 本题第三问,返回所有结果 + public static List> minCutAllWays(String s) { + List> ans = new ArrayList<>(); + if (s == null || s.length() < 2) { + List cur = new ArrayList<>(); + cur.add(s); + ans.add(cur); + } else { + char[] str = s.toCharArray(); + int N = str.length; + boolean[][] checkMap = createCheckMap(str, N); + int[] dp = new int[N + 1]; + dp[N] = 0; + for (int i = N - 1; i >= 0; i--) { + if (checkMap[i][N - 1]) { + dp[i] = 1; + } else { + int next = Integer.MAX_VALUE; + for (int j = i; j < N; j++) { + if (checkMap[i][j]) { + next = Math.min(next, dp[j + 1]); + } + } + dp[i] = 1 + next; + } + } + process(s, 0, 1, checkMap, dp, new ArrayList<>(), ans); + } + return ans; + } + + // s[0....i-1] 存到path里去了 + // s[i..j-1]考察的分出来的第一份 + public static void process(String s, int i, int j, boolean[][] checkMap, int[] dp, + List path, + List> ans) { + if (j == s.length()) { // s[i...N-1] + if (checkMap[i][j - 1] && dp[i] == dp[j] + 1) { + path.add(s.substring(i, j)); + ans.add(copyStringList(path)); + path.remove(path.size() - 1); + } + } else {// s[i...j-1] + if (checkMap[i][j - 1] && dp[i] == dp[j] + 1) { + path.add(s.substring(i, j)); + process(s, j, j + 1, checkMap, dp, path, ans); + path.remove(path.size() - 1); + } + process(s, i, j + 1, checkMap, dp, path, ans); + } + } + + public static List copyStringList(List list) { + List ans = new ArrayList<>(); + for (String str : list) { + ans.add(str); + } + return ans; + } + + public static void main(String[] args) { + String s = null; + List ans2 = null; + List> ans3 = null; + + System.out.println("本题第二问,返回其中一种结果测试开始"); + s = "abacbc"; + ans2 = minCutOneWay(s); + for (String str : ans2) { + System.out.print(str + " "); + } + System.out.println(); + + s = "aabccbac"; + ans2 = minCutOneWay(s); + for (String str : ans2) { + System.out.print(str + " "); + } + System.out.println(); + + s = "aabaa"; + ans2 = minCutOneWay(s); + for (String str : ans2) { + System.out.print(str + " "); + } + System.out.println(); + System.out.println("本题第二问,返回其中一种结果测试结束"); + System.out.println(); + System.out.println("本题第三问,返回所有可能结果测试开始"); + s = "cbbbcbc"; + ans3 = minCutAllWays(s); + for (List way : ans3) { + for (String str : way) { + System.out.print(str + " "); + } + System.out.println(); + } + System.out.println(); + + s = "aaaaaa"; + ans3 = minCutAllWays(s); + for (List way : ans3) { + for (String str : way) { + System.out.print(str + " "); + } + System.out.println(); + } + System.out.println(); + + s = "fcfffcffcc"; + ans3 = minCutAllWays(s); + for (List way : ans3) { + for (String str : way) { + System.out.print(str + " "); + } + System.out.println(); + } + System.out.println(); + System.out.println("本题第三问,返回所有可能结果测试结束"); + } + +} diff --git a/大厂刷题班/class12/Code01_ContainAllCharExactly.java b/大厂刷题班/class12/Code01_ContainAllCharExactly.java new file mode 100644 index 0000000..1774deb --- /dev/null +++ b/大厂刷题班/class12/Code01_ContainAllCharExactly.java @@ -0,0 +1,125 @@ +package class12; + +import java.util.Arrays; + +// 本题测试链接 : https://leetcode.com/problems/permutation-in-string/ +public class Code01_ContainAllCharExactly { + + public static int containExactly1(String s, String a) { + if (s == null || a == null || s.length() < a.length()) { + return -1; + } + char[] aim = a.toCharArray(); + Arrays.sort(aim); + String aimSort = String.valueOf(aim); + for (int L = 0; L < s.length(); L++) { + for (int R = L; R < s.length(); R++) { + char[] cur = s.substring(L, R + 1).toCharArray(); + Arrays.sort(cur); + String curSort = String.valueOf(cur); + if (curSort.equals(aimSort)) { + return L; + } + } + } + return -1; + } + + public static int containExactly2(String s, String a) { + if (s == null || a == null || s.length() < a.length()) { + return -1; + } + char[] str = s.toCharArray(); + char[] aim = a.toCharArray(); + for (int L = 0; L <= str.length - aim.length; L++) { + if (isCountEqual(str, L, aim)) { + return L; + } + } + return -1; + } + + public static boolean isCountEqual(char[] str, int L, char[] aim) { + int[] count = new int[256]; + for (int i = 0; i < aim.length; i++) { + count[aim[i]]++; + } + for (int i = 0; i < aim.length; i++) { + if (count[str[L + i]]-- == 0) { + return false; + } + } + return true; + } + + public static int containExactly3(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() < s2.length()) { + return -1; + } + char[] str2 = s2.toCharArray(); + int M = str2.length; + int[] count = new int[256]; + for (int i = 0; i < M; i++) { + count[str2[i]]++; + } + int all = M; + char[] str1 = s1.toCharArray(); + int R = 0; + // 0~M-1 + for (; R < M; R++) { // 最早的M个字符,让其窗口初步形成 + if (count[str1[R]]-- > 0) { + all--; + } + } + // 窗口初步形成了,并没有判断有效无效,决定下一个位置一上来判断 + // 接下来的过程,窗口右进一个,左吐一个 + for (; R < str1.length; R++) { + if (all == 0) { // R-1 + return R - M; + } + if (count[str1[R]]-- > 0) { + all--; + } + if (count[str1[R - M]]++ >= 0) { + all++; + } + } + return all == 0 ? R - M : -1; + } + + // for test + public static String getRandomString(int possibilities, int maxSize) { + char[] ans = new char[(int) (Math.random() * maxSize) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (char) ((int) (Math.random() * possibilities) + 'a'); + } + return String.valueOf(ans); + } + + public static void main(String[] args) { + int possibilities = 5; + int strMaxSize = 20; + int aimMaxSize = 10; + int testTimes = 500000; + System.out.println("test begin, test time : " + testTimes); + for (int i = 0; i < testTimes; i++) { + String str = getRandomString(possibilities, strMaxSize); + String aim = getRandomString(possibilities, aimMaxSize); + int ans1 = containExactly1(str, aim); + int ans2 = containExactly2(str, aim); + int ans3 = containExactly3(str, aim); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("Oops!"); + System.out.println(str); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + break; + } + } + System.out.println("test finish"); + + } + +} diff --git a/大厂刷题班/class12/Code03_FindKthMinNumber.java b/大厂刷题班/class12/Code03_FindKthMinNumber.java new file mode 100644 index 0000000..c36cbda --- /dev/null +++ b/大厂刷题班/class12/Code03_FindKthMinNumber.java @@ -0,0 +1,103 @@ +package class12; + +// 本题测试链接 : https://leetcode.com/problems/median-of-two-sorted-arrays/ +public class Code03_FindKthMinNumber { + + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + int size = nums1.length + nums2.length; + boolean even = (size & 1) == 0; + if (nums1.length != 0 && nums2.length != 0) { + if (even) { + return (double) (findKthNum(nums1, nums2, size / 2) + findKthNum(nums1, nums2, size / 2 + 1)) / 2D; + } else { + return findKthNum(nums1, nums2, size / 2 + 1); + } + } else if (nums1.length != 0) { + if (even) { + return (double) (nums1[(size - 1) / 2] + nums1[size / 2]) / 2; + } else { + return nums1[size / 2]; + } + } else if (nums2.length != 0) { + if (even) { + return (double) (nums2[(size - 1) / 2] + nums2[size / 2]) / 2; + } else { + return nums2[size / 2]; + } + } else { + return 0; + } + } + + // 进阶问题 : 在两个都有序的数组中,找整体第K小的数 + // 可以做到O(log(Min(M,N))) + public static int findKthNum(int[] arr1, int[] arr2, int kth) { + int[] longs = arr1.length >= arr2.length ? arr1 : arr2; + int[] shorts = arr1.length < arr2.length ? arr1 : arr2; + int l = longs.length; + int s = shorts.length; + if (kth <= s) { // 1) + return getUpMedian(shorts, 0, kth - 1, longs, 0, kth - 1); + } + if (kth > l) { // 3) + if (shorts[kth - l - 1] >= longs[l - 1]) { + return shorts[kth - l - 1]; + } + if (longs[kth - s - 1] >= shorts[s - 1]) { + return longs[kth - s - 1]; + } + return getUpMedian(shorts, kth - l, s - 1, longs, kth - s, l - 1); + } + // 2) s < k <= l + if (longs[kth - s - 1] >= shorts[s - 1]) { + return longs[kth - s - 1]; + } + return getUpMedian(shorts, 0, s - 1, longs, kth - s, kth - 1); + } + + + + + // A[s1...e1] + // B[s2...e2] + // 一定等长! + // 返回整体的,上中位数!8(4) 10(5) 12(6) + public static int getUpMedian(int[] A, int s1, int e1, int[] B, int s2, int e2) { + int mid1 = 0; + int mid2 = 0; + while (s1 < e1) { + // mid1 = s1 + (e1 - s1) >> 1 + mid1 = (s1 + e1) / 2; + mid2 = (s2 + e2) / 2; + if (A[mid1] == B[mid2]) { + return A[mid1]; + } + // 两个中点一定不等! + if (((e1 - s1 + 1) & 1) == 1) { // 奇数长度 + if (A[mid1] > B[mid2]) { + if (B[mid2] >= A[mid1 - 1]) { + return B[mid2]; + } + e1 = mid1 - 1; + s2 = mid2 + 1; + } else { // A[mid1] < B[mid2] + if (A[mid1] >= B[mid2 - 1]) { + return A[mid1]; + } + e2 = mid2 - 1; + s1 = mid1 + 1; + } + } else { // 偶数长度 + if (A[mid1] > B[mid2]) { + e1 = mid1; + s2 = mid2 + 1; + } else { + e2 = mid2; + s1 = mid1 + 1; + } + } + } + return Math.min(A[s1], B[s2]); + } + +} diff --git a/大厂刷题班/class12/Code03_LongestConsecutive.java b/大厂刷题班/class12/Code03_LongestConsecutive.java new file mode 100644 index 0000000..8beb48d --- /dev/null +++ b/大厂刷题班/class12/Code03_LongestConsecutive.java @@ -0,0 +1,65 @@ +package class12; + +import java.util.HashMap; +import java.util.HashSet; + +// 本题测试链接 : https://leetcode.com/problems/longest-consecutive-sequence/ +public class Code03_LongestConsecutive { + + // 课上讲的解法 + public static int longestConsecutive(int[] nums) { + HashMap map = new HashMap<>(); + int len = 0; + for (int num : nums) { + if (!map.containsKey(num)) { + map.put(num, 1); + int preLen = map.containsKey(num - 1) ? map.get(num - 1) : 0; + int posLen = map.containsKey(num + 1) ? map.get(num + 1) : 0; + int all = preLen + posLen + 1; + map.put(num - preLen, all); + map.put(num + posLen, all); + len = Math.max(len, all); + } + } + return len; + } + + // 补充一个两张表:头表、尾表。非常好理解的方法 + // 不是最优解,但是好理解 + public static int longestConsecutive2(int[] nums) { + HashMap headMap = new HashMap(); + HashMap tailMap = new HashMap(); + HashSet visited = new HashSet<>(); + for (int num : nums) { + if (!visited.contains(num)) { + visited.add(num); + headMap.put(num, 1); + tailMap.put(num, 1); + if (tailMap.containsKey(num - 1)) { + int preLen = tailMap.get(num - 1); + int preHead = num - preLen; + headMap.put(preHead, preLen + 1); + tailMap.put(num, preLen + 1); + headMap.remove(num); + tailMap.remove(num - 1); + } + if (headMap.containsKey(num + 1)) { + int preLen = tailMap.get(num); + int preHead = num - preLen + 1; + int postLen = headMap.get(num + 1); + int postTail = num + postLen; + headMap.put(preHead, preLen + postLen); + tailMap.put(postTail, preLen + postLen); + headMap.remove(num + 1); + tailMap.remove(num); + } + } + } + int ans = 0; + for (int len : headMap.values()) { + ans = Math.max(ans, len); + } + return ans; + } + +} diff --git a/大厂刷题班/class12/Code04_RegularExpressionMatch.java b/大厂刷题班/class12/Code04_RegularExpressionMatch.java new file mode 100644 index 0000000..c4b35b7 --- /dev/null +++ b/大厂刷题班/class12/Code04_RegularExpressionMatch.java @@ -0,0 +1,135 @@ +package class12; + +// 测试链接 : https://leetcode.com/problems/regular-expression-matching/ +public class Code04_RegularExpressionMatch { + + public static boolean isValid(char[] s, char[] e) { + // s中不能有'.' or '*' + for (int i = 0; i < s.length; i++) { + if (s[i] == '*' || s[i] == '.') { + return false; + } + } + // 开头的e[0]不能是'*',没有相邻的'*' + for (int i = 0; i < e.length; i++) { + if (e[i] == '*' && (i == 0 || e[i - 1] == '*')) { + return false; + } + } + return true; + } + + // 初始尝试版本,不包含斜率优化 + public static boolean isMatch1(String str, String exp) { + if (str == null || exp == null) { + return false; + } + char[] s = str.toCharArray(); + char[] e = exp.toCharArray(); + return isValid(s, e) && process(s, e, 0, 0); + } + + // str[si.....] 能不能被 exp[ei.....]配出来! true false + public static boolean process(char[] s, char[] e, int si, int ei) { + if (ei == e.length) { // exp 没了 str? + return si == s.length; + } + // exp[ei]还有字符 + // ei + 1位置的字符,不是* + if (ei + 1 == e.length || e[ei + 1] != '*') { + // ei + 1 不是* + // str[si] 必须和 exp[ei] 能配上! + return si != s.length && (e[ei] == s[si] || e[ei] == '.') && process(s, e, si + 1, ei + 1); + } + // exp[ei]还有字符 + // ei + 1位置的字符,是*! + while (si != s.length && (e[ei] == s[si] || e[ei] == '.')) { + if (process(s, e, si, ei + 2)) { + return true; + } + si++; + } + return process(s, e, si, ei + 2); + } + + // 改记忆化搜索+斜率优化 + public static boolean isMatch2(String str, String exp) { + if (str == null || exp == null) { + return false; + } + char[] s = str.toCharArray(); + char[] e = exp.toCharArray(); + if (!isValid(s, e)) { + return false; + } + int[][] dp = new int[s.length + 1][e.length + 1]; + // dp[i][j] = 0, 没算过! + // dp[i][j] = -1 算过,返回值是false + // dp[i][j] = 1 算过,返回值是true + return isValid(s, e) && process2(s, e, 0, 0, dp); + } + + public static boolean process2(char[] s, char[] e, int si, int ei, int[][] dp) { + if (dp[si][ei] != 0) { + return dp[si][ei] == 1; + } + boolean ans = false; + if (ei == e.length) { + ans = si == s.length; + } else { + if (ei + 1 == e.length || e[ei + 1] != '*') { + ans = si != s.length && (e[ei] == s[si] || e[ei] == '.') && process2(s, e, si + 1, ei + 1, dp); + } else { + if (si == s.length) { // ei ei+1 * + ans = process2(s, e, si, ei + 2, dp); + } else { // si没结束 + if (s[si] != e[ei] && e[ei] != '.') { + ans = process2(s, e, si, ei + 2, dp); + } else { // s[si] 可以和 e[ei]配上 + ans = process2(s, e, si, ei + 2, dp) || process2(s, e, si + 1, ei, dp); + } + } + } + } + dp[si][ei] = ans ? 1 : -1; + return ans; + } + + // 动态规划版本 + 斜率优化 + public static boolean isMatch3(String str, String pattern) { + if (str == null || pattern == null) { + return false; + } + char[] s = str.toCharArray(); + char[] p = pattern.toCharArray(); + if (!isValid(s, p)) { + return false; + } + int N = s.length; + int M = p.length; + boolean[][] dp = new boolean[N + 1][M + 1]; + dp[N][M] = true; + for (int j = M - 1; j >= 0; j--) { + dp[N][j] = (j + 1 < M && p[j + 1] == '*') && dp[N][j + 2]; + } + // dp[0..N-2][M-1]都等于false,只有dp[N-1][M-1]需要讨论 + if (N > 0 && M > 0) { + dp[N - 1][M - 1] = (s[N - 1] == p[M - 1] || p[M - 1] == '.'); + } + for (int i = N - 1; i >= 0; i--) { + for (int j = M - 2; j >= 0; j--) { + if (p[j + 1] != '*') { + dp[i][j] = ((s[i] == p[j]) || (p[j] == '.')) && dp[i + 1][j + 1]; + } else { + if ((s[i] == p[j] || p[j] == '.') && dp[i + 1][j]) { + dp[i][j] = true; + } else { + dp[i][j] = dp[i][j + 2]; + } + } + } + } + return dp[0][0]; + } + +} diff --git a/大厂刷题班/class13/Code01_NCardsABWin.java b/大厂刷题班/class13/Code01_NCardsABWin.java new file mode 100644 index 0000000..4a2b832 --- /dev/null +++ b/大厂刷题班/class13/Code01_NCardsABWin.java @@ -0,0 +1,169 @@ +package class13; + +import java.text.DecimalFormat; + +public class Code01_NCardsABWin { + + // 谷歌面试题 + // 面值为1~10的牌组成一组, + // 每次你从组里等概率的抽出1~10中的一张 + // 下次抽会换一个新的组,有无限组 + // 当累加和<17时,你将一直抽牌 + // 当累加和>=17且<21时,你将获胜 + // 当累加和>=21时,你将失败 + // 返回获胜的概率 + public static double f1() { + return p1(0); + } + + // 游戏的规则,如上 + // 当你来到cur这个累加和的时候,获胜概率是多少返回! + public static double p1(int cur) { + if (cur >= 17 && cur < 21) { + return 1.0; + } + if (cur >= 21) { + return 0.0; + } + double w = 0.0; + for (int i = 1; i <= 10; i++) { + w += p1(cur + i); + } + return w / 10; + } + + // 谷歌面试题扩展版 + // 面值为1~N的牌组成一组, + // 每次你从组里等概率的抽出1~N中的一张 + // 下次抽会换一个新的组,有无限组 + // 当累加和=a且=b时,你将失败 + // 返回获胜的概率,给定的参数为N,a,b + public static double f2(int N, int a, int b) { + if (N < 1 || a >= b || a < 0 || b < 0) { + return 0.0; + } + if (b - a >= N) { + return 1.0; + } + // 所有参数都合法,并且b-a < N + return p2(0, N, a, b); + } + + // 游戏规则,如上,int N, int a, int b,固定参数! + // cur,目前到达了cur的累加和 + // 返回赢的概率 + public static double p2(int cur, int N, int a, int b) { + if (cur >= a && cur < b) { + return 1.0; + } + if (cur >= b) { + return 0.0; + } + double w = 0.0; + for (int i = 1; i <= N; i++) { + w += p2(cur + i, N, a, b); + } + return w / N; + } + + // f2的改进版本,用到了观察位置优化枚举的技巧 + // 可以课上讲一下 + public static double f3(int N, int a, int b) { + if (N < 1 || a >= b || a < 0 || b < 0) { + return 0.0; + } + if (b - a >= N) { + return 1.0; + } + return p3(0, N, a, b); + } + + public static double p3(int cur, int N, int a, int b) { + if (cur >= a && cur < b) { + return 1.0; + } + if (cur >= b) { + return 0.0; + } + if (cur == a - 1) { + return 1.0 * (b - a) / N; + } + double w = p3(cur + 1, N, a, b) + p3(cur + 1, N, a, b) * N; + if (cur + 1 + N < b) { + w -= p3(cur + 1 + N, N, a, b); + } + return w / N; + } + + // f3的改进版本的动态规划 + // 可以课上讲一下 + public static double f4(int N, int a, int b) { + if (N < 1 || a >= b || a < 0 || b < 0) { + return 0.0; + } + if (b - a >= N) { + return 1.0; + } + double[] dp = new double[b]; + for (int i = a; i < b; i++) { + dp[i] = 1.0; + } + if (a - 1 >= 0) { + dp[a - 1] = 1.0 * (b - a) / N; + } + for (int cur = a - 2; cur >= 0; cur--) { + double w = dp[cur + 1] + dp[cur + 1] * N; + if (cur + 1 + N < b) { + w -= dp[cur + 1 + N]; + } + dp[cur] = w / N; + } + return dp[0]; + } + + public static void main(String[] args) { + int N = 10; + int a = 17; + int b = 21; + System.out.println("N = " + N + ", a = " + a + ", b = " + b); + System.out.println(f1()); + System.out.println(f2(N, a, b)); + System.out.println(f3(N, a, b)); + System.out.println(f4(N, a, b)); + + int maxN = 15; + int maxM = 20; + int testTime = 100000; + System.out.println("测试开始"); + System.out.println("比对double类型答案可能会有精度对不准的问题"); + System.out.println("所以答案一律只保留小数点后四位进行比对"); + System.out.println("如果没有错误提示, 说明验证通过"); + DecimalFormat df = new DecimalFormat("#.####"); + for (int i = 0; i < testTime; i++) { + N = (int) (Math.random() * maxN); + a = (int) (Math.random() * maxM); + b = (int) (Math.random() * maxM); + double ans2 = Double.valueOf(df.format(f2(N, a, b))); + double ans3 = Double.valueOf(df.format(f3(N, a, b))); + double ans4 = Double.valueOf(df.format(f4(N, a, b))); + if (ans2 != ans3 || ans2 != ans4) { + System.out.println("Oops!"); + System.out.println(N + " , " + a + " , " + b); + System.out.println(ans2); + System.out.println(ans3); + System.out.println(ans4); + } + } + System.out.println("测试结束"); + + N = 10000; + a = 67834; + b = 72315; + System.out.println("N = " + N + ", a = " + a + ", b = " + b + "时, 除了方法4外都超时"); + System.out.print("方法4答案: "); + System.out.println(f4(N, a, b)); + } + +} diff --git a/大厂刷题班/class13/Code02_SuperWashingMachines.java b/大厂刷题班/class13/Code02_SuperWashingMachines.java new file mode 100644 index 0000000..bd23ae4 --- /dev/null +++ b/大厂刷题班/class13/Code02_SuperWashingMachines.java @@ -0,0 +1,34 @@ +package class13; + +// 本题测试链接 : https://leetcode.com/problems/super-washing-machines/ +public class Code02_SuperWashingMachines { + + public static int findMinMoves(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int size = arr.length; + int sum = 0; + for (int i = 0; i < size; i++) { + sum += arr[i]; + } + if (sum % size != 0) { + return -1; + } + int avg = sum / size; + int leftSum = 0; + int ans = 0; + for (int i = 0; i < arr.length; i++) { + int leftRest = leftSum - i * avg; + int rightRest = (sum - leftSum - arr[i]) - (size - i - 1) * avg; + if (leftRest < 0 && rightRest < 0) { + ans = Math.max(ans, Math.abs(leftRest) + Math.abs(rightRest)); + } else { + ans = Math.max(ans, Math.max(Math.abs(leftRest), Math.abs(rightRest))); + } + leftSum += arr[i]; + } + return ans; + } + +} diff --git a/大厂刷题班/class13/Code03_ScrambleString.java b/大厂刷题班/class13/Code03_ScrambleString.java new file mode 100644 index 0000000..49244c4 --- /dev/null +++ b/大厂刷题班/class13/Code03_ScrambleString.java @@ -0,0 +1,186 @@ +package class13; + +// 本题测试链接 : https://leetcode.com/problems/scramble-string/ +public class Code03_ScrambleString { + + public static boolean isScramble0(String s1, String s2) { + if ((s1 == null && s2 != null) || (s1 != null && s2 == null)) { + return false; + } + if (s1 == null && s2 == null) { + return true; + } + if (s1.equals(s2)) { + return true; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + if (!sameTypeSameNumber(str1, str2)) { + return false; + } + return process0(str1, 0, str1.length - 1, str2, 0, str2.length - 1); + } + + // str1[L1...R1] str2[L2...R2] 是否互为玄变串 + // 一定保证这两段是等长的! + public static boolean process0(char[] str1, int L1, int R1, char[] str2, int L2, int R2) { + if (L1 == R1) { + return str1[L1] == str2[L2]; + } + for (int leftEnd = L1; leftEnd < R1; leftEnd++) { + boolean p1 = process0(str1, L1, leftEnd, str2, L2, L2 + leftEnd - L1) + && process0(str1, leftEnd + 1, R1, str2, L2 + leftEnd - L1 + 1, R2); + boolean p2 = process0(str1, L1, leftEnd, str2, R2 - (leftEnd - L1), R2) + && process0(str1, leftEnd + 1, R1, str2, L2, R2 - (leftEnd - L1) - 1); + if (p1 || p2) { + return true; + } + } + return false; + } + + public static boolean sameTypeSameNumber(char[] str1, char[] str2) { + if (str1.length != str2.length) { + return false; + } + int[] map = new int[256]; + for (int i = 0; i < str1.length; i++) { + map[str1[i]]++; + } + for (int i = 0; i < str2.length; i++) { + if (--map[str2[i]] < 0) { + return false; + } + } + return true; + } + + public static boolean isScramble1(String s1, String s2) { + if ((s1 == null && s2 != null) || (s1 != null && s2 == null)) { + return false; + } + if (s1 == null && s2 == null) { + return true; + } + if (s1.equals(s2)) { + return true; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + if (!sameTypeSameNumber(str1, str2)) { + return false; + } + int N = s1.length(); + return process1(str1, str2, 0, 0, N); + } + + // 返回str1[从L1开始往右长度为size的子串]和str2[从L2开始往右长度为size的子串]是否互为旋变字符串 + // 在str1中的这一段和str2中的这一段一定是等长的,所以只用一个参数size + public static boolean process1(char[] str1, char[] str2, int L1, int L2, int size) { + if (size == 1) { + return str1[L1] == str2[L2]; + } + // 枚举每一种情况,有一个计算出互为旋变就返回true。都算不出来最后返回false + for (int leftPart = 1; leftPart < size; leftPart++) { + if ( + // 如果1左对2左,并且1右对2右 + (process1(str1, str2, L1, L2, leftPart) + && process1(str1, str2, L1 + leftPart, L2 + leftPart, size - leftPart)) || + // 如果1左对2右,并且1右对2左 + (process1(str1, str2, L1, L2 + size - leftPart, leftPart) + && process1(str1, str2, L1 + leftPart, L2, size - leftPart))) { + return true; + } + } + return false; + } + + public static boolean isScramble2(String s1, String s2) { + if ((s1 == null && s2 != null) || (s1 != null && s2 == null)) { + return false; + } + if (s1 == null && s2 == null) { + return true; + } + if (s1.equals(s2)) { + return true; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + if (!sameTypeSameNumber(str1, str2)) { + return false; + } + int N = s1.length(); + int[][][] dp = new int[N][N][N + 1]; + // dp[i][j][k] = 0 processDP(i,j,k)状态之前没有算过的 + // dp[i][j][k] = -1 processDP(i,j,k)状态之前算过的,返回值是false + // dp[i][j][k] = 1 processDP(i,j,k)状态之前算过的,返回值是true + return process2(str1, str2, 0, 0, N, dp); + } + + public static boolean process2(char[] str1, char[] str2, int L1, int L2, int size, int[][][] dp) { + if (dp[L1][L2][size] != 0) { + return dp[L1][L2][size] == 1; + } + boolean ans = false; + if (size == 1) { + ans = str1[L1] == str2[L2]; + } else { + for (int leftPart = 1; leftPart < size; leftPart++) { + if ((process2(str1, str2, L1, L2, leftPart, dp) + && process2(str1, str2, L1 + leftPart, L2 + leftPart, size - leftPart, dp)) + || (process2(str1, str2, L1, L2 + size - leftPart, leftPart, dp) + && process2(str1, str2, L1 + leftPart, L2, size - leftPart, dp))) { + ans = true; + break; + } + } + } + dp[L1][L2][size] = ans ? 1 : -1; + return ans; + } + + public static boolean isScramble3(String s1, String s2) { + if ((s1 == null && s2 != null) || (s1 != null && s2 == null)) { + return false; + } + if (s1 == null && s2 == null) { + return true; + } + if (s1.equals(s2)) { + return true; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + if (!sameTypeSameNumber(str1, str2)) { + return false; + } + int N = s1.length(); + boolean[][][] dp = new boolean[N][N][N + 1]; + for (int L1 = 0; L1 < N; L1++) { + for (int L2 = 0; L2 < N; L2++) { + dp[L1][L2][1] = str1[L1] == str2[L2]; + } + } + // 第一层for循环含义是:依次填size=2层、size=3层..size=N层,每一层都是一个二维平面 + // 第二、三层for循环含义是:在具体的一层,整个面都要填写,所以用两个for循环去填一个二维面 + // L1的取值氛围是[0,N-size],因为从L1出发往右长度为size的子串,L1是不能从N-size+1出发的,这样往右就不够size个字符了 + // L2的取值范围同理 + // 第4层for循环完全是递归函数怎么写,这里就怎么改的 + for (int size = 2; size <= N; size++) { + for (int L1 = 0; L1 <= N - size; L1++) { + for (int L2 = 0; L2 <= N - size; L2++) { + for (int leftPart = 1; leftPart < size; leftPart++) { + if ((dp[L1][L2][leftPart] && dp[L1 + leftPart][L2 + leftPart][size - leftPart]) + || (dp[L1][L2 + size - leftPart][leftPart] && dp[L1 + leftPart][L2][size - leftPart])) { + dp[L1][L2][size] = true; + break; + } + } + } + } + } + return dp[0][0][N]; + } + +} diff --git a/大厂刷题班/class13/Code04_BricksFallingWhenHit.java b/大厂刷题班/class13/Code04_BricksFallingWhenHit.java new file mode 100644 index 0000000..657095c --- /dev/null +++ b/大厂刷题班/class13/Code04_BricksFallingWhenHit.java @@ -0,0 +1,149 @@ +package class13; + +// 本题测试链接 : https://leetcode.com/problems/bricks-falling-when-hit/ +public class Code04_BricksFallingWhenHit { + + public static int[] hitBricks(int[][] grid, int[][] hits) { + for (int i = 0; i < hits.length; i++) { + if (grid[hits[i][0]][hits[i][1]] == 1) { + grid[hits[i][0]][hits[i][1]] = 2; + } + } + UnionFind unionFind = new UnionFind(grid); + int[] ans = new int[hits.length]; + for (int i = hits.length - 1; i >= 0; i--) { + if (grid[hits[i][0]][hits[i][1]] == 2) { + ans[i] = unionFind.finger(hits[i][0], hits[i][1]); + } + } + return ans; + } + + // 并查集 + public static class UnionFind { + private int N; + private int M; + // 有多少块砖,连到了天花板上 + private int cellingAll; + // 原始矩阵,因为炮弹的影响,1 -> 2 + private int[][] grid; + // cellingSet[i] = true; i 是头节点,所在的集合是天花板集合 + private boolean[] cellingSet; + private int[] fatherMap; + private int[] sizeMap; + private int[] stack; + + public UnionFind(int[][] matrix) { + initSpace(matrix); + initConnect(); + } + + private void initSpace(int[][] matrix) { + grid = matrix; + N = grid.length; + M = grid[0].length; + int all = N * M; + cellingAll = 0; + cellingSet = new boolean[all]; + fatherMap = new int[all]; + sizeMap = new int[all]; + stack = new int[all]; + for (int row = 0; row < N; row++) { + for (int col = 0; col < M; col++) { + if (grid[row][col] == 1) { + int index = row * M + col; + fatherMap[index] = index; + sizeMap[index] = 1; + if (row == 0) { + cellingSet[index] = true; + cellingAll++; + } + } + } + } + } + + private void initConnect() { + for (int row = 0; row < N; row++) { + for (int col = 0; col < M; col++) { + union(row, col, row - 1, col); + union(row, col, row + 1, col); + union(row, col, row, col - 1); + union(row, col, row, col + 1); + } + } + } + + private int find(int row, int col) { + int stackSize = 0; + int index = row * M + col; + while (index != fatherMap[index]) { + stack[stackSize++] = index; + index = fatherMap[index]; + } + while (stackSize != 0) { + fatherMap[stack[--stackSize]] = index; + } + return index; + } + + private void union(int r1, int c1, int r2, int c2) { + if (valid(r1, c1) && valid(r2, c2)) { + int father1 = find(r1, c1); + int father2 = find(r2, c2); + if (father1 != father2) { + int size1 = sizeMap[father1]; + int size2 = sizeMap[father2]; + boolean status1 = cellingSet[father1]; + boolean status2 = cellingSet[father2]; + if (size1 <= size2) { + fatherMap[father1] = father2; + sizeMap[father2] = size1 + size2; + if (status1 ^ status2) { + cellingSet[father2] = true; + cellingAll += status1 ? size2 : size1; + } + } else { + fatherMap[father2] = father1; + sizeMap[father1] = size1 + size2; + if (status1 ^ status2) { + cellingSet[father1] = true; + cellingAll += status1 ? size2 : size1; + } + } + } + } + } + + private boolean valid(int row, int col) { + return row >= 0 && row < N && col >= 0 && col < M && grid[row][col] == 1; + } + + public int cellingNum() { + return cellingAll; + } + + public int finger(int row, int col) { + grid[row][col] = 1; + int cur = row * M + col; + if (row == 0) { + cellingSet[cur] = true; + cellingAll++; + } + fatherMap[cur] = cur; + sizeMap[cur] = 1; + int pre = cellingAll; + union(row, col, row - 1, col); + union(row, col, row + 1, col); + union(row, col, row, col - 1); + union(row, col, row, col + 1); + int now = cellingAll; + if (row == 0) { + return now - pre; + } else { + return now == pre ? 0 : now - pre - 1; + } + } + } + +} diff --git a/大厂刷题班/class14/Code01_Parentheses.java b/大厂刷题班/class14/Code01_Parentheses.java new file mode 100644 index 0000000..acd810f --- /dev/null +++ b/大厂刷题班/class14/Code01_Parentheses.java @@ -0,0 +1,97 @@ +package class14; + +public class Code01_Parentheses { + + public static boolean valid(String s) { + char[] str = s.toCharArray(); + int count = 0; + for (int i = 0; i < str.length; i++) { + count += str[i] == '(' ? 1 : -1; + if (count < 0) { + return false; + } + } + return count == 0; + } + + public static int needParentheses(String s) { + char[] str = s.toCharArray(); + int count = 0; + int need = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == '(') { + count++; + } else { // 遇到的是')' + if (count == 0) { + need++; + } else { + count--; + } + } + } + return count + need; + } + + public static boolean isValid(char[] str) { + if (str == null || str.length == 0) { + return false; + } + int status = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] != ')' && str[i] != '(') { + return false; + } + if (str[i] == ')' && --status < 0) { + return false; + } + if (str[i] == '(') { + status++; + } + } + return status == 0; + } + + public static int deep(String s) { + char[] str = s.toCharArray(); + if (!isValid(str)) { + return 0; + } + int count = 0; + int max = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == '(') { + max = Math.max(max, ++count); + } else { + count--; + } + } + return max; + } + + // s只由(和)组成 + // 求最长有效括号子串长度 + // 本题测试链接 : https://leetcode.com/problems/longest-valid-parentheses/ + public static int longestValidParentheses(String s) { + if (s == null || s.length() < 2) { + return 0; + } + char[] str = s.toCharArray(); + // dp[i] : 子串必须以i位置结尾的情况下,往左最远能扩出多长的有效区域 + int[] dp = new int[str.length]; + // dp[0] = 0; ( ) + int pre = 0; + int ans = 0; + for (int i = 1; i < str.length; i++) { + if (str[i] == ')') { + // 当前谁和i位置的),去配! + pre = i - dp[i - 1] - 1; // 与str[i]配对的左括号的位置 pre + if (pre >= 0 && str[pre] == '(') { + dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0); + } + } + ans = Math.max(ans, dp[i]); + } + return ans; + } + +} diff --git a/大厂刷题班/class14/Code02_MaxSubArraySumLessOrEqualK.java b/大厂刷题班/class14/Code02_MaxSubArraySumLessOrEqualK.java new file mode 100644 index 0000000..0456e4b --- /dev/null +++ b/大厂刷题班/class14/Code02_MaxSubArraySumLessOrEqualK.java @@ -0,0 +1,28 @@ +package class14; + +import java.util.TreeSet; + +public class Code02_MaxSubArraySumLessOrEqualK { + + // 请返回arr中,求个子数组的累加和,是<=K的,并且是最大的。 + // 返回这个最大的累加和 + public static int getMaxLessOrEqualK(int[] arr, int K) { + // 记录i之前的,前缀和,按照有序表组织 + TreeSet set = new TreeSet(); + // 一个数也没有的时候,就已经有一个前缀和是0了 + set.add(0); + int max = Integer.MIN_VALUE; + int sum = 0; + // 每一步的i,都求子数组必须以i结尾的情况下,求个子数组的累加和,是<=K的,并且是最大的 + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; // sum -> arr[0..i]; + if (set.ceiling(sum - K) != null) { + max = Math.max(max, sum - set.ceiling(sum - K)); + } + set.add(sum); // 当前的前缀和加入到set中去 + } + return max; + + } + +} diff --git a/大厂刷题班/class14/Code03_BiggestBSTTopologyInTree.java b/大厂刷题班/class14/Code03_BiggestBSTTopologyInTree.java new file mode 100644 index 0000000..c45d2f8 --- /dev/null +++ b/大厂刷题班/class14/Code03_BiggestBSTTopologyInTree.java @@ -0,0 +1,65 @@ +package class14; + +// 本题测试链接 : https://www.nowcoder.com/practice/e13bceaca5b14860b83cbcc4912c5d4a +// 提交以下的代码,并把主类名改成Main +// 可以直接通过 +import java.util.Scanner; + +public class Code03_BiggestBSTTopologyInTree { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int n = sc.nextInt(); + int h = sc.nextInt(); + int[][] tree = new int[n + 1][3]; + for (int i = 1; i <= n; i++) { + int c = sc.nextInt(); + int l = sc.nextInt(); + int r = sc.nextInt(); + tree[l][0] = c; + tree[r][0] = c; + tree[c][1] = l; + tree[c][2] = r; + } + System.out.println(maxBSTTopology(h, tree, new int[n + 1])); + sc.close(); + } + + // h: 代表当前的头节点 + // t: 代表树 t[i][0]是i节点的父节点、t[i][1]是i节点的左孩子、t[i][2]是i节点的右孩子 + // m: i节点为头的最大bst拓扑结构大小 -> m[i] + // 返回: 以h为头的整棵树上,最大bst拓扑结构的大小 + public static int maxBSTTopology(int h, int[][] t, int[] m) { + if (h == 0) { + return 0; + } + int l = t[h][1]; + int r = t[h][2]; + int c = 0; + int p1 = maxBSTTopology(l, t, m); + int p2 = maxBSTTopology(r, t, m); + while (l < h && m[l] != 0) { + l = t[l][2]; + } + if (m[l] != 0) { + c = m[l]; + while (l != h) { + m[l] -= c; + l = t[l][0]; + } + } + while (r > h && m[r] != 0) { + r = t[r][1]; + } + if (m[r] != 0) { + c = m[r]; + while (r != h) { + m[r] -= c; + r = t[r][0]; + } + } + m[h] = m[t[h][1]] + m[t[h][2]] + 1; + return Math.max(Math.max(p1, p2), m[h]); + } + +} diff --git a/大厂刷题班/class14/Code04_CompleteTreeNodeNumber.java b/大厂刷题班/class14/Code04_CompleteTreeNodeNumber.java new file mode 100644 index 0000000..5e0c09b --- /dev/null +++ b/大厂刷题班/class14/Code04_CompleteTreeNodeNumber.java @@ -0,0 +1,45 @@ +package class14; + +//本题测试链接 : https://leetcode.cn/problems/count-complete-tree-nodes/ +public class Code04_CompleteTreeNodeNumber { + + // 提交时不要提交这个类 + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + } + + // 提交如下的方法 + public static int countNodes(TreeNode head) { + if (head == null) { + return 0; + } + return bs(head, 1, mostLeftLevel(head, 1)); + } + + // 当前来到node节点,node节点在level层,总层数是h + // 返回node为头的子树(必是完全二叉树),有多少个节点 + public static int bs(TreeNode node, int Level, int h) { + if (Level == h) { + return 1; + } + if (mostLeftLevel(node.right, Level + 1) == h) { + return (1 << (h - Level)) + bs(node.right, Level + 1, h); + } else { + return (1 << (h - Level - 1)) + bs(node.left, Level + 1, h); + } + } + + // 如果node在第level层, + // 求以node为头的子树,最大深度是多少 + // node为头的子树,一定是完全二叉树 + public static int mostLeftLevel(TreeNode node, int level) { + while (node != null) { + level++; + node = node.left; + } + return level - 1; + } + +} diff --git a/大厂刷题班/class14/Code05_RecoverBinarySearchTree.java b/大厂刷题班/class14/Code05_RecoverBinarySearchTree.java new file mode 100644 index 0000000..2ee6e62 --- /dev/null +++ b/大厂刷题班/class14/Code05_RecoverBinarySearchTree.java @@ -0,0 +1,511 @@ +package class14; + +import java.util.Stack; + +// 本题测试链接 : https://leetcode.com/problems/recover-binary-search-tree/ +public class Code05_RecoverBinarySearchTree { + + // 不要提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int v) { + val = v; + } + } + + // 如果能过leetcode,只需要提交这个方法即可 + // 但其实recoverTree2才是正路,只不过leetcode没有那么考 + public static void recoverTree(TreeNode root) { + TreeNode[] errors = twoErrors(root); + if (errors[0] != null && errors[1] != null) { + int tmp = errors[0].val; + errors[0].val = errors[1].val; + errors[1].val = tmp; + } + } + + public static TreeNode[] twoErrors(TreeNode head) { + TreeNode[] ans = new TreeNode[2]; + if (head == null) { + return ans; + } + TreeNode cur = head; + TreeNode mostRight = null; + TreeNode pre = null; + TreeNode e1 = null; + TreeNode e2 = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } + if (pre != null && pre.val >= cur.val) { + e1 = e1 == null ? pre : e1; + e2 = cur; + } + pre = cur; + cur = cur.right; + } + ans[0] = e1; + ans[1] = e2; + return ans; + } + + // 以下的方法,提交leetcode是通过不了的,但那是因为leetcode的验证方式有问题 + // 但其实!以下的方法,才是正路!在结构上彻底交换两个节点,而不是值交换 + public static TreeNode recoverTree2(TreeNode head) { + TreeNode[] errs = getTwoErrNodes(head); + TreeNode[] parents = getTwoErrParents(head, errs[0], errs[1]); + TreeNode e1 = errs[0]; + TreeNode e1P = parents[0]; + TreeNode e1L = e1.left; + TreeNode e1R = e1.right; + TreeNode e2 = errs[1]; + TreeNode e2P = parents[1]; + TreeNode e2L = e2.left; + TreeNode e2R = e2.right; + if (e1 == head) { + if (e1 == e2P) { + e1.left = e2L; + e1.right = e2R; + e2.right = e1; + e2.left = e1L; + } else if (e2P.left == e2) { + e2P.left = e1; + e2.left = e1L; + e2.right = e1R; + e1.left = e2L; + e1.right = e2R; + } else { + e2P.right = e1; + e2.left = e1L; + e2.right = e1R; + e1.left = e2L; + e1.right = e2R; + } + head = e2; + } else if (e2 == head) { + if (e2 == e1P) { + e2.left = e1L; + e2.right = e1R; + e1.left = e2; + e1.right = e2R; + } else if (e1P.left == e1) { + e1P.left = e2; + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1R; + } else { + e1P.right = e2; + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1R; + } + head = e1; + } else { + if (e1 == e2P) { + if (e1P.left == e1) { + e1P.left = e2; + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1; + } else { + e1P.right = e2; + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1; + } + } else if (e2 == e1P) { + if (e2P.left == e2) { + e2P.left = e1; + e2.left = e1L; + e2.right = e1R; + e1.left = e2; + e1.right = e2R; + } else { + e2P.right = e1; + e2.left = e1L; + e2.right = e1R; + e1.left = e2; + e1.right = e2R; + } + } else { + if (e1P.left == e1) { + if (e2P.left == e2) { + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1R; + e1P.left = e2; + e2P.left = e1; + } else { + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1R; + e1P.left = e2; + e2P.right = e1; + } + } else { + if (e2P.left == e2) { + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1R; + e1P.right = e2; + e2P.left = e1; + } else { + e1.left = e2L; + e1.right = e2R; + e2.left = e1L; + e2.right = e1R; + e1P.right = e2; + e2P.right = e1; + } + } + } + } + return head; + } + + public static TreeNode[] getTwoErrNodes(TreeNode head) { + TreeNode[] errs = new TreeNode[2]; + if (head == null) { + return errs; + } + Stack stack = new Stack(); + TreeNode pre = null; + while (!stack.isEmpty() || head != null) { + if (head != null) { + stack.push(head); + head = head.left; + } else { + head = stack.pop(); + if (pre != null && pre.val > head.val) { + errs[0] = errs[0] == null ? pre : errs[0]; + errs[1] = head; + } + pre = head; + head = head.right; + } + } + return errs; + } + + public static TreeNode[] getTwoErrParents(TreeNode head, TreeNode e1, TreeNode e2) { + TreeNode[] parents = new TreeNode[2]; + if (head == null) { + return parents; + } + Stack stack = new Stack(); + while (!stack.isEmpty() || head != null) { + if (head != null) { + stack.push(head); + head = head.left; + } else { + head = stack.pop(); + if (head.left == e1 || head.right == e1) { + parents[0] = head; + } + if (head.left == e2 || head.right == e2) { + parents[1] = head; + } + head = head.right; + } + } + return parents; + } + + // for test -- print tree + public static void printTree(TreeNode head) { + System.out.println("Binary Tree:"); + printInOrder(head, 0, "H", 17); + System.out.println(); + } + + public static void printInOrder(TreeNode head, int height, String to, int len) { + if (head == null) { + return; + } + printInOrder(head.right, height + 1, "v", len); + String val = to + head.val + to; + int lenM = val.length(); + int lenL = (len - lenM) / 2; + int lenR = len - lenM - lenL; + val = getSpace(lenL) + val + getSpace(lenR); + System.out.println(getSpace(height * len) + val); + printInOrder(head.left, height + 1, "^", len); + } + + public static String getSpace(int num) { + String space = " "; + StringBuffer buf = new StringBuffer(""); + for (int i = 0; i < num; i++) { + buf.append(space); + } + return buf.toString(); + } + + // 为了测试 + public static boolean isBST(TreeNode head) { + if (head == null) { + return false; + } + Stack stack = new Stack(); + TreeNode pre = null; + while (!stack.isEmpty() || head != null) { + if (head != null) { + stack.push(head); + head = head.left; + } else { + head = stack.pop(); + if (pre != null && pre.val > head.val) { + return false; + } + pre = head; + head = head.right; + } + } + return true; + } + + public static void main(String[] args) { + TreeNode head = new TreeNode(5); + head.left = new TreeNode(3); + head.right = new TreeNode(7); + head.left.left = new TreeNode(2); + head.left.right = new TreeNode(4); + head.right.left = new TreeNode(6); + head.right.right = new TreeNode(8); + head.left.left.left = new TreeNode(1); + printTree(head); + System.out.println(isBST(head)); + + System.out.println("situation 1"); + TreeNode head1 = new TreeNode(7); + head1.left = new TreeNode(3); + head1.right = new TreeNode(5); + head1.left.left = new TreeNode(2); + head1.left.right = new TreeNode(4); + head1.right.left = new TreeNode(6); + head1.right.right = new TreeNode(8); + head1.left.left.left = new TreeNode(1); + printTree(head1); + System.out.println(isBST(head1)); + TreeNode res1 = recoverTree2(head1); + printTree(res1); + System.out.println(isBST(res1)); + + System.out.println("situation 2"); + TreeNode head2 = new TreeNode(6); + head2.left = new TreeNode(3); + head2.right = new TreeNode(7); + head2.left.left = new TreeNode(2); + head2.left.right = new TreeNode(4); + head2.right.left = new TreeNode(5); + head2.right.right = new TreeNode(8); + head2.left.left.left = new TreeNode(1); + printTree(head2); + System.out.println(isBST(head2)); + TreeNode res2 = recoverTree2(head2); + printTree(res2); + System.out.println(isBST(res2)); + + System.out.println("situation 3"); + TreeNode head3 = new TreeNode(8); + head3.left = new TreeNode(3); + head3.right = new TreeNode(7); + head3.left.left = new TreeNode(2); + head3.left.right = new TreeNode(4); + head3.right.left = new TreeNode(6); + head3.right.right = new TreeNode(5); + head3.left.left.left = new TreeNode(1); + printTree(head3); + System.out.println(isBST(head3)); + TreeNode res3 = recoverTree2(head3); + printTree(res3); + System.out.println(isBST(res3)); + + System.out.println("situation 4"); + TreeNode head4 = new TreeNode(3); + head4.left = new TreeNode(5); + head4.right = new TreeNode(7); + head4.left.left = new TreeNode(2); + head4.left.right = new TreeNode(4); + head4.right.left = new TreeNode(6); + head4.right.right = new TreeNode(8); + head4.left.left.left = new TreeNode(1); + printTree(head4); + System.out.println(isBST(head4)); + TreeNode res4 = recoverTree2(head4); + printTree(res4); + System.out.println(isBST(res4)); + + System.out.println("situation 5"); + TreeNode head5 = new TreeNode(2); + head5.left = new TreeNode(3); + head5.right = new TreeNode(7); + head5.left.left = new TreeNode(5); + head5.left.right = new TreeNode(4); + head5.right.left = new TreeNode(6); + head5.right.right = new TreeNode(8); + head5.left.left.left = new TreeNode(1); + printTree(head5); + System.out.println(isBST(head5)); + TreeNode res5 = recoverTree2(head5); + printTree(res5); + System.out.println(isBST(res5)); + + System.out.println("situation 6"); + TreeNode head6 = new TreeNode(4); + head6.left = new TreeNode(3); + head6.right = new TreeNode(7); + head6.left.left = new TreeNode(2); + head6.left.right = new TreeNode(5); + head6.right.left = new TreeNode(6); + head6.right.right = new TreeNode(8); + head6.left.left.left = new TreeNode(1); + printTree(head6); + System.out.println(isBST(head6)); + TreeNode res6 = recoverTree2(head6); + printTree(res6); + System.out.println(isBST(res6)); + + System.out.println("situation 7"); + TreeNode head7 = new TreeNode(5); + head7.left = new TreeNode(4); + head7.right = new TreeNode(7); + head7.left.left = new TreeNode(2); + head7.left.right = new TreeNode(3); + head7.right.left = new TreeNode(6); + head7.right.right = new TreeNode(8); + head7.left.left.left = new TreeNode(1); + printTree(head7); + System.out.println(isBST(head7)); + TreeNode res7 = recoverTree2(head7); + printTree(res7); + System.out.println(isBST(res7)); + + System.out.println("situation 8"); + TreeNode head8 = new TreeNode(5); + head8.left = new TreeNode(3); + head8.right = new TreeNode(8); + head8.left.left = new TreeNode(2); + head8.left.right = new TreeNode(4); + head8.right.left = new TreeNode(6); + head8.right.right = new TreeNode(7); + head8.left.left.left = new TreeNode(1); + printTree(head8); + System.out.println(isBST(head8)); + TreeNode res8 = recoverTree2(head8); + printTree(res8); + System.out.println(isBST(res8)); + + System.out.println("situation 9"); + TreeNode head9 = new TreeNode(5); + head9.left = new TreeNode(2); + head9.right = new TreeNode(7); + head9.left.left = new TreeNode(3); + head9.left.right = new TreeNode(4); + head9.right.left = new TreeNode(6); + head9.right.right = new TreeNode(8); + head9.left.left.left = new TreeNode(1); + printTree(head9); + System.out.println(isBST(head9)); + TreeNode res9 = recoverTree2(head9); + printTree(res9); + System.out.println(isBST(res9)); + + System.out.println("situation 10"); + TreeNode head10 = new TreeNode(5); + head10.left = new TreeNode(3); + head10.right = new TreeNode(6); + head10.left.left = new TreeNode(2); + head10.left.right = new TreeNode(4); + head10.right.left = new TreeNode(7); + head10.right.right = new TreeNode(8); + head10.left.left.left = new TreeNode(1); + printTree(head10); + System.out.println(isBST(head10)); + TreeNode res10 = recoverTree2(head10); + printTree(res10); + System.out.println(isBST(res10)); + + System.out.println("situation 11"); + TreeNode head11 = new TreeNode(5); + head11.left = new TreeNode(3); + head11.right = new TreeNode(7); + head11.left.left = new TreeNode(6); + head11.left.right = new TreeNode(4); + head11.right.left = new TreeNode(2); + head11.right.right = new TreeNode(8); + head11.left.left.left = new TreeNode(1); + printTree(head11); + System.out.println(isBST(head11)); + TreeNode res11 = recoverTree2(head11); + printTree(res11); + System.out.println(isBST(res11)); + + System.out.println("situation 12"); + TreeNode head12 = new TreeNode(5); + head12.left = new TreeNode(3); + head12.right = new TreeNode(7); + head12.left.left = new TreeNode(8); + head12.left.right = new TreeNode(4); + head12.right.left = new TreeNode(6); + head12.right.right = new TreeNode(2); + head12.left.left.left = new TreeNode(1); + printTree(head12); + System.out.println(isBST(head12)); + TreeNode res12 = recoverTree2(head12); + printTree(res12); + System.out.println(isBST(res12)); + + System.out.println("situation 13"); + TreeNode head13 = new TreeNode(5); + head13.left = new TreeNode(3); + head13.right = new TreeNode(7); + head13.left.left = new TreeNode(2); + head13.left.right = new TreeNode(6); + head13.right.left = new TreeNode(4); + head13.right.right = new TreeNode(8); + head13.left.left.left = new TreeNode(1); + printTree(head13); + System.out.println(isBST(head13)); + TreeNode res13 = recoverTree2(head13); + printTree(res13); + System.out.println(isBST(res13)); + + System.out.println("situation 14"); + TreeNode head14 = new TreeNode(5); + head14.left = new TreeNode(3); + head14.right = new TreeNode(7); + head14.left.left = new TreeNode(2); + head14.left.right = new TreeNode(8); + head14.right.left = new TreeNode(6); + head14.right.right = new TreeNode(4); + head14.left.left.left = new TreeNode(1); + printTree(head14); + System.out.println(isBST(head14)); + TreeNode res14 = recoverTree2(head14); + printTree(res14); + System.out.println(isBST(res14)); + } + +} diff --git a/大厂刷题班/class14/Code06_MissingNumber.java b/大厂刷题班/class14/Code06_MissingNumber.java new file mode 100644 index 0000000..9abca16 --- /dev/null +++ b/大厂刷题班/class14/Code06_MissingNumber.java @@ -0,0 +1,29 @@ +package class14; + +// 测试链接:https://leetcode.com/problems/first-missing-positive/ +public class Code06_MissingNumber { + + public static int firstMissingPositive(int[] arr) { + // l是盯着的位置 + // 0 ~ L-1有效区 + int L = 0; + int R = arr.length; + while (L != R) { + if (arr[L] == L + 1) { + L++; + } else if (arr[L] <= L || arr[L] > R || arr[arr[L] - 1] == arr[L]) { // 垃圾的情况 + swap(arr, L, --R); + } else { + swap(arr, L, arr[L] - 1); + } + } + return L + 1; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + +} diff --git a/大厂刷题班/class15/Code01_BestTimeToBuyAndSellStock.java b/大厂刷题班/class15/Code01_BestTimeToBuyAndSellStock.java new file mode 100644 index 0000000..3660c43 --- /dev/null +++ b/大厂刷题班/class15/Code01_BestTimeToBuyAndSellStock.java @@ -0,0 +1,21 @@ +package class15; + +// leetcode 121 +public class Code01_BestTimeToBuyAndSellStock { + + public static int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + // 必须在0时刻卖掉,[0] - [0] + int ans = 0; + // arr[0...0] + int min = prices[0]; + for (int i = 1; i < prices.length; i++) { + min = Math.min(min, prices[i]); + ans = Math.max(ans, prices[i] - min); + } + return ans; + } + +} diff --git a/大厂刷题班/class15/Code02_BestTimeToBuyAndSellStockII.java b/大厂刷题班/class15/Code02_BestTimeToBuyAndSellStockII.java new file mode 100644 index 0000000..c751f54 --- /dev/null +++ b/大厂刷题班/class15/Code02_BestTimeToBuyAndSellStockII.java @@ -0,0 +1,17 @@ +package class15; + +//leetcode 122 +public class Code02_BestTimeToBuyAndSellStockII { + + public static int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int ans = 0; + for (int i = 1; i < prices.length; i++) { + ans += Math.max(prices[i] - prices[i-1], 0); + } + return ans; + } + +} diff --git a/大厂刷题班/class15/Code03_BestTimeToBuyAndSellStockIII.java b/大厂刷题班/class15/Code03_BestTimeToBuyAndSellStockIII.java new file mode 100644 index 0000000..a1aadde --- /dev/null +++ b/大厂刷题班/class15/Code03_BestTimeToBuyAndSellStockIII.java @@ -0,0 +1,23 @@ +package class15; + +//leetcode 123 +public class Code03_BestTimeToBuyAndSellStockIII { + + public static int maxProfit(int[] prices) { + if (prices == null || prices.length < 2) { + return 0; + } + int ans = 0; + int doneOnceMinusBuyMax = -prices[0]; + int doneOnceMax = 0; + int min = prices[0]; + for (int i = 1; i < prices.length; i++) { + min = Math.min(min, prices[i]); + ans = Math.max(ans, doneOnceMinusBuyMax + prices[i]); + doneOnceMax = Math.max(doneOnceMax, prices[i] - min); + doneOnceMinusBuyMax = Math.max(doneOnceMinusBuyMax, doneOnceMax - prices[i]); + } + return ans; + } + +} diff --git a/大厂刷题班/class15/Code04_BestTimeToBuyAndSellStockIV.java b/大厂刷题班/class15/Code04_BestTimeToBuyAndSellStockIV.java new file mode 100644 index 0000000..7b8bd72 --- /dev/null +++ b/大厂刷题班/class15/Code04_BestTimeToBuyAndSellStockIV.java @@ -0,0 +1,65 @@ +package class15; + +//leetcode 188 +public class Code04_BestTimeToBuyAndSellStockIV { + + public static int maxProfit(int K, int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int N = prices.length; + if (K >= N / 2) { + return allTrans(prices); + } + int[][] dp = new int[K + 1][N]; + int ans = 0; + for (int tran = 1; tran <= K; tran++) { + int pre = dp[tran][0]; + int best = pre - prices[0]; + for (int index = 1; index < N; index++) { + pre = dp[tran - 1][index]; + dp[tran][index] = Math.max(dp[tran][index - 1], prices[index] + best); + best = Math.max(best, pre - prices[index]); + ans = Math.max(dp[tran][index], ans); + } + } + return ans; + } + + public static int allTrans(int[] prices) { + int ans = 0; + for (int i = 1; i < prices.length; i++) { + ans += Math.max(prices[i] - prices[i - 1], 0); + } + return ans; + } + + // 课上写的版本,对了 + public static int maxProfit2(int K, int[] arr) { + if (arr == null || arr.length == 0 || K < 1) { + return 0; + } + int N = arr.length; + if (K >= N / 2) { + return allTrans(arr); + } + int[][] dp = new int[N][K + 1]; + // dp[...][0] = 0 + // dp[0][...] = arr[0.0] 0 + for (int j = 1; j <= K; j++) { + // dp[1][j] + int p1 = dp[0][j]; + int best = Math.max(dp[1][j - 1] - arr[1], dp[0][j - 1] - arr[0]); + dp[1][j] = Math.max(p1, best + arr[1]); + // dp[1][j] 准备好一些枚举,接下来准备好的枚举 + for (int i = 2; i < N; i++) { + p1 = dp[i - 1][j]; + int newP = dp[i][j - 1] - arr[i]; + best = Math.max(newP, best); + dp[i][j] = Math.max(p1, best + arr[i]); + } + } + return dp[N - 1][K]; + } + +} diff --git a/大厂刷题班/class15/Code05_BestTimeToBuyAndSellStockWithCooldown.java b/大厂刷题班/class15/Code05_BestTimeToBuyAndSellStockWithCooldown.java new file mode 100644 index 0000000..0dea85d --- /dev/null +++ b/大厂刷题班/class15/Code05_BestTimeToBuyAndSellStockWithCooldown.java @@ -0,0 +1,101 @@ +package class15; + +//leetcode 309 +public class Code05_BestTimeToBuyAndSellStockWithCooldown { + + // 该方法是对的,提交之后大量的测试用例通过,最后几个会超时 + // 如果把这个方法改成动态规划,是可以通过的,但这个尝试不是最优解 + public static int maxProfit1(int[] prices) { + return process1(prices, false, 0, 0); + } + + // buy == false 目前可以交易,而且当前没有购买行为 + // buy == true 已经买了,买入价buyPrices,待卖出 + public static int process1(int[] prices, boolean buy, int index, int buyPrices) { + if (index >= prices.length) { + return 0; + } + if (buy) { + int noSell = process1(prices, true, index + 1, buyPrices); + int yesSell = prices[index] - buyPrices + process1(prices, false, index + 2, 0); + return Math.max(noSell, yesSell); + } else { + int noBuy = process1(prices, false, index + 1, 0); + int yesBuy = process1(prices, true, index + 1, prices[index]); + return Math.max(noBuy, yesBuy); + } + } + + // 最优尝试如下: + // buy[i] : 在0...i范围上,最后一次操作是buy动作, + // 这最后一次操作有可能发生在i位置,也可能发生在i之前 + // buy[i]值的含义是:max{ 所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格] } + // 比如:arr[0...i]假设为[1,3,4,6,2,7,1...i之后的数字不用管] + // 什么叫,所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格]? + // 比如其中一种可能性: + // 假设最后一次buy动作发生在2这个数的时候,那么之前的交易只能在[1,3,4]上结束,因为6要cooldown的, + // 此时最大收益是多少呢?是4-1==3。那么,之前交易获得的最大收益 - 最后buy动作的收购价格 = 3 - 2 = 1 + // 另一种可能性: + // 再比如最后一次buy动作发生在最后的1这个数的时候,那么之前的交易只能在[1,3,4,6,2]上发生,因为7要cooldown的, + // 此时最大收益是多少呢?是6-1==5。那么,之前交易获得的最大收益 - 最后buy动作的收购价格 = 5 - 1 = 4 + // 除了上面两种可能性之外,还有很多可能性,你可以假设每个数字都是最后buy动作的时候, + // 所有可能性中,(之前交易获得的最大收益 - 最后buy动作的收购价格)的最大值,就是buy[i]的含义 + // 为啥buy[i]要算之前的最大收益 - 最后一次收购价格?尤其是最后为什么要减这么一下? + // 因为这样一来,当你之后以X价格做成一笔交易的时候,当前最好的总收益直接就是 X + buy[i]了 + // + // sell[i] :0...i范围上,最后一次操作是sell动作,这最后一次操作有可能发生在i位置,也可能发生在之前 + // sell[i]值的含义:0...i范围上,最后一次动作是sell的情况下,最好的收益 + // + // 于是通过分析,能得到以下的转移方程: + // buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i]) + + + + + // 如果i位置没有发生buy行为,说明有没有i位置都一样,那么buy[i] = buy[i-1],这显而易见 + // 如果i位置发生了buy行为, 那么buy[i] = sell[i - 2] - prices[i], + // 因为你想在i位置买的话,你必须保证之前交易行为发生在0...i-2上, + // 因为如果i-1位置有可能参与交易的话,i位置就要cooldown了, + // 而且之前交易行为必须以sell结束,你才能buy,而且要保证之前交易尽可能得到最好的利润, + // 这正好是sell[i - 2]所代表的含义,并且根据buy[i]的定义,最后一定要 - prices[i] + // + // sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]) + // 如果i位置没有发生sell行为,那么sell[i] = sell[i-1],这显而易见 + // 如果i位置发生了sell行为,那么我们一定要找到 {之前获得尽可能好的收益 - 最后一次的收购价格尽可能低}, + // 而这正好是buy[i - 1]的含义!之前所有的"尽可能"中,最好的一个! + public static int maxProfit2(int[] prices) { + if (prices.length < 2) { + return 0; + } + int N = prices.length; + int[] buy = new int[N]; + int[] sell = new int[N]; + // buy[0] 不需要设置 buy[0] = -arr[0] + // sell[0] = 0 + buy[1] = Math.max(-prices[0], -prices[1]); + sell[1] = Math.max(0, prices[1] - prices[0]); + for (int i = 2; i < N; i++) { + buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i]); + sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]); + } + return sell[N - 1]; + } + + // 最优解就是方法2的空间优化而已 + public static int maxProfit3(int[] prices) { + if (prices.length < 2) { + return 0; + } + int buy1 = Math.max(-prices[0], -prices[1]); + int sell1 = Math.max(0, prices[1] - prices[0]); + int sell2 = 0; + for (int i = 2; i < prices.length; i++) { + int tmp = sell1; + sell1 = Math.max(sell1, buy1 + prices[i]); + buy1 = Math.max(buy1, sell2 - prices[i]); + sell2 = tmp; + } + return sell1; + } + +} diff --git a/大厂刷题班/class15/Code06_BestTimeToBuyAndSellStockWithTransactionFee.java b/大厂刷题班/class15/Code06_BestTimeToBuyAndSellStockWithTransactionFee.java new file mode 100644 index 0000000..121e8a6 --- /dev/null +++ b/大厂刷题班/class15/Code06_BestTimeToBuyAndSellStockWithTransactionFee.java @@ -0,0 +1,27 @@ +package class15; + +//leetcode 714 +public class Code06_BestTimeToBuyAndSellStockWithTransactionFee { + + public static int maxProfit(int[] arr, int fee) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + // 0..0 0 -[0] - fee + int bestbuy = -arr[0] - fee; + // 0..0 卖 0 + int bestsell = 0; + for (int i = 1; i < N; i++) { + // 来到i位置了! + // 如果在i必须买 收入 - 批发价 - fee + int curbuy = bestsell - arr[i] - fee; + // 如果在i必须卖 整体最优(收入 - 良好批发价 - fee) + int cursell = bestbuy + arr[i]; + bestbuy = Math.max(bestbuy, curbuy); + bestsell = Math.max(bestsell, cursell); + } + return bestsell; + } + +} diff --git a/大厂刷题班/class16/Code01_IsSum.java b/大厂刷题班/class16/Code01_IsSum.java new file mode 100644 index 0000000..210c277 --- /dev/null +++ b/大厂刷题班/class16/Code01_IsSum.java @@ -0,0 +1,206 @@ +package class16; + +import java.util.HashMap; +import java.util.HashSet; + +// 这道题是一个小小的补充,课上没有讲 +// 但是如果你听过体系学习班动态规划专题和本节课的话 +// 这道题就是一道水题 +public class Code01_IsSum { + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 暴力递归方法 + public static boolean isSum1(int[] arr, int sum) { + if (sum == 0) { + return true; + } + if (arr == null || arr.length == 0) { + return false; + } + return process1(arr, arr.length - 1, sum); + } + + // 可以自由使用arr[0...i]上的数字,能不能累加得到sum + public static boolean process1(int[] arr, int i, int sum) { + if (sum == 0) { + return true; + } + if (i == -1) { + return false; + } + return process1(arr, i - 1, sum) || process1(arr, i - 1, sum - arr[i]); + } + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 记忆化搜索方法 + // 从暴力递归方法来,加了记忆化缓存,就是动态规划了 + public static boolean isSum2(int[] arr, int sum) { + if (sum == 0) { + return true; + } + if (arr == null || arr.length == 0) { + return false; + } + return process2(arr, arr.length - 1, sum, new HashMap<>()); + } + + public static boolean process2(int[] arr, int i, int sum, HashMap> dp) { + if (dp.containsKey(i) && dp.get(i).containsKey(sum)) { + return dp.get(i).get(sum); + } + boolean ans = false; + if (sum == 0) { + ans = true; + } else if (i != -1) { + ans = process2(arr, i - 1, sum, dp) || process2(arr, i - 1, sum - arr[i], dp); + } + if (!dp.containsKey(i)) { + dp.put(i, new HashMap<>()); + } + dp.get(i).put(sum, ans); + return ans; + } + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 经典动态规划 + public static boolean isSum3(int[] arr, int sum) { + if (sum == 0) { + return true; + } + // sum != 0 + if (arr == null || arr.length == 0) { + return false; + } + // arr有数,sum不为0 + int min = 0; + int max = 0; + for (int num : arr) { + min += num < 0 ? num : 0; + max += num > 0 ? num : 0; + } + // min~max + if (sum < min || sum > max) { + return false; + } + + // min <= sum <= max + int N = arr.length; + // dp[i][j] + // + // 0 1 2 3 4 5 6 7 (实际的对应) + // -7 -6 -5 -4 -3 -2 -1 0(想象中) + // + // dp[0][-min] -> dp[0][7] -> dp[0][0] + boolean[][] dp = new boolean[N][max - min + 1]; + // dp[0][0] = true + dp[0][-min] = true; + // dp[0][arr[0]] = true + dp[0][arr[0] - min] = true; + for (int i = 1; i < N; i++) { + for (int j = min; j <= max; j++) { + // dp[i][j] = dp[i-1][j] + dp[i][j - min] = dp[i - 1][j - min]; + // dp[i][j] |= dp[i-1][j - arr[i]] + int next = j - min - arr[i]; + dp[i][j - min] |= (next >= 0 && next <= max - min && dp[i - 1][next]); + } + } + // dp[N-1][sum] + return dp[N - 1][sum - min]; + } + + // arr中的值可能为正,可能为负,可能为0 + // 自由选择arr中的数字,能不能累加得到sum + // 分治的方法 + // 如果arr中的数值特别大,动态规划方法依然会很慢 + // 此时如果arr的数字个数不算多(40以内),哪怕其中的数值很大,分治的方法也将是最优解 + public static boolean isSum4(int[] arr, int sum) { + if (sum == 0) { + return true; + } + if (arr == null || arr.length == 0) { + return false; + } + if (arr.length == 1) { + return arr[0] == sum; + } + int N = arr.length; + int mid = N >> 1; + HashSet leftSum = new HashSet<>(); + HashSet rightSum = new HashSet<>(); + // 0...mid-1 + process4(arr, 0, mid, 0, leftSum); + // mid..N-1 + process4(arr, mid, N, 0, rightSum); + // 单独查看,只使用左部分,能不能搞出sum + // 单独查看,只使用右部分,能不能搞出sum + // 左+右,联合能不能搞出sum + // 左部分搞出所有累加和的时候,包含左部分一个数也没有,这种情况的,leftsum表里,0 + // 17 17 + for (int l : leftSum) { + if (rightSum.contains(sum - l)) { + return true; + } + } + return false; + } + + // arr[0...i-1]决定已经做完了!形成的累加和是pre + // arr[i...end - 1] end(终止) 所有数字随意选择, + // arr[0...end-1]所有可能的累加和存到ans里去 + public static void process4(int[] arr, int i, int end, int pre, HashSet ans) { + if (i == end) { + ans.add(pre); + } else { + process4(arr, i + 1, end, pre, ans); + process4(arr, i + 1, end, pre + arr[i], ans); + } + } + + // 为了测试 + // 生成长度为len的随机数组 + // 值在[-max, max]上随机 + public static int[] randomArray(int len, int max) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * ((max << 1) + 1)) - max; + } + return arr; + } + + // 对数器验证所有方法 + public static void main(String[] args) { + int N = 20; + int M = 100; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * (N + 1)); + int[] arr = randomArray(size, M); + int sum = (int) (Math.random() * ((M << 1) + 1)) - M; + boolean ans1 = isSum1(arr, sum); + boolean ans2 = isSum2(arr, sum); + boolean ans3 = isSum3(arr, sum); + boolean ans4 = isSum4(arr, sum); + if (ans1 ^ ans2 || ans3 ^ ans4 || ans1 ^ ans3) { + System.out.println("出错了!"); + System.out.print("arr : "); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("sum : " + sum); + System.out.println("方法一答案 : " + ans1); + System.out.println("方法二答案 : " + ans2); + System.out.println("方法三答案 : " + ans3); + System.out.println("方法四答案 : " + ans4); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class16/Code02_SmallestUnFormedSum.java b/大厂刷题班/class16/Code02_SmallestUnFormedSum.java new file mode 100644 index 0000000..645b3af --- /dev/null +++ b/大厂刷题班/class16/Code02_SmallestUnFormedSum.java @@ -0,0 +1,123 @@ +package class16; + +import java.util.Arrays; +import java.util.HashSet; + +public class Code02_SmallestUnFormedSum { + + public static int unformedSum1(int[] arr) { + if (arr == null || arr.length == 0) { + return 1; + } + HashSet set = new HashSet(); + process(arr, 0, 0, set); + int min = Integer.MAX_VALUE; + for (int i = 0; i != arr.length; i++) { + min = Math.min(min, arr[i]); + } + for (int i = min + 1; i != Integer.MIN_VALUE; i++) { + if (!set.contains(i)) { + return i; + } + } + return 0; + } + + public static void process(int[] arr, int i, int sum, HashSet set) { + if (i == arr.length) { + set.add(sum); + return; + } + process(arr, i + 1, sum, set); + process(arr, i + 1, sum + arr[i], set); + } + + public static int unformedSum2(int[] arr) { + if (arr == null || arr.length == 0) { + return 1; + } + int sum = 0; + int min = Integer.MAX_VALUE; + for (int i = 0; i != arr.length; i++) { + sum += arr[i]; + min = Math.min(min, arr[i]); + } + // boolean[][] dp ... + int N = arr.length; + boolean[][] dp = new boolean[N][sum + 1]; + for (int i = 0; i < N; i++) {// arr[0..i] 0 + dp[i][0] = true; + } + dp[0][arr[0]] = true; + for (int i = 1; i < N; i++) { + for (int j = 1; j <= sum; j++) { + dp[i][j] = dp[i - 1][j] || ((j - arr[i] >= 0) ? dp[i - 1][j - arr[i]] : false); + } + } + for (int j = min; j <= sum; j++) { + if (!dp[N - 1][j]) { + return j; + } + } + return sum + 1; + } + + // 已知arr中肯定有1这个数 + public static int unformedSum3(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + Arrays.sort(arr); // O (N * logN) + int range = 1; + // arr[0] == 1 + for (int i = 1; i != arr.length; i++) { + if (arr[i] > range + 1) { + return range + 1; + } else { + range += arr[i]; + } + } + return range + 1; + } + + public static int[] generateArray(int len, int maxValue) { + int[] res = new int[len]; + for (int i = 0; i != res.length; i++) { + res[i] = (int) (Math.random() * maxValue) + 1; + } + return res; + } + + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 27; + int max = 30; + int[] arr = generateArray(len, max); + printArray(arr); + long start = System.currentTimeMillis(); + System.out.println(unformedSum1(arr)); + long end = System.currentTimeMillis(); + System.out.println("cost time: " + (end - start) + " ms"); + System.out.println("======================================"); + + start = System.currentTimeMillis(); + System.out.println(unformedSum2(arr)); + end = System.currentTimeMillis(); + System.out.println("cost time: " + (end - start) + " ms"); + System.out.println("======================================"); + + System.out.println("set arr[0] to 1"); + arr[0] = 1; + start = System.currentTimeMillis(); + System.out.println(unformedSum3(arr)); + end = System.currentTimeMillis(); + System.out.println("cost time: " + (end - start) + " ms"); + + } +} diff --git a/大厂刷题班/class16/Code03_MinPatches.java b/大厂刷题班/class16/Code03_MinPatches.java new file mode 100644 index 0000000..de5eb5d --- /dev/null +++ b/大厂刷题班/class16/Code03_MinPatches.java @@ -0,0 +1,82 @@ +package class16; + +import java.util.Arrays; + +public class Code03_MinPatches { + + // arr请保证有序,且正数 1~aim + public static int minPatches(int[] arr, int aim) { + int patches = 0; // 缺多少个数字 + long range = 0; // 已经完成了1 ~ range的目标 + Arrays.sort(arr); + for (int i = 0; i != arr.length; i++) { + // arr[i] + // 要求:1 ~ arr[i]-1 范围被搞定! + while (arr[i] - 1 > range) { // arr[i] 1 ~ arr[i]-1 + range += range + 1; // range + 1 是缺的数字 + patches++; + if (range >= aim) { + return patches; + } + } + // 要求被满足了! + range += arr[i]; + if (range >= aim) { + return patches; + } + } + while (aim >= range + 1) { + range += range + 1; + patches++; + } + return patches; + } + + // 嘚瑟 + public static int minPatches2(int[] arr, int K) { + int patches = 0; // 缺多少个数字 + int range = 0; // 已经完成了1 ~ range的目标 + for (int i = 0; i != arr.length; i++) { + // 1~range + // 1 ~ arr[i]-1 + while (arr[i] > range + 1) { // arr[i] 1 ~ arr[i]-1 + + if (range > Integer.MAX_VALUE - range - 1) { + return patches + 1; + } + + range += range + 1; // range + 1 是缺的数字 + patches++; + if (range >= K) { + return patches; + } + } + if (range > Integer.MAX_VALUE - arr[i]) { + return patches; + } + range += arr[i]; + if (range >= K) { + return patches; + } + } + while (K >= range + 1) { + if (K == range && K == Integer.MAX_VALUE) { + return patches; + } + if (range > Integer.MAX_VALUE - range - 1) { + return patches + 1; + } + range += range + 1; + patches++; + } + return patches; + } + + public static void main(String[] args) { + int[] test = { 1, 2, 31, 33 }; + int n = 2147483647; + System.out.println(minPatches(test, n)); + + } + +} diff --git a/大厂刷题班/class16/Code04_MergeRecord.java b/大厂刷题班/class16/Code04_MergeRecord.java new file mode 100644 index 0000000..a7b1798 --- /dev/null +++ b/大厂刷题班/class16/Code04_MergeRecord.java @@ -0,0 +1,220 @@ +package class16; + +public class Code04_MergeRecord { + + /* + * 腾讯原题 + * + * 给定整数power,给定一个数组arr,给定一个数组reverse。含义如下: + * arr的长度一定是2的power次方,reverse中的每个值一定都在0~power范围。 + * 例如power = 2, arr = {3, 1, 4, 2},reverse = {0, 1, 0, 2} + * 任何一个在前的数字可以和任何一个在后的数组,构成一对数。可能是升序关系、相等关系或者降序关系。 + * 比如arr开始时有如下的降序对:(3,1)、(3,2)、(4,2),一共3个。 + * 接下来根据reverse对arr进行调整: + * reverse[0] = 0, 表示在arr中,划分每1(2的0次方)个数一组,然后每个小组内部逆序,那么arr变成 + * [3,1,4,2],此时有3个逆序对。 + * reverse[1] = 1, 表示在arr中,划分每2(2的1次方)个数一组,然后每个小组内部逆序,那么arr变成 + * [1,3,2,4],此时有1个逆序对 + * reverse[2] = 0, 表示在arr中,划分每1(2的0次方)个数一组,然后每个小组内部逆序,那么arr变成 + * [1,3,2,4],此时有1个逆序对。 + * reverse[3] = 2, 表示在arr中,划分每4(2的2次方)个数一组,然后每个小组内部逆序,那么arr变成 + * [4,2,3,1],此时有5个逆序对。 + * 所以返回[3,1,1,5],表示每次调整之后的逆序对数量。 + * + * 输入数据状况: + * power的范围[0,20] + * arr长度范围[1,10的7次方] + * reverse长度范围[1,10的6次方] + * + * */ + + public static int[] reversePair1(int[] originArr, int[] reverseArr, int power) { + int[] ans = new int[reverseArr.length]; + for (int i = 0; i < reverseArr.length; i++) { + reverseArray(originArr, 1 << (reverseArr[i])); + ans[i] = countReversePair(originArr); + } + return ans; + } + + public static void reverseArray(int[] originArr, int teamSize) { + if (teamSize < 2) { + return; + } + for (int i = 0; i < originArr.length; i += teamSize) { + reversePart(originArr, i, i + teamSize - 1); + } + } + + public static void reversePart(int[] arr, int L, int R) { + while (L < R) { + int tmp = arr[L]; + arr[L++] = arr[R]; + arr[R--] = tmp; + } + } + + public static int countReversePair(int[] originArr) { + int ans = 0; + for (int i = 0; i < originArr.length; i++) { + for (int j = i + 1; j < originArr.length; j++) { + if (originArr[i] > originArr[j]) { + ans++; + } + } + } + return ans; + } + + public static int[] reversePair2(int[] originArr, int[] reverseArr, int power) { + int[] reverse = copyArray(originArr); + reversePart(reverse, 0, reverse.length - 1); + int[] recordDown = new int[power + 1]; + int[] recordUp = new int[power + 1]; + process(originArr, 0, originArr.length - 1, power, recordDown); + process(reverse, 0, reverse.length - 1, power, recordUp); + int[] ans = new int[reverseArr.length]; + for (int i = 0; i < reverseArr.length; i++) { + int curPower = reverseArr[i]; + for (int p = 1; p <= curPower; p++) { + int tmp = recordDown[p]; + recordDown[p] = recordUp[p]; + recordUp[p] = tmp; + } + for (int p = 1; p <= power; p++) { + ans[i] += recordDown[p]; + } + } + return ans; + } + + // originArr[L...R]完成排序! + // L...M左 M...R右 merge + // L...R 2的power次方 + + public static void process(int[] originArr, int L, int R, int power, int[] record) { + if (L == R) { + return; + } + int mid = L + ((R - L) >> 1); + process(originArr, L, mid, power - 1, record); + process(originArr, mid + 1, R, power - 1, record); + record[power] += merge(originArr, 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 ans = 0; + while (p1 <= m && p2 <= r) { + ans += arr[p1] > arr[p2] ? (m - p1 + 1) : 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 ans; + } + + // for test + public static int[] generateRandomOriginArray(int power, int value) { + int[] ans = new int[1 << power]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * value); + } + return ans; + } + + // for test + public static int[] generateRandomReverseArray(int len, int power) { + int[] ans = new int[len]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * (power + 1)); + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + System.out.println("arr size : " + arr.length); + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + int powerMax = 8; + int msizeMax = 10; + int value = 30; + int testTime = 50000; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int power = (int) (Math.random() * powerMax) + 1; + int msize = (int) (Math.random() * msizeMax) + 1; + int[] originArr = generateRandomOriginArray(power, value); + int[] originArr1 = copyArray(originArr); + int[] originArr2 = copyArray(originArr); + int[] reverseArr = generateRandomReverseArray(msize, power); + int[] reverseArr1 = copyArray(reverseArr); + int[] reverseArr2 = copyArray(reverseArr); + int[] ans1 = reversePair1(originArr1, reverseArr1, power); + int[] ans2 = reversePair2(originArr2, reverseArr2, power); + if (!isEqual(ans1, ans2)) { + System.out.println("Oops!"); + } + } + System.out.println("test finish!"); + + powerMax = 20; + msizeMax = 1000000; + value = 1000; + int[] originArr = generateRandomOriginArray(powerMax, value); + int[] reverseArr = generateRandomReverseArray(msizeMax, powerMax); + // int[] ans1 = reversePair1(originArr1, reverseArr1, powerMax); + long start = System.currentTimeMillis(); + reversePair2(originArr, reverseArr, powerMax); + long end = System.currentTimeMillis(); + System.out.println("run time : " + (end - start) + " ms"); + } + +} diff --git a/大厂刷题班/class16/Code05_JosephusProblem.java b/大厂刷题班/class16/Code05_JosephusProblem.java new file mode 100644 index 0000000..0aaebd8 --- /dev/null +++ b/大厂刷题班/class16/Code05_JosephusProblem.java @@ -0,0 +1,120 @@ +package class16; + +// 本题测试链接 : https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/ +public class Code05_JosephusProblem { + + // 提交直接通过 + // 给定的编号是0~n-1的情况下,数到m就杀 + // 返回谁会活? + public int lastRemaining1(int n, int m) { + return getLive(n, m) - 1; + } + + // 课上题目的设定是,给定的编号是1~n的情况下,数到m就杀 + // 返回谁会活? + public static int getLive(int n, int m) { + if (n == 1) { + return 1; + } + return (getLive(n - 1, m) + m - 1) % n + 1; + } + + // 提交直接通过 + // 给定的编号是0~n-1的情况下,数到m就杀 + // 返回谁会活? + // 这个版本是迭代版 + public int lastRemaining2(int n, int m) { + int ans = 1; + int r = 1; + while (r <= n) { + ans = (ans + m - 1) % (r++) + 1; + } + return ans - 1; + } + + // 以下的code针对单链表,不要提交 + public static class Node { + public int value; + public Node next; + + public Node(int data) { + this.value = data; + } + } + + public static Node josephusKill1(Node head, int m) { + if (head == null || head.next == head || m < 1) { + return head; + } + Node last = head; + while (last.next != head) { + last = last.next; + } + int count = 0; + while (head != last) { + if (++count == m) { + last.next = head.next; + count = 0; + } else { + last = last.next; + } + head = last.next; + } + return head; + } + + public static Node josephusKill2(Node head, int m) { + if (head == null || head.next == head || m < 1) { + return head; + } + Node cur = head.next; + int size = 1; // tmp -> list size + while (cur != head) { + size++; + cur = cur.next; + } + int live = getLive(size, m); // tmp -> service node position + while (--live != 0) { + head = head.next; + } + head.next = head; + return head; + } + + public static void printCircularList(Node head) { + if (head == null) { + return; + } + System.out.print("Circular List: " + head.value + " "); + Node cur = head.next; + while (cur != head) { + System.out.print(cur.value + " "); + cur = cur.next; + } + System.out.println("-> " + head.value); + } + + public static void main(String[] args) { + Node head1 = new Node(1); + head1.next = new Node(2); + head1.next.next = new Node(3); + head1.next.next.next = new Node(4); + head1.next.next.next.next = new Node(5); + head1.next.next.next.next.next = head1; + printCircularList(head1); + head1 = josephusKill1(head1, 3); + printCircularList(head1); + + Node head2 = new Node(1); + head2.next = new Node(2); + head2.next.next = new Node(3); + head2.next.next.next = new Node(4); + head2.next.next.next.next = new Node(5); + head2.next.next.next.next.next = head2; + printCircularList(head2); + head2 = josephusKill2(head2, 3); + printCircularList(head2); + + } + +} \ No newline at end of file diff --git a/大厂刷题班/class17/Code01_FindNumInSortedMatrix.java b/大厂刷题班/class17/Code01_FindNumInSortedMatrix.java new file mode 100644 index 0000000..16aea90 --- /dev/null +++ b/大厂刷题班/class17/Code01_FindNumInSortedMatrix.java @@ -0,0 +1,34 @@ +package class17; + +public class Code01_FindNumInSortedMatrix { + + public static boolean isContains(int[][] matrix, int K) { + int row = 0; + int col = matrix[0].length - 1; + while (row < matrix.length && col > -1) { + if (matrix[row][col] == K) { + return true; + } else if (matrix[row][col] > K) { + col--; + } else { + row++; + } + } + return false; + } + + public static void main(String[] args) { + int[][] matrix = new int[][] { { 0, 1, 2, 3, 4, 5, 6 }, // 0 + { 10, 12, 13, 15, 16, 17, 18 }, // 1 + { 23, 24, 25, 26, 27, 28, 29 }, // 2 + { 44, 45, 46, 47, 48, 49, 50 }, // 3 + { 65, 66, 67, 68, 69, 70, 71 }, // 4 + { 96, 97, 98, 99, 100, 111, 122 }, // 5 + { 166, 176, 186, 187, 190, 195, 200 }, // 6 + { 233, 243, 321, 341, 356, 370, 380 } // 7 + }; + int K = 233; + System.out.println(isContains(matrix, K)); + } + +} diff --git a/大厂刷题班/class17/Code02_KthSmallestElementInSortedMatrix.java b/大厂刷题班/class17/Code02_KthSmallestElementInSortedMatrix.java new file mode 100644 index 0000000..0dcd8e9 --- /dev/null +++ b/大厂刷题班/class17/Code02_KthSmallestElementInSortedMatrix.java @@ -0,0 +1,110 @@ +package class17; + +import java.util.Comparator; +import java.util.PriorityQueue; + +// 本题测试链接 : https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/ +public class Code02_KthSmallestElementInSortedMatrix { + + // 堆的方法 + public static int kthSmallest1(int[][] matrix, int k) { + int N = matrix.length; + int M = matrix[0].length; + PriorityQueue heap = new PriorityQueue<>(new NodeComparator()); + boolean[][] set = new boolean[N][M]; + heap.add(new Node(matrix[0][0], 0, 0)); + set[0][0] = true; + int count = 0; + Node ans = null; + while (!heap.isEmpty()) { + ans = heap.poll(); + if (++count == k) { + break; + } + int row = ans.row; + int col = ans.col; + if (row + 1 < N && !set[row + 1][col]) { + heap.add(new Node(matrix[row + 1][col], row + 1, col)); + set[row + 1][col] = true; + } + if (col + 1 < M && !set[row][col + 1]) { + heap.add(new Node(matrix[row][col + 1], row, col + 1)); + set[row][col + 1] = true; + } + } + return ans.value; + } + + public static class Node { + public int value; + public int row; + public int col; + + public Node(int v, int r, int c) { + value = v; + row = r; + col = c; + } + + } + + public static class NodeComparator implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.value - o2.value; + } + + } + + // 二分的方法 + public static int kthSmallest2(int[][] matrix, int k) { + int N = matrix.length; + int M = matrix[0].length; + int left = matrix[0][0]; + int right = matrix[N - 1][M - 1]; + int ans = 0; + while (left <= right) { + int mid = left + ((right - left) >> 1); + // <=mid 有几个 <= mid 在矩阵中真实出现的数,谁最接近mid + Info info = noMoreNum(matrix, mid); + if (info.num < k) { + left = mid + 1; + } else { + ans = info.near; + right = mid - 1; + } + } + return ans; + } + + public static class Info { + public int near; + public int num; + + public Info(int n1, int n2) { + near = n1; + num = n2; + } + } + + public static Info noMoreNum(int[][] matrix, int value) { + int near = Integer.MIN_VALUE; + int num = 0; + int N = matrix.length; + int M = matrix[0].length; + int row = 0; + int col = M - 1; + while (row < N && col >= 0) { + if (matrix[row][col] <= value) { + near = Math.max(near, matrix[row][col]); + num += col + 1; + row++; + } else { + col--; + } + } + return new Info(near, num); + } + +} diff --git a/大厂刷题班/class17/Code03_PalindromePairs.java b/大厂刷题班/class17/Code03_PalindromePairs.java new file mode 100644 index 0000000..b3d110b --- /dev/null +++ b/大厂刷题班/class17/Code03_PalindromePairs.java @@ -0,0 +1,104 @@ +package class17; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +// 测试链接 : https://leetcode.com/problems/palindrome-pairs/ +public class Code03_PalindromePairs { + + public static List> palindromePairs(String[] words) { + HashMap wordset = new HashMap<>(); + for (int i = 0; i < words.length; i++) { + wordset.put(words[i], i); + } + List> res = new ArrayList<>(); + //{ [6,23] 、 [7,13] } + for (int i = 0; i < words.length; i++) { + // i words[i] + // findAll(字符串,在i位置,wordset) 返回所有生成的结果返回 + res.addAll(findAll(words[i], i, wordset)); + } + return res; + } + + public static List> findAll(String word, int index, HashMap words) { + List> res = new ArrayList<>(); + String reverse = reverse(word); + Integer rest = words.get(""); + if (rest != null && rest != index && word.equals(reverse)) { + addRecord(res, rest, index); + addRecord(res, index, rest); + } + int[] rs = manacherss(word); + int mid = rs.length >> 1; + for (int i = 1; i < mid; i++) { + if (i - rs[i] == -1) { + rest = words.get(reverse.substring(0, mid - i)); + if (rest != null && rest != index) { + addRecord(res, rest, index); + } + } + } + for (int i = mid + 1; i < rs.length; i++) { + if (i + rs[i] == rs.length) { + rest = words.get(reverse.substring((mid << 1) - i)); + if (rest != null && rest != index) { + addRecord(res, index, rest); + } + } + } + return res; + } + + public static void addRecord(List> res, int left, int right) { + List newr = new ArrayList<>(); + newr.add(left); + newr.add(right); + res.add(newr); + } + + public static int[] manacherss(String word) { + char[] mchs = manachercs(word); + int[] rs = new int[mchs.length]; + int center = -1; + int pr = -1; + for (int i = 0; i != mchs.length; i++) { + rs[i] = pr > i ? Math.min(rs[(center << 1) - i], pr - i) : 1; + while (i + rs[i] < mchs.length && i - rs[i] > -1) { + if (mchs[i + rs[i]] != mchs[i - rs[i]]) { + break; + } + rs[i]++; + } + if (i + rs[i] > pr) { + pr = i + rs[i]; + center = i; + } + } + return rs; + } + + public static char[] manachercs(String word) { + char[] chs = word.toCharArray(); + char[] mchs = new char[chs.length * 2 + 1]; + int index = 0; + for (int i = 0; i != mchs.length; i++) { + mchs[i] = (i & 1) == 0 ? '#' : chs[index++]; + } + return mchs; + } + + public static String reverse(String str) { + char[] chs = str.toCharArray(); + int l = 0; + int r = chs.length - 1; + while (l < r) { + char tmp = chs[l]; + chs[l++] = chs[r]; + chs[r--] = tmp; + } + return String.valueOf(chs); + } + +} \ No newline at end of file diff --git a/大厂刷题班/class17/Code04_DistinctSubseq.java b/大厂刷题班/class17/Code04_DistinctSubseq.java new file mode 100644 index 0000000..ff047e4 --- /dev/null +++ b/大厂刷题班/class17/Code04_DistinctSubseq.java @@ -0,0 +1,101 @@ +package class17; + +// 测试链接 : https://leetcode-cn.com/problems/21dk04/ +public class Code04_DistinctSubseq { + + public static int numDistinct1(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + return process(s, t, s.length, t.length); + } + + public static int process(char[] s, char[] t, int i, int j) { + if (j == 0) { + return 1; + } + if (i == 0) { + return 0; + } + int res = process(s, t, i - 1, j); + if (s[i - 1] == t[j - 1]) { + res += process(s, t, i - 1, j - 1); + } + return res; + } + + // S[...i]的所有子序列中,包含多少个字面值等于T[...j]这个字符串的子序列 + // 记为dp[i][j] + // 可能性1)S[...i]的所有子序列中,都不以s[i]结尾,则dp[i][j]肯定包含dp[i-1][j] + // 可能性2)S[...i]的所有子序列中,都必须以s[i]结尾, + // 这要求S[i] == T[j],则dp[i][j]包含dp[i-1][j-1] + public static int numDistinct2(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + // dp[i][j] : s[0..i] T[0...j] + + // dp[i][j] : s只拿前i个字符做子序列,有多少个子序列,字面值等于T的前j个字符的前缀串 + int[][] dp = new int[s.length + 1][t.length + 1]; + // dp[0][0] + // dp[0][j] = s只拿前0个字符做子序列, T前j个字符 + for (int j = 0; j <= t.length; j++) { + dp[0][j] = 0; + } + for (int i = 0; i <= s.length; i++) { + dp[i][0] = 1; + } + for (int i = 1; i <= s.length; i++) { + for (int j = 1; j <= t.length; j++) { + dp[i][j] = dp[i - 1][j] + (s[i - 1] == t[j - 1] ? dp[i - 1][j - 1] : 0); + } + } + return dp[s.length][t.length]; + } + + public static int numDistinct3(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + int[] dp = new int[t.length + 1]; + dp[0] = 1; + for (int j = 1; j <= t.length; j++) { + dp[j] = 0; + } + for (int i = 1; i <= s.length; i++) { + for (int j = t.length; j >= 1; j--) { + dp[j] += s[i - 1] == t[j - 1] ? dp[j - 1] : 0; + } + } + return dp[t.length]; + } + + public static int dp(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + int N = s.length; + int M = t.length; + int[][] dp = new int[N][M]; + // s[0..0] T[0..0] dp[0][0] + dp[0][0] = s[0] == t[0] ? 1 : 0; + for (int i = 1; i < N; i++) { + dp[i][0] = s[i] == t[0] ? (dp[i - 1][0] + 1) : dp[i - 1][0]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j <= Math.min(i, M - 1); j++) { + dp[i][j] = dp[i - 1][j]; + if (s[i] == t[j]) { + dp[i][j] += dp[i - 1][j - 1]; + } + } + } + return dp[N - 1][M - 1]; + } + + public static void main(String[] args) { + String S = "1212311112121132"; + String T = "13"; + + System.out.println(numDistinct3(S, T)); + System.out.println(dp(S, T)); + + } + +} diff --git a/大厂刷题班/class17/Code05_DistinctSubseqValue.java b/大厂刷题班/class17/Code05_DistinctSubseqValue.java new file mode 100644 index 0000000..17f00d5 --- /dev/null +++ b/大厂刷题班/class17/Code05_DistinctSubseqValue.java @@ -0,0 +1,50 @@ +package class17; + +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.com/problems/distinct-subsequences-ii/ +public class Code05_DistinctSubseqValue { + + public static int distinctSubseqII(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int m = 1000000007; + char[] str = s.toCharArray(); + int[] count = new int[26]; + int all = 1; // 算空集 + for (char x : str) { + int add = (all - count[x - 'a'] + m) % m; + all = (all + add) % m; + count[x - 'a'] = (count[x - 'a'] + add) % m; + } + return all - 1; + } + + public static int zuo(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int m = 1000000007; + char[] str = s.toCharArray(); + HashMap map = new HashMap<>(); + int all = 1; // 一个字符也没遍历的时候,有空集 + for (char x : str) { + int newAdd = all; +// int curAll = all + newAdd - (map.containsKey(x) ? map.get(x) : 0); + int curAll = all; + curAll = (curAll + newAdd) % m; + curAll = (curAll - (map.containsKey(x) ? map.get(x) : 0) + m) % m; + all = curAll; + map.put(x, newAdd); + } + return all; + } + + public static void main(String[] args) { + String s = "bccaccbaabbc"; + System.out.println(distinctSubseqII(s) + 1); + System.out.println(zuo(s)); + } + +} diff --git a/大厂刷题班/class18/Code01_HanoiProblem.java b/大厂刷题班/class18/Code01_HanoiProblem.java new file mode 100644 index 0000000..588377e --- /dev/null +++ b/大厂刷题班/class18/Code01_HanoiProblem.java @@ -0,0 +1,97 @@ +package class18; + +public class Code01_HanoiProblem { + + public static int step1(int[] arr) { + if (arr == null || arr.length == 0) { + return -1; + } + return process(arr, arr.length - 1, 1, 2, 3); + } + + // 目标是: 把0~i的圆盘,从from全部挪到to上 + // 返回,根据arr中的状态arr[0..i],它是最优解的第几步? + // f(i, 3 , 2, 1) f(i, 1, 3, 2) f(i, 3, 1, 2) + public static int process(int[] arr, int i, int from, int other, int to) { + if (i == -1) { + return 0; + } + if (arr[i] != from && arr[i] != to) { + return -1; + } + if (arr[i] == from) { // 第一大步没走完 + return process(arr, i - 1, from, to, other); + } else { // arr[i] == to + // 已经走完1,2两步了, + int rest = process(arr, i - 1, other, from, to); // 第三大步完成的程度 + if (rest == -1) { + return -1; + } + return (1 << i) + rest; + } + } + + public static int step2(int[] arr) { + if (arr == null || arr.length == 0) { + return -1; + } + int from = 1; + int mid = 2; + int to = 3; + int i = arr.length - 1; + int res = 0; + int tmp = 0; + while (i >= 0) { + if (arr[i] != from && arr[i] != to) { + return -1; + } + if (arr[i] == to) { + res += 1 << i; + tmp = from; + from = mid; + } else { + tmp = to; + to = mid; + } + mid = tmp; + i--; + } + return res; + } + + public static int kth(int[] arr) { + int N = arr.length; + return step(arr, N - 1, 1, 3, 2); + } + + // 0...index这些圆盘,arr[0..index] index+1层塔 + // 在哪?from 去哪?to 另一个是啥?other + // arr[0..index]这些状态,是index+1层汉诺塔问题的,最优解第几步 + public static int step(int[] arr, int index, int from, int to, int other) { + if (index == -1) { + return 0; + } + if (arr[index] == other) { + return -1; + } + // arr[index] == from arr[index] == to; + if (arr[index] == from) { + return step(arr, index - 1, from, other, to); + } else { + int p1 = (1 << index) - 1; + int p2 = 1; + int p3 = step(arr, index - 1, other, to, from); + if (p3 == -1) { + return -1; + } + return p1 + p2 + p3; + } + } + + public static void main(String[] args) { + int[] arr = { 3, 3, 2, 1 }; + System.out.println(step1(arr)); + System.out.println(step2(arr)); + System.out.println(kth(arr)); + } +} diff --git a/大厂刷题班/class18/Code02_ShortestBridge.java b/大厂刷题班/class18/Code02_ShortestBridge.java new file mode 100644 index 0000000..cac7b20 --- /dev/null +++ b/大厂刷题班/class18/Code02_ShortestBridge.java @@ -0,0 +1,96 @@ +package class18; + +// 本题测试链接 : https://leetcode.com/problems/shortest-bridge/ +public class Code02_ShortestBridge { + + public static int shortestBridge(int[][] m) { + int N = m.length; + int M = m[0].length; + int all = N * M; + int island = 0; + int[] curs = new int[all]; + int[] nexts = new int[all]; + int[][] records = new int[2][all]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (m[i][j] == 1) { // 当前位置发现了1! + // 把这一片的1,都变成2,同时,抓上来了,这一片1组成的初始队列 + // curs, 把这一片的1到自己的距离,都设置成1了,records + int queueSize = infect(m, i, j, N, M, curs, 0, records[island]); + int V = 1; + while (queueSize != 0) { + V++; + // curs里面的点,上下左右,records[点]==0, nexts + queueSize = bfs(N, M, all, V, curs, queueSize, nexts, records[island]); + int[] tmp = curs; + curs = nexts; + nexts = tmp; + } + island++; + } + } + } + int min = Integer.MAX_VALUE; + for (int i = 0; i < all; i++) { + min = Math.min(min, records[0][i] + records[1][i]); + } + return min - 3; + } + + // 当前来到m[i][j] , 总行数是N,总列数是M + // m[i][j]感染出去(找到这一片岛所有的1),把每一个1的坐标,放入到int[] curs队列! + // 1 (a,b) -> curs[index++] = (a * M + b) + // 1 (c,d) -> curs[index++] = (c * M + d) + // 二维已经变成一维了, 1 (a,b) -> a * M + b + // 设置距离record[a * M +b ] = 1 + public static int infect(int[][] m, int i, int j, int N, int M, int[] curs, int index, int[] record) { + if (i < 0 || i == N || j < 0 || j == M || m[i][j] != 1) { + return index; + } + // m[i][j] 不越界,且m[i][j] == 1 + m[i][j] = 2; + int p = i * M + j; + record[p] = 1; + // 收集到不同的1 + curs[index++] = p; + index = infect(m, i - 1, j, N, M, curs, index, record); + index = infect(m, i + 1, j, N, M, curs, index, record); + index = infect(m, i, j - 1, N, M, curs, index, record); + index = infect(m, i, j + 1, N, M, curs, index, record); + return index; + } + + // 二维原始矩阵中,N总行数,M总列数 + // all 总 all = N * M + // V 要生成的是第几层 curs V-1 nexts V + // record里面拿距离 + public static int bfs(int N, int M, int all, int V, + int[] curs, int size, int[] nexts, int[] record) { + int nexti = 0; // 我要生成的下一层队列成长到哪了? + for (int i = 0; i < size; i++) { + // curs[i] -> 一个位置 + int up = curs[i] < M ? -1 : curs[i] - M; + int down = curs[i] + M >= all ? -1 : curs[i] + M; + int left = curs[i] % M == 0 ? -1 : curs[i] - 1; + int right = curs[i] % M == M - 1 ? -1 : curs[i] + 1; + if (up != -1 && record[up] == 0) { + record[up] = V; + nexts[nexti++] = up; + } + if (down != -1 && record[down] == 0) { + record[down] = V; + nexts[nexti++] = down; + } + if (left != -1 && record[left] == 0) { + record[left] = V; + nexts[nexti++] = left; + } + if (right != -1 && record[right] == 0) { + record[right] = V; + nexts[nexti++] = right; + } + } + return nexti; + } + +} diff --git a/大厂刷题班/class18/Code03_CherryPickup.java b/大厂刷题班/class18/Code03_CherryPickup.java new file mode 100644 index 0000000..07b9c54 --- /dev/null +++ b/大厂刷题班/class18/Code03_CherryPickup.java @@ -0,0 +1,70 @@ +package class18; + +// 牛客的测试链接: +// https://www.nowcoder.com/questionTerminal/8ecfe02124674e908b2aae65aad4efdf +// 把如下的全部代码拷贝进java编辑器 +// 把文件大类名字改成Main,可以直接通过 + +import java.util.Scanner; + +public class Code03_CherryPickup { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int N = sc.nextInt(); + int M = sc.nextInt(); + int[][] matrix = new int[N][M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + matrix[i][j] = sc.nextInt(); + } + } + int ans = cherryPickup(matrix); + System.out.println(ans); + sc.close(); + } + + public static int cherryPickup(int[][] grid) { + int N = grid.length; + int M = grid[0].length; + int[][][] dp = new int[N][M][N]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + for (int k = 0; k < N; k++) { + dp[i][j][k] = Integer.MIN_VALUE; + } + } + } + int ans = process(grid, 0, 0, 0, dp); + return ans < 0 ? 0 : ans; + } + + public static int process(int[][] grid, int x1, int y1, int x2, int[][][] dp) { + if (x1 == grid.length || y1 == grid[0].length || x2 == grid.length || x1 + y1 - x2 == grid[0].length) { + return Integer.MIN_VALUE; + } + if (dp[x1][y1][x2] != Integer.MIN_VALUE) { + return dp[x1][y1][x2]; + } + if (x1 == grid.length - 1 && y1 == grid[0].length - 1) { + dp[x1][y1][x2] = grid[x1][y1]; + return dp[x1][y1][x2]; + } + int next = Integer.MIN_VALUE; + next = Math.max(next, process(grid, x1 + 1, y1, x2 + 1, dp)); + next = Math.max(next, process(grid, x1 + 1, y1, x2, dp)); + next = Math.max(next, process(grid, x1, y1 + 1, x2, dp)); + next = Math.max(next, process(grid, x1, y1 + 1, x2 + 1, dp)); + if (grid[x1][y1] == -1 || grid[x2][x1 + y1 - x2] == -1 || next == -1) { + dp[x1][y1][x2] = -1; + return dp[x1][y1][x2]; + } + if (x1 == x2) { + dp[x1][y1][x2] = grid[x1][y1] + next; + return dp[x1][y1][x2]; + } + dp[x1][y1][x2] = grid[x1][y1] + grid[x2][x1 + y1 - x2] + next; + return dp[x1][y1][x2]; + } + +} \ No newline at end of file diff --git a/大厂刷题班/class18/Code04_TopKSumCrossTwoArrays.java b/大厂刷题班/class18/Code04_TopKSumCrossTwoArrays.java new file mode 100644 index 0000000..9820986 --- /dev/null +++ b/大厂刷题班/class18/Code04_TopKSumCrossTwoArrays.java @@ -0,0 +1,93 @@ +package class18; + +// 牛客的测试链接: +// https://www.nowcoder.com/practice/7201cacf73e7495aa5f88b223bbbf6d1 +// 不要提交包信息,把import底下的类名改成Main,提交下面的代码可以直接通过 +// 因为测试平台会卡空间,所以把set换成了动态加和减的结构 + +import java.util.Scanner; +import java.util.Comparator; +import java.util.HashSet; +import java.util.PriorityQueue; + +public class Code04_TopKSumCrossTwoArrays { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int N = sc.nextInt(); + int K = sc.nextInt(); + int[] arr1 = new int[N]; + int[] arr2 = new int[N]; + for (int i = 0; i < N; i++) { + arr1[i] = sc.nextInt(); + } + for (int i = 0; i < N; i++) { + arr2[i] = sc.nextInt(); + } + int[] topK = topKSum(arr1, arr2, K); + for (int i = 0; i < K; i++) { + System.out.print(topK[i] + " "); + } + System.out.println(); + sc.close(); + } + + // 放入大根堆中的结构 + public static class Node { + public int index1;// arr1中的位置 + public int index2;// arr2中的位置 + public int sum;// arr1[index1] + arr2[index2]的值 + + public Node(int i1, int i2, int s) { + index1 = i1; + index2 = i2; + sum = s; + } + } + + // 生成大根堆的比较器 + public static class MaxHeapComp implements Comparator { + @Override + public int compare(Node o1, Node o2) { + return o2.sum - o1.sum; + } + } + + public static int[] topKSum(int[] arr1, int[] arr2, int topK) { + if (arr1 == null || arr2 == null || topK < 1) { + return null; + } + int N = arr1.length; + int M = arr2.length; + topK = Math.min(topK, N * M); + int[] res = new int[topK]; + int resIndex = 0; + PriorityQueue maxHeap = new PriorityQueue<>(new MaxHeapComp()); + HashSet set = new HashSet<>(); + int i1 = N - 1; + int i2 = M - 1; + maxHeap.add(new Node(i1, i2, arr1[i1] + arr2[i2])); + set.add(x(i1, i2, M)); + while (resIndex != topK) { + Node curNode = maxHeap.poll(); + res[resIndex++] = curNode.sum; + i1 = curNode.index1; + i2 = curNode.index2; + set.remove(x(i1, i2, M)); + if (i1 - 1 >= 0 && !set.contains(x(i1 - 1, i2, M))) { + set.add(x(i1 - 1, i2, M)); + maxHeap.add(new Node(i1 - 1, i2, arr1[i1 - 1] + arr2[i2])); + } + if (i2 - 1 >= 0 && !set.contains(x(i1, i2 - 1, M))) { + set.add(x(i1, i2 - 1, M)); + maxHeap.add(new Node(i1, i2 - 1, arr1[i1] + arr2[i2 - 1])); + } + } + return res; + } + + public static long x(int i1, int i2, int M) { + return (long) i1 * (long) M + (long) i2; + } + +} diff --git a/大厂刷题班/class19/Code01_LRUCache.java b/大厂刷题班/class19/Code01_LRUCache.java new file mode 100644 index 0000000..4cae760 --- /dev/null +++ b/大厂刷题班/class19/Code01_LRUCache.java @@ -0,0 +1,142 @@ +package class19; + +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.com/problems/lru-cache/ +// 提交时把类名和构造方法名改成 : LRUCache +public class Code01_LRUCache { + + public Code01_LRUCache(int capacity) { + cache = new MyCache<>(capacity); + } + + private MyCache cache; + + public int get(int key) { + Integer ans = cache.get(key); + return ans == null ? -1 : ans; + } + + public void put(int key, int value) { + cache.set(key, value); + } + + public static class Node { + public K key; + public V value; + public Node last; + public Node next; + + public Node(K key, V value) { + this.key = key; + this.value = value; + } + } + + public static class NodeDoubleLinkedList { + private Node head; + private Node tail; + + public NodeDoubleLinkedList() { + head = null; + tail = null; + } + + // 现在来了一个新的node,请挂到尾巴上去 + public void addNode(Node newNode) { + if (newNode == null) { + return; + } + if (head == null) { + head = newNode; + tail = newNode; + } else { + tail.next = newNode; + newNode.last = tail; + tail = newNode; + } + } + + // node 入参,一定保证!node在双向链表里! + // node原始的位置,左右重新连好,然后把node分离出来 + // 挂到整个链表的尾巴上 + public void moveNodeToTail(Node node) { + if (tail == node) { + return; + } + if (head == node) { + head = node.next; + head.last = null; + } else { + node.last.next = node.next; + node.next.last = node.last; + } + node.last = tail; + node.next = null; + tail.next = node; + tail = node; + } + + public Node removeHead() { + if (head == null) { + return null; + } + Node res = head; + if (head == tail) { + head = null; + tail = null; + } else { + head = res.next; + res.next = null; + head.last = null; + } + return res; + } + + } + + public static class MyCache { + private HashMap> keyNodeMap; + private NodeDoubleLinkedList nodeList; + private final int capacity; + + public MyCache(int cap) { + keyNodeMap = new HashMap>(); + nodeList = new NodeDoubleLinkedList(); + capacity = cap; + } + + public V get(K key) { + if (keyNodeMap.containsKey(key)) { + Node res = keyNodeMap.get(key); + nodeList.moveNodeToTail(res); + return res.value; + } + return null; + } + + // set(Key, Value) + // 新增 更新value的操作 + public void set(K key, V value) { + if (keyNodeMap.containsKey(key)) { + Node node = keyNodeMap.get(key); + node.value = value; + nodeList.moveNodeToTail(node); + } else { // 新增! + Node newNode = new Node(key, value); + keyNodeMap.put(key, newNode); + nodeList.addNode(newNode); + if (keyNodeMap.size() == capacity + 1) { + removeMostUnusedCache(); + } + } + } + + private void removeMostUnusedCache() { + Node removeNode = nodeList.removeHead(); + keyNodeMap.remove(removeNode.key); + } + + } + +} diff --git a/大厂刷题班/class19/Code02_LFUCache.java b/大厂刷题班/class19/Code02_LFUCache.java new file mode 100644 index 0000000..30ed43a --- /dev/null +++ b/大厂刷题班/class19/Code02_LFUCache.java @@ -0,0 +1,204 @@ +package class19; + +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.com/problems/lfu-cache/ +// 提交时把类名和构造方法名改为 : LFUCache +public class Code02_LFUCache { + + public Code02_LFUCache(int K) { + capacity = K; + size = 0; + records = new HashMap<>(); + heads = new HashMap<>(); + headList = null; + } + + private int capacity; // 缓存的大小限制,即K + private int size; // 缓存目前有多少个节点 + private HashMap records;// 表示key(Integer)由哪个节点(Node)代表 + private HashMap heads; // 表示节点(Node)在哪个桶(NodeList)里 + private NodeList headList; // 整个结构中位于最左的桶 + + // 节点的数据结构 + public static class Node { + public Integer key; + public Integer value; + public Integer times; // 这个节点发生get或者set的次数总和 + public Node up; // 节点之间是双向链表所以有上一个节点 + public Node down;// 节点之间是双向链表所以有下一个节点 + + public Node(int k, int v, int t) { + key = k; + value = v; + times = t; + } + } + + // 桶结构 + public static class NodeList { + public Node head; // 桶的头节点 + public Node tail; // 桶的尾节点 + public NodeList last; // 桶之间是双向链表所以有前一个桶 + public NodeList next; // 桶之间是双向链表所以有后一个桶 + + public NodeList(Node node) { + head = node; + tail = node; + } + + // 把一个新的节点加入这个桶,新的节点都放在顶端变成新的头部 + public void addNodeFromHead(Node newHead) { + newHead.down = head; + head.up = newHead; + head = newHead; + } + + // 判断这个桶是不是空的 + public boolean isEmpty() { + return head == null; + } + + // 删除node节点并保证node的上下环境重新连接 + public void deleteNode(Node node) { + if (head == tail) { + head = null; + tail = null; + } else { + if (node == head) { + head = node.down; + head.up = null; + } else if (node == tail) { + tail = node.up; + tail.down = null; + } else { + node.up.down = node.down; + node.down.up = node.up; + } + } + node.up = null; + node.down = null; + } + } + + // removeNodeList:刚刚减少了一个节点的桶 + // 这个函数的功能是,判断刚刚减少了一个节点的桶是不是已经空了。 + // 1)如果不空,什么也不做 + // + // 2)如果空了,removeNodeList还是整个缓存结构最左的桶(headList)。 + // 删掉这个桶的同时也要让最左的桶变成removeNodeList的下一个。 + // + // 3)如果空了,removeNodeList不是整个缓存结构最左的桶(headList)。 + // 把这个桶删除,并保证上一个的桶和下一个桶之间还是双向链表的连接方式 + // + // 函数的返回值表示刚刚减少了一个节点的桶是不是已经空了,空了返回true;不空返回false + private boolean modifyHeadList(NodeList removeNodeList) { + if (removeNodeList.isEmpty()) { + if (headList == removeNodeList) { + headList = removeNodeList.next; + if (headList != null) { + headList.last = null; + } + } else { + removeNodeList.last.next = removeNodeList.next; + if (removeNodeList.next != null) { + removeNodeList.next.last = removeNodeList.last; + } + } + return true; + } + return false; + } + + // 函数的功能 + // node这个节点的次数+1了,这个节点原来在oldNodeList里。 + // 把node从oldNodeList删掉,然后放到次数+1的桶中 + // 整个过程既要保证桶之间仍然是双向链表,也要保证节点之间仍然是双向链表 + private void move(Node node, NodeList oldNodeList) { + oldNodeList.deleteNode(node); + // preList表示次数+1的桶的前一个桶是谁 + // 如果oldNodeList删掉node之后还有节点,oldNodeList就是次数+1的桶的前一个桶 + // 如果oldNodeList删掉node之后空了,oldNodeList是需要删除的,所以次数+1的桶的前一个桶,是oldNodeList的前一个 + NodeList preList = modifyHeadList(oldNodeList) ? oldNodeList.last : oldNodeList; + // nextList表示次数+1的桶的后一个桶是谁 + NodeList nextList = oldNodeList.next; + if (nextList == null) { + NodeList newList = new NodeList(node); + if (preList != null) { + preList.next = newList; + } + newList.last = preList; + if (headList == null) { + headList = newList; + } + heads.put(node, newList); + } else { + if (nextList.head.times.equals(node.times)) { + nextList.addNodeFromHead(node); + heads.put(node, nextList); + } else { + NodeList newList = new NodeList(node); + if (preList != null) { + preList.next = newList; + } + newList.last = preList; + newList.next = nextList; + nextList.last = newList; + if (headList == nextList) { + headList = newList; + } + heads.put(node, newList); + } + } + } + + public void put(int key, int value) { + if (capacity == 0) { + return; + } + if (records.containsKey(key)) { + Node node = records.get(key); + node.value = value; + node.times++; + NodeList curNodeList = heads.get(node); + move(node, curNodeList); + } else { + if (size == capacity) { + Node node = headList.tail; + headList.deleteNode(node); + modifyHeadList(headList); + records.remove(node.key); + heads.remove(node); + size--; + } + Node node = new Node(key, value, 1); + if (headList == null) { + headList = new NodeList(node); + } else { + if (headList.head.times.equals(node.times)) { + headList.addNodeFromHead(node); + } else { + NodeList newList = new NodeList(node); + newList.next = headList; + headList.last = newList; + headList = newList; + } + } + records.put(key, node); + heads.put(node, headList); + size++; + } + } + + public int get(int key) { + if (!records.containsKey(key)) { + return -1; + } + Node node = records.get(key); + node.times++; + NodeList curNodeList = heads.get(node); + move(node, curNodeList); + return node.value; + } + +} \ No newline at end of file diff --git a/大厂刷题班/class19/Code03_OneNumber.java b/大厂刷题班/class19/Code03_OneNumber.java new file mode 100644 index 0000000..36f2cd6 --- /dev/null +++ b/大厂刷题班/class19/Code03_OneNumber.java @@ -0,0 +1,83 @@ +package class19; + +public class Code03_OneNumber { + + public static int solution1(int num) { + if (num < 1) { + return 0; + } + int count = 0; + for (int i = 1; i != num + 1; i++) { + count += get1Nums(i); + } + return count; + } + + public static int get1Nums(int num) { + int res = 0; + while (num != 0) { + if (num % 10 == 1) { + res++; + } + num /= 10; + } + return res; + } + + + // 1 ~ num 这个范围上,画了几道1 + public static int solution2(int num) { + if (num < 1) { + return 0; + } + // num -> 13625 + // len = 5位数 + int len = getLenOfNum(num); + if (len == 1) { + return 1; + } + // num 13625 + // tmp1 10000 + // + // num 7872328738273 + // tmp1 1000000000000 + int tmp1 = powerBaseOf10(len - 1); + // num最高位 num / tmp1 + int first = num / tmp1; + // 最高1 N % tmp1 + 1 + // 最高位first tmp1 + int firstOneNum = first == 1 ? num % tmp1 + 1 : tmp1; + // 除去最高位之外,剩下1的数量 + // 最高位1 10(k-2次方) * (k-1) * 1 + // 最高位first 10(k-2次方) * (k-1) * first + int otherOneNum = first * (len - 1) * (tmp1 / 10); + return firstOneNum + otherOneNum + solution2(num % tmp1); + } + + public static int getLenOfNum(int num) { + int len = 0; + while (num != 0) { + len++; + num /= 10; + } + return len; + } + + public static int powerBaseOf10(int base) { + return (int) Math.pow(10, base); + } + + public static void main(String[] args) { + int num = 50000000; + long start1 = System.currentTimeMillis(); + System.out.println(solution1(num)); + long end1 = System.currentTimeMillis(); + System.out.println("cost time: " + (end1 - start1) + " ms"); + + long start2 = System.currentTimeMillis(); + System.out.println(solution2(num)); + long end2 = System.currentTimeMillis(); + System.out.println("cost time: " + (end2 - start2) + " ms"); + + } +} diff --git a/大厂刷题班/class19/Code04_SmallestRangeCoveringElementsfromKLists.java b/大厂刷题班/class19/Code04_SmallestRangeCoveringElementsfromKLists.java new file mode 100644 index 0000000..5a49728 --- /dev/null +++ b/大厂刷题班/class19/Code04_SmallestRangeCoveringElementsfromKLists.java @@ -0,0 +1,58 @@ +package class19; + +import java.util.Comparator; +import java.util.List; +import java.util.TreeSet; + +// 本题测试链接 : https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/ +public class Code04_SmallestRangeCoveringElementsfromKLists { + + public static class Node { + public int value; + public int arrid; + public int index; + + public Node(int v, int ai, int i) { + value = v; + arrid = ai; + index = i; + } + } + + public static class NodeComparator implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.value != o2.value ? o1.value - o2.value : o1.arrid - o2.arrid; + } + + } + + public static int[] smallestRange(List> nums) { + int N = nums.size(); + TreeSet orderSet = new TreeSet<>(new NodeComparator()); + for (int i = 0; i < N; i++) { + orderSet.add(new Node(nums.get(i).get(0), i, 0)); + } + boolean set = false; + int a = 0; + int b = 0; + while (orderSet.size() == N) { + Node min = orderSet.first(); + Node max = orderSet.last(); + if (!set || (max.value - min.value < b - a)) { + set = true; + a = min.value; + b = max.value; + } + min = orderSet.pollFirst(); + int arrid = min.arrid; + int index = min.index + 1; + if (index != nums.get(arrid).size()) { + orderSet.add(new Node(nums.get(arrid).get(index), arrid, index)); + } + } + return new int[] { a, b }; + } + +} diff --git a/大厂刷题班/class19/Code05_CardsProblem.java b/大厂刷题班/class19/Code05_CardsProblem.java new file mode 100644 index 0000000..5b9092c --- /dev/null +++ b/大厂刷题班/class19/Code05_CardsProblem.java @@ -0,0 +1,166 @@ +package class19; + +import java.util.LinkedList; + +/* + * 一张扑克有3个属性,每种属性有3种值(A、B、C) + * 比如"AAA",第一个属性值A,第二个属性值A,第三个属性值A + * 比如"BCA",第一个属性值B,第二个属性值C,第三个属性值A + * 给定一个字符串类型的数组cards[],每一个字符串代表一张扑克 + * 从中挑选三张扑克,一个属性达标的条件是:这个属性在三张扑克中全一样,或全不一样 + * 挑选的三张扑克达标的要求是:每种属性都满足上面的条件 + * 比如:"ABC"、"CBC"、"BBC" + * 第一张第一个属性为"A"、第二张第一个属性为"C"、第三张第一个属性为"B",全不一样 + * 第一张第二个属性为"B"、第二张第二个属性为"B"、第三张第二个属性为"B",全一样 + * 第一张第三个属性为"C"、第二张第三个属性为"C"、第三张第三个属性为"C",全一样 + * 每种属性都满足在三张扑克中全一样,或全不一样,所以这三张扑克达标 + * 返回在cards[]中任意挑选三张扑克,达标的方法数 + * + * */ +public class Code05_CardsProblem { + + public static int ways1(String[] cards) { + LinkedList picks = new LinkedList<>(); + return process1(cards, 0, picks); + } + + public static int process1(String[] cards, int index, LinkedList picks) { + if (picks.size() == 3) { + return getWays1(picks); + } + if (index == cards.length) { + return 0; + } + int ways = process1(cards, index + 1, picks); + picks.addLast(cards[index]); + ways += process1(cards, index + 1, picks); + picks.pollLast(); + return ways; + } + + public static int getWays1(LinkedList picks) { + char[] s1 = picks.get(0).toCharArray(); + char[] s2 = picks.get(1).toCharArray(); + char[] s3 = picks.get(2).toCharArray(); + for (int i = 0; i < 3; i++) { + if ((s1[i] != s2[i] && s1[i] != s3[i] && s2[i] != s3[i]) || (s1[i] == s2[i] && s1[i] == s3[i])) { + continue; + } + return 0; + } + return 1; + } + + public static int ways2(String[] cards) { + int[] counts = new int[27]; + for (String s : cards) { + char[] str = s.toCharArray(); + counts[(str[0] - 'A') * 9 + (str[1] - 'A') * 3 + (str[2] - 'A') * 1]++; + } + int ways = 0; + for (int status = 0; status < 27; status++) { + int n = counts[status]; + if (n > 2) { + ways += n == 3 ? 1 : (n * (n - 1) * (n - 2) / 6); + } + } + LinkedList path = new LinkedList<>(); + for (int i = 0; i < 27; i++) { + if (counts[i] != 0) { + path.addLast(i); + ways += process2(counts, i, path); + path.pollLast(); + } + } + return ways; + } + + // 之前的牌面,拿了一些 ABC BBB ... + // pre = BBB + // ABC ... + // pre = ABC + // ABC BBB CAB + // pre = CAB + // 牌面一定要依次变大,所有形成的有效牌面,把方法数返回 + public static int process2(int[] counts, int pre, LinkedList path) { + if (path.size() == 3) { + return getWays2(counts, path); + } + int ways = 0; + for (int next = pre + 1; next < 27; next++) { + if (counts[next] != 0) { + path.addLast(next); + ways += process2(counts, next, path); + path.pollLast(); + } + } + return ways; + } + + public static int getWays2(int[] counts, LinkedList path) { + int v1 = path.get(0); + int v2 = path.get(1); + int v3 = path.get(2); + for (int i = 9; i > 0; i /= 3) { + int cur1 = v1 / i; + int cur2 = v2 / i; + int cur3 = v3 / i; + v1 %= i; + v2 %= i; + v3 %= i; + if ((cur1 != cur2 && cur1 != cur3 && cur2 != cur3) || (cur1 == cur2 && cur1 == cur3)) { + continue; + } + return 0; + } + v1 = path.get(0); + v2 = path.get(1); + v3 = path.get(2); + return counts[v1] * counts[v2] * counts[v3]; + } + + // for test + public static String[] generateCards(int size) { + int n = (int) (Math.random() * size) + 3; + String[] ans = new String[n]; + for (int i = 0; i < n; i++) { + char cha0 = (char) ((int) (Math.random() * 3) + 'A'); + char cha1 = (char) ((int) (Math.random() * 3) + 'A'); + char cha2 = (char) ((int) (Math.random() * 3) + 'A'); + ans[i] = String.valueOf(cha0) + String.valueOf(cha1) + String.valueOf(cha2); + } + return ans; + } + + // for test + public static void main(String[] args) { + int size = 20; + int testTime = 100000; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + String[] arr = generateCards(size); + int ans1 = ways1(arr); + int ans2 = ways2(arr); + if (ans1 != ans2) { + for (String str : arr) { + System.out.println(str); + } + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("test finish"); + + long start = 0; + long end = 0; + String[] arr = generateCards(10000000); + System.out.println("arr size : " + arr.length + " runtime test begin"); + start = System.currentTimeMillis(); + ways2(arr); + end = System.currentTimeMillis(); + System.out.println("run time : " + (end - start) + " ms"); + System.out.println("runtime test end"); + } + +} diff --git a/大厂刷题班/class20/Code01_PreAndInArrayToPosArray.java b/大厂刷题班/class20/Code01_PreAndInArrayToPosArray.java new file mode 100644 index 0000000..abc2bcc --- /dev/null +++ b/大厂刷题班/class20/Code01_PreAndInArrayToPosArray.java @@ -0,0 +1,233 @@ +package class20; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +public class Code01_PreAndInArrayToPosArray { + + public static int[] zuo(int[] pre, int[] in) { + if (pre == null || in == null || pre.length != in.length) { + return null; + } + int N = pre.length; + HashMap inMap = new HashMap<>(); + for (int i = 0; i < N; i++) { + inMap.put(in[i], i); + } + int[] pos = new int[N]; + func(pre, 0, N - 1, in, 0, N - 1, pos, 0, N - 1, inMap); + return pos; + } + + public static void func(int[] pre, int L1, int R1, int[] in, int L2, int R2, int[] pos, int L3, int R3, + HashMap inMap) { + if (L1 > R1) { + return; + } + if (L1 == R1) { + pos[L3] = pre[L1]; + } else { + pos[R3] = pre[L1]; + int index = inMap.get(pre[L1]); + func(pre, L1 + 1, L1 + index - L2, in, L2, index - 1, pos, L3, L3 + index - L2 - 1, inMap); + func(pre, L1 + index - L2 + 1, R1, in, index + 1, R2, pos, L3 + index - L2, R3 - 1, inMap); + } + } + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + public static int[] preInToPos1(int[] pre, int[] in) { + if (pre == null || in == null || pre.length != in.length) { + return null; + } + int N = pre.length; + int[] pos = new int[N]; + process1(pre, 0, N - 1, in, 0, N - 1, pos, 0, N - 1); + return pos; + } + + // L1...R1 L2...R2 L3...R3 + public static void process1(int[] pre, int L1, int R1, int[] in, int L2, int R2, int[] pos, int L3, int R3) { + if (L1 > R1) { + return; + } + if (L1 == R1) { + pos[L3] = pre[L1]; + return; + } + pos[R3] = pre[L1]; + int mid = L2; + for (; mid <= R2; mid++) { + if (in[mid] == pre[L1]) { + break; + } + } + int leftSize = mid - L2; + process1(pre, L1 + 1, L1 + leftSize, in, L2, mid - 1, pos, L3, L3 + leftSize - 1); + process1(pre, L1 + leftSize + 1, R1, in, mid + 1, R2, pos, L3 + leftSize, R3 - 1); + } + + public static int[] preInToPos2(int[] pre, int[] in) { + if (pre == null || in == null || pre.length != in.length) { + return null; + } + int N = pre.length; + HashMap inMap = new HashMap<>(); + for (int i = 0; i < N; i++) { + inMap.put(in[i], i); + } + int[] pos = new int[N]; + process2(pre, 0, N - 1, in, 0, N - 1, pos, 0, N - 1, inMap); + return pos; + } + + public static void process2(int[] pre, int L1, int R1, int[] in, int L2, int R2, int[] pos, int L3, int R3, + HashMap inMap) { + if (L1 > R1) { + return; + } + if (L1 == R1) { + pos[L3] = pre[L1]; + return; + } + pos[R3] = pre[L1]; + int mid = inMap.get(pre[L1]); + int leftSize = mid - L2; + process2(pre, L1 + 1, L1 + leftSize, in, L2, mid - 1, pos, L3, L3 + leftSize - 1, inMap); + process2(pre, L1 + leftSize + 1, R1, in, mid + 1, R2, pos, L3 + leftSize, R3 - 1, inMap); + } + + // for test + public static int[] getPreArray(Node head) { + ArrayList arr = new ArrayList<>(); + fillPreArray(head, arr); + int[] ans = new int[arr.size()]; + for (int i = 0; i < ans.length; i++) { + ans[i] = arr.get(i); + } + return ans; + } + + // for test + public static void fillPreArray(Node head, ArrayList arr) { + if (head == null) { + return; + } + arr.add(head.value); + fillPreArray(head.left, arr); + fillPreArray(head.right, arr); + } + + // for test + public static int[] getInArray(Node head) { + ArrayList arr = new ArrayList<>(); + fillInArray(head, arr); + int[] ans = new int[arr.size()]; + for (int i = 0; i < ans.length; i++) { + ans[i] = arr.get(i); + } + return ans; + } + + // for test + public static void fillInArray(Node head, ArrayList arr) { + if (head == null) { + return; + } + fillInArray(head.left, arr); + arr.add(head.value); + fillInArray(head.right, arr); + } + + // for test + public static int[] getPosArray(Node head) { + ArrayList arr = new ArrayList<>(); + fillPostArray(head, arr); + int[] ans = new int[arr.size()]; + for (int i = 0; i < ans.length; i++) { + ans[i] = arr.get(i); + } + return ans; + } + + // for test + public static void fillPostArray(Node head, ArrayList arr) { + if (head == null) { + return; + } + fillPostArray(head.left, arr); + fillPostArray(head.right, arr); + arr.add(head.value); + } + + // for test + public static Node generateRandomTree(int value, int maxLevel) { + HashSet hasValue = new HashSet(); + return createTree(value, 1, maxLevel, hasValue); + } + + // for test + public static Node createTree(int value, int level, int maxLevel, HashSet hasValue) { + if (level > maxLevel) { + return null; + } + int cur = 0; + do { + cur = (int) (Math.random() * value) + 1; + } while (hasValue.contains(cur)); + hasValue.add(cur); + Node head = new Node(cur); + head.left = createTree(value, level + 1, maxLevel, hasValue); + head.right = createTree(value, level + 1, maxLevel, hasValue); + return head; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + System.out.println("test begin"); + int maxLevel = 5; + int value = 1000; + int testTime = 100000; + for (int i = 0; i < testTime; i++) { + Node head = generateRandomTree(value, maxLevel); + int[] pre = getPreArray(head); + int[] in = getInArray(head); + int[] pos = getPosArray(head); + int[] ans1 = preInToPos1(pre, in); + int[] ans2 = preInToPos2(pre, in); + int[] classAns = zuo(pre, in); + if (!isEqual(pos, ans1) || !isEqual(ans1, ans2) || !isEqual(pos, classAns)) { + System.out.println("Oops!"); + } + } + System.out.println("test end"); + + } +} diff --git a/大厂刷题班/class20/Code02_LargestComponentSizebyCommonFactor.java b/大厂刷题班/class20/Code02_LargestComponentSizebyCommonFactor.java new file mode 100644 index 0000000..bb746cb --- /dev/null +++ b/大厂刷题班/class20/Code02_LargestComponentSizebyCommonFactor.java @@ -0,0 +1,109 @@ +package class20; + +import java.util.HashMap; + +// 本题为leetcode原题 +// 测试链接:https://leetcode.com/problems/largest-component-size-by-common-factor/ +// 方法1会超时,但是方法2直接通过 +public class Code02_LargestComponentSizebyCommonFactor { + + public static int largestComponentSize1(int[] arr) { + int N = arr.length; + UnionFind set = new UnionFind(N); + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + if (gcd(arr[i], arr[j]) != 1) { + set.union(i, j); + } + } + } + return set.maxSize(); + } + + public static int largestComponentSize2(int[] arr) { + int N = arr.length; + // arr中,N个位置,在并查集初始时,每个位置自己是一个集合 + UnionFind unionFind = new UnionFind(N); + // key 某个因子 value 哪个位置拥有这个因子 + HashMap fatorsMap = new HashMap<>(); + for (int i = 0; i < N; i++) { + int num = arr[i]; + // 求出根号N, -> limit + int limit = (int) Math.sqrt(num); + for (int j = 1; j <= limit; j++) { + if (num % j == 0) { + if (j != 1) { + if (!fatorsMap.containsKey(j)) { + fatorsMap.put(j, i); + } else { + unionFind.union(fatorsMap.get(j), i); + } + } + int other = num / j; + if (other != 1) { + if (!fatorsMap.containsKey(other)) { + fatorsMap.put(other, i); + } else { + unionFind.union(fatorsMap.get(other), i); + } + } + } + } + } + return unionFind.maxSize(); + } + + // O(1) + // m,n 要是正数,不能有任何一个等于0 + public static int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } + + public static class UnionFind { + private int[] parents; + private int[] sizes; + private int[] help; + + public UnionFind(int N) { + parents = new int[N]; + sizes = new int[N]; + help = new int[N]; + for (int i = 0; i < N; i++) { + parents[i] = i; + sizes[i] = 1; + } + } + + public int maxSize() { + int ans = 0; + for (int size : sizes) { + ans = Math.max(ans, size); + } + return ans; + } + + private int find(int i) { + int hi = 0; + while (i != parents[i]) { + help[hi++] = i; + i = parents[i]; + } + for (hi--; hi >= 0; hi--) { + parents[help[hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int f1 = find(i); + int f2 = find(j); + if (f1 != f2) { + int big = sizes[f1] >= sizes[f2] ? f1 : f2; + int small = big == f1 ? f2 : f1; + parents[small] = big; + sizes[big] = sizes[f1] + sizes[f2]; + } + } + } + +} diff --git a/大厂刷题班/class20/Code03_ShuffleProblem.java b/大厂刷题班/class20/Code03_ShuffleProblem.java new file mode 100644 index 0000000..fabd281 --- /dev/null +++ b/大厂刷题班/class20/Code03_ShuffleProblem.java @@ -0,0 +1,154 @@ +package class20; + +import java.util.Arrays; + +public class Code03_ShuffleProblem { + + // 数组的长度为len,调整前的位置是i,返回调整之后的位置 + // 下标不从0开始,从1开始 + public static int modifyIndex1(int i, int len) { + if (i <= len / 2) { + return 2 * i; + } else { + return 2 * (i - (len / 2)) - 1; + } + } + + // 数组的长度为len,调整前的位置是i,返回调整之后的位置 + // 下标不从0开始,从1开始 + public static int modifyIndex2(int i, int len) { + return (2 * i) % (len + 1); + } + + // 主函数 + // 数组必须不为空,且长度为偶数 + public static void shuffle(int[] arr) { + if (arr != null && arr.length != 0 && (arr.length & 1) == 0) { + shuffle(arr, 0, arr.length - 1); + } + } + + // 在arr[L..R]上做完美洗牌的调整(arr[L..R]范围上一定要是偶数个数字) + public static void shuffle(int[] arr, int L, int R) { + while (R - L + 1 > 0) { // 切成一块一块的解决,每一块的长度满足(3^k)-1 + int len = R - L + 1; + int base = 3; + int k = 1; + // 计算小于等于len并且是离len最近的,满足(3^k)-1的数 + // 也就是找到最大的k,满足3^k <= len+1 + while (base <= (len + 1) / 3) { // base > (N+1)/3 + base *= 3; + k++; + } + // 3^k -1 + // 当前要解决长度为base-1的块,一半就是再除2 + int half = (base - 1) / 2; + // [L..R]的中点位置 + int mid = (L + R) / 2; + // 要旋转的左部分为[L+half...mid], 右部分为arr[mid+1..mid+half] + // 注意在这里,arr下标是从0开始的 + rotate(arr, L + half, mid, mid + half); + // 旋转完成后,从L开始算起,长度为base-1的部分进行下标连续推 + cycles(arr, L, base - 1, k); + // 解决了前base-1的部分,剩下的部分继续处理 + L = L + base - 1; // L -> [] [+1...R] + } + } + + // 从start位置开始,往右len的长度这一段,做下标连续推 + // 出发位置依次为1,3,9... + public static void cycles(int[] arr, int start, int len, int k) { + // 找到每一个出发位置trigger,一共k个 + // 每一个trigger都进行下标连续推 + // 出发位置是从1开始算的,而数组下标是从0开始算的。 + for (int i = 0, trigger = 1; i < k; i++, trigger *= 3) { + int preValue = arr[trigger + start - 1]; + int cur = modifyIndex2(trigger, len); + while (cur != trigger) { + int tmp = arr[cur + start - 1]; + arr[cur + start - 1] = preValue; + preValue = tmp; + cur = modifyIndex2(cur, len); + } + arr[cur + start - 1] = preValue; + } + } + + // [L..M]为左部分,[M+1..R]为右部分,左右两部分互换 + public static void rotate(int[] arr, int L, int M, int R) { + reverse(arr, L, M); + reverse(arr, M + 1, R); + reverse(arr, L, R); + } + + // [L..R]做逆序调整 + public static void reverse(int[] arr, int L, int R) { + while (L < R) { + int tmp = arr[L]; + arr[L++] = arr[R]; + arr[R--] = tmp; + } + } + + public static void wiggleSort(int[] arr) { + if (arr == null || arr.length == 0) { + return; + } + // 假设这个排序是额外空间复杂度O(1)的,当然系统提供的排序并不是,你可以自己实现一个堆排序 + Arrays.sort(arr); + if ((arr.length & 1) == 1) { + shuffle(arr, 1, arr.length - 1); + } else { + shuffle(arr, 0, arr.length - 1); + for (int i = 0; i < arr.length; i += 2) { + int tmp = arr[i]; + arr[i] = arr[i + 1]; + arr[i + 1] = tmp; + } + } + } + + // for test + public static boolean isValidWiggle(int[] arr) { + for (int i = 1; i < arr.length; i++) { + if ((i & 1) == 1 && arr[i] < arr[i - 1]) { + return false; + } + if ((i & 1) == 0 && arr[i] > arr[i - 1]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static int[] generateArray() { + int len = (int) (Math.random() * 10) * 2; + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * 100); + } + return arr; + } + + public static void main(String[] args) { + for (int i = 0; i < 5000000; i++) { + int[] arr = generateArray(); + wiggleSort(arr); + if (!isValidWiggle(arr)) { + System.out.println("ooops!"); + printArray(arr); + break; + } + } + } + +} diff --git a/大厂刷题班/class20/Code04_PalindromeWays.java b/大厂刷题班/class20/Code04_PalindromeWays.java new file mode 100644 index 0000000..0608e0c --- /dev/null +++ b/大厂刷题班/class20/Code04_PalindromeWays.java @@ -0,0 +1,91 @@ +package class20; + +public class Code04_PalindromeWays { + + public static int ways1(String str) { + if (str == null || str.length() == 0) { + return 0; + } + char[] s = str.toCharArray(); + char[] path = new char[s.length]; + return process(str.toCharArray(), 0, path, 0); + } + + public static int process(char[] s, int si, char[] path, int pi) { + if (si == s.length) { + return isP(path, pi) ? 1 : 0; + } + int ans = process(s, si + 1, path, pi); + path[pi] = s[si]; + ans += process(s, si + 1, path, pi + 1); + return ans; + } + + public static boolean isP(char[] path, int pi) { + if (pi == 0) { + return false; + } + int L = 0; + int R = pi - 1; + while (L < R) { + if (path[L++] != path[R--]) { + return false; + } + } + return true; + } + + public static int ways2(String str) { + if (str == null || str.length() == 0) { + return 0; + } + char[] s = str.toCharArray(); + int n = s.length; + int[][] dp = new int[n][n]; + for (int i = 0; i < n; i++) { + dp[i][i] = 1; + } + for (int i = 0; i < n - 1; i++) { + dp[i][i + 1] = s[i] == s[i + 1] ? 3 : 2; + } + for (int L = n - 3; L >= 0; L--) { + for (int R = L + 2; R < n; R++) { + dp[L][R] = dp[L + 1][R] + dp[L][R - 1] - dp[L + 1][R - 1]; + if (s[L] == s[R]) { + dp[L][R] += dp[L + 1][R - 1] + 1; + } + } + } + return dp[0][n - 1]; + } + + public static String randomString(int len, int types) { + char[] str = new char[len]; + for (int i = 0; i < str.length; i++) { + str[i] = (char) ('a' + (int) (Math.random() * types)); + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int N = 10; + int types = 5; + int testTimes = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int len = (int) (Math.random() * N); + String str = randomString(len, types); + int ans1 = ways1(str); + int ans2 = ways2(str); + if (ans1 != ans2) { + System.out.println(str); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class21/TreeChainPartition.java b/大厂刷题班/class21/TreeChainPartition.java new file mode 100644 index 0000000..94ff19c --- /dev/null +++ b/大厂刷题班/class21/TreeChainPartition.java @@ -0,0 +1,416 @@ +package class21; + +import java.util.HashMap; + +public class TreeChainPartition { + + public static class TreeChain { + // 时间戳 0 1 2 3 4 + private int tim; + // 节点个数是n,节点编号是1~n + private int n; + // 谁是头 + private int h; + // 朴素树结构 + private int[][] tree; + // 权重数组 原始的0节点权重是6 -> val[1] = 6 + private int[] val; + // father数组一个平移,因为标号要+1 + private int[] fa; + // 深度数组! + private int[] dep; + // son[i] = 0 i这个节点,没有儿子 + // son[i] != 0 j i这个节点,重儿子是j + private int[] son; + // siz[i] i这个节点为头的子树,有多少个节点 + private int[] siz; + // top[i] = j i这个节点,所在的重链,头是j + private int[] top; + // dfn[i] = j i这个节点,在dfs序中是第j个 + private int[] dfn; + // 如果原来的节点a,权重是10 + // 如果a节点在dfs序中是第5个节点, tnw[5] = 10 + private int[] tnw; + // 线段树,在tnw上,玩连续的区间查询或者更新 + private SegmentTree seg; + + public TreeChain(int[] father, int[] values) { + // 原始的树 tree,弄好了,可以从i这个点,找到下级的直接孩子 + // 上面的一大堆结构,准备好了空间,values -> val + // 找到头部点 + initTree(father, values); + // fa; + // dep; + // son; + // siz; + dfs1(h, 0); + // top; + // dfn; + // tnw; + dfs2(h, h); + seg = new SegmentTree(tnw); + seg.build(1, n, 1); + } + + private void initTree(int[] father, int[] values) { + tim = 0; + n = father.length + 1; + tree = new int[n][]; + val = new int[n]; + fa = new int[n]; + dep = new int[n]; + son = new int[n]; + siz = new int[n]; + top = new int[n]; + dfn = new int[n]; + tnw = new int[n--]; + int[] cnum = new int[n]; + for (int i = 0; i < n; i++) { + val[i + 1] = values[i]; + } + for (int i = 0; i < n; i++) { + if (father[i] == i) { + h = i + 1; + } else { + cnum[father[i]]++; + } + } + tree[0] = new int[0]; + for (int i = 0; i < n; i++) { + tree[i + 1] = new int[cnum[i]]; + } + for (int i = 0; i < n; i++) { + if (i + 1 != h) { + tree[father[i] + 1][--cnum[father[i]]] = i + 1; + } + } + } + + // u 当前节点 + // f u的父节点 + private void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + int maxSize = -1; + for (int v : tree[u]) { // 遍历u节点,所有的直接孩子 + dfs1(v, u); + siz[u] += siz[v]; + if (siz[v] > maxSize) { + maxSize = siz[v]; + son[u] = v; + } + } + } + + // u当前节点 + // t是u所在重链的头部 + private void dfs2(int u, int t) { + dfn[u] = ++tim; + top[u] = t; + tnw[tim] = val[u]; + if (son[u] != 0) { // 如果u有儿子 siz[u] > 1 + dfs2(son[u], t); + for (int v : tree[u]) { + if (v != son[u]) { + dfs2(v, v); + } + } + } + } + + // head为头的子树上,所有节点值+value + // 因为节点经过平移,所以head(原始节点) -> head(平移节点) + public void addSubtree(int head, int value) { + // 原始点编号 -> 平移编号 + head++; + // 平移编号 -> dfs编号 dfn[head] + seg.add(dfn[head], dfn[head] + siz[head] - 1, value, 1, n, 1); + } + + public int querySubtree(int head) { + head++; + return seg.query(dfn[head], dfn[head] + siz[head] - 1, 1, n, 1); + } + + public void addChain(int a, int b, int v) { + a++; + b++; + while (top[a] != top[b]) { + if (dep[top[a]] > dep[top[b]]) { + seg.add(dfn[top[a]], dfn[a], v, 1, n, 1); + a = fa[top[a]]; + } else { + seg.add(dfn[top[b]], dfn[b], v, 1, n, 1); + b = fa[top[b]]; + } + } + if (dep[a] > dep[b]) { + seg.add(dfn[b], dfn[a], v, 1, n, 1); + } else { + seg.add(dfn[a], dfn[b], v, 1, n, 1); + } + } + + public int queryChain(int a, int b) { + a++; + b++; + int ans = 0; + while (top[a] != top[b]) { + if (dep[top[a]] > dep[top[b]]) { + ans += seg.query(dfn[top[a]], dfn[a], 1, n, 1); + a = fa[top[a]]; + } else { + ans += seg.query(dfn[top[b]], dfn[b], 1, n, 1); + b = fa[top[b]]; + } + } + if (dep[a] > dep[b]) { + ans += seg.query(dfn[b], dfn[a], 1, n, 1); + } else { + ans += seg.query(dfn[a], dfn[b], 1, n, 1); + } + return ans; + } + } + + public static class SegmentTree { + private int MAXN; + private int[] arr; + private int[] sum; + private int[] lazy; + + public SegmentTree(int[] origin) { + MAXN = origin.length; + arr = origin; + sum = new int[MAXN << 2]; + lazy = new int[MAXN << 2]; + } + + private void pushUp(int rt) { + sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; + } + + private void pushDown(int rt, int ln, int rn) { + if (lazy[rt] != 0) { + lazy[rt << 1] += lazy[rt]; + sum[rt << 1] += lazy[rt] * ln; + lazy[rt << 1 | 1] += lazy[rt]; + sum[rt << 1 | 1] += lazy[rt] * rn; + lazy[rt] = 0; + } + } + + public void build(int l, int r, int rt) { + if (l == r) { + sum[rt] = arr[l]; + return; + } + int mid = (l + r) >> 1; + build(l, mid, rt << 1); + build(mid + 1, r, rt << 1 | 1); + pushUp(rt); + } + + public void add(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + sum[rt] += C * (r - l + 1); + lazy[rt] += C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + add(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + add(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return sum[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans += query(L, R, l, mid, rt << 1); + } + if (R > mid) { + ans += query(L, R, mid + 1, r, rt << 1 | 1); + } + return ans; + } + + } + + // 为了测试,这个结构是暴力但正确的方法 + public static class Right { + private int n; + private int[][] tree; + private int[] fa; + private int[] val; + private HashMap path; + + public Right(int[] father, int[] value) { + n = father.length; + tree = new int[n][]; + fa = new int[n]; + val = new int[n]; + for (int i = 0; i < n; i++) { + fa[i] = father[i]; + val[i] = value[i]; + } + int[] help = new int[n]; + int h = 0; + for (int i = 0; i < n; i++) { + if (father[i] == i) { + h = i; + } else { + help[father[i]]++; + } + } + for (int i = 0; i < n; i++) { + tree[i] = new int[help[i]]; + } + for (int i = 0; i < n; i++) { + if (i != h) { + tree[father[i]][--help[father[i]]] = i; + } + } + path = new HashMap<>(); + } + + public void addSubtree(int head, int value) { + val[head] += value; + for (int next : tree[head]) { + addSubtree(next, value); + } + } + + public int querySubtree(int head) { + int ans = val[head]; + for (int next : tree[head]) { + ans += querySubtree(next); + } + return ans; + } + + public void addChain(int a, int b, int v) { + path.clear(); + path.put(a, null); + while (a != fa[a]) { + path.put(fa[a], a); + a = fa[a]; + } + while (!path.containsKey(b)) { + val[b] += v; + b = fa[b]; + } + val[b] += v; + while (path.get(b) != null) { + b = path.get(b); + val[b] += v; + } + } + + public int queryChain(int a, int b) { + path.clear(); + path.put(a, null); + while (a != fa[a]) { + path.put(fa[a], a); + a = fa[a]; + } + int ans = 0; + while (!path.containsKey(b)) { + ans += val[b]; + b = fa[b]; + } + ans += val[b]; + while (path.get(b) != null) { + b = path.get(b); + ans += val[b]; + } + return ans; + } + + } + + // 为了测试 + // 随机生成N个节点树,可能是多叉树,并且一定不是森林 + // 输入参数N要大于0 + public static int[] generateFatherArray(int N) { + int[] order = new int[N]; + for (int i = 0; i < N; i++) { + order[i] = i; + } + for (int i = N - 1; i >= 0; i--) { + swap(order, i, (int) (Math.random() * (i + 1))); + } + int[] ans = new int[N]; + ans[order[0]] = order[0]; + for (int i = 1; i < N; i++) { + ans[order[i]] = order[(int) (Math.random() * i)]; + } + return ans; + } + + // 为了测试 + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 为了测试 + public static int[] generateValueArray(int N, int V) { + int[] ans = new int[N]; + for (int i = 0; i < N; i++) { + ans[i] = (int) (Math.random() * V) + 1; + } + return ans; + } + + // 对数器 + public static void main(String[] args) { + int N = 50000; + int V = 100000; + int[] father = generateFatherArray(N); + int[] values = generateValueArray(N, V); + TreeChain tc = new TreeChain(father, values); + Right right = new Right(father, values); + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + double decision = Math.random(); + if (decision < 0.25) { + int head = (int) (Math.random() * N); + int value = (int) (Math.random() * V); + tc.addSubtree(head, value); + right.addSubtree(head, value); + } else if (decision < 0.5) { + int head = (int) (Math.random() * N); + if (tc.querySubtree(head) != right.querySubtree(head)) { + System.out.println("出错了!"); + } + } else if (decision < 0.75) { + int a = (int) (Math.random() * N); + int b = (int) (Math.random() * N); + int value = (int) (Math.random() * V); + tc.addChain(a, b, value); + right.addChain(a, b, value); + } else { + int a = (int) (Math.random() * N); + int b = (int) (Math.random() * N); + if (tc.queryChain(a, b) != right.queryChain(a, b)) { + System.out.println("出错了!"); + } + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class22/Code01_MaximumSumof3NonOverlappingSubarrays.java b/大厂刷题班/class22/Code01_MaximumSumof3NonOverlappingSubarrays.java new file mode 100644 index 0000000..22f2a37 --- /dev/null +++ b/大厂刷题班/class22/Code01_MaximumSumof3NonOverlappingSubarrays.java @@ -0,0 +1,102 @@ +package class22; + +// 本题测试链接 : https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/ +public class Code01_MaximumSumof3NonOverlappingSubarrays { + +// public static int[] maxSumArray1(int[] arr) { +// int N = arr.length; +// int[] help = new int[N]; +// // help[i] 子数组必须以i位置结尾的情况下,累加和最大是多少? +// help[0] = arr[0]; +// for (int i = 1; i < N; i++) { +// int p1 = arr[i]; +// int p2 = arr[i] + help[i - 1]; +// help[i] = Math.max(p1, p2); +// } +// // dp[i] 在0~i范围上,随意选一个子数组,累加和最大是多少? +// int[] dp = new int[N]; +// dp[0] = help[0]; +// for (int i = 1; i < N; i++) { +// int p1 = help[i]; +// int p2 = dp[i - 1]; +// dp[i] = Math.max(p1, p2); +// } +// return dp; +// } +// +// public static int maxSumLenK(int[] arr, int k) { +// int N = arr.length; +// // 子数组必须以i位置的数结尾,长度一定要是K,累加和最大是多少? +// // help[0] help[k-2] ... +// int sum = 0; +// for (int i = 0; i < k - 1; i++) { +// sum += arr[i]; +// } +// // 0...k-2 k-1 sum +// int[] help = new int[N]; +// for (int i = k - 1; i < N; i++) { +// // 0..k-2 +// // 01..k-1 +// sum += arr[i]; +// help[i] = sum; +// // i == k-1 +// sum -= arr[i - k + 1]; +// } +// // help[i] - > dp[i] 0-..i K +// +// } + + public static int[] maxSumOfThreeSubarrays(int[] nums, int k) { + int N = nums.length; + int[] range = new int[N]; + int[] left = new int[N]; + int sum = 0; + for (int i = 0; i < k; i++) { + sum += nums[i]; + } + range[0] = sum; + left[k - 1] = 0; + int max = sum; + for (int i = k; i < N; i++) { + sum = sum - nums[i - k] + nums[i]; + range[i - k + 1] = sum; + left[i] = left[i - 1]; + if (sum > max) { + max = sum; + left[i] = i - k + 1; + } + } + sum = 0; + for (int i = N - 1; i >= N - k; i--) { + sum += nums[i]; + } + max = sum; + int[] right = new int[N]; + right[N - k] = N - k; + for (int i = N - k - 1; i >= 0; i--) { + sum = sum - nums[i + k] + nums[i]; + right[i] = right[i + 1]; + if (sum >= max) { + max = sum; + right[i] = i; + } + } + int a = 0; + int b = 0; + int c = 0; + max = 0; + for (int i = k; i < N - 2 * k + 1; i++) { // 中间一块的起始点 (0...k-1)选不了 i == N-1 + int part1 = range[left[i - 1]]; + int part2 = range[i]; + int part3 = range[right[i + k]]; + if (part1 + part2 + part3 > max) { + max = part1 + part2 + part3; + a = left[i - 1]; + b = i; + c = right[i + k]; + } + } + return new int[] { a, b, c }; + } + +} diff --git a/大厂刷题班/class22/Code02_TrappingRainWater.java b/大厂刷题班/class22/Code02_TrappingRainWater.java new file mode 100644 index 0000000..e022a12 --- /dev/null +++ b/大厂刷题班/class22/Code02_TrappingRainWater.java @@ -0,0 +1,28 @@ +package class22; + +// 本题测试链接 : https://leetcode.com/problems/trapping-rain-water/ +public class Code02_TrappingRainWater { + + public static int trap(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int L = 1; + int leftMax = arr[0]; + int R = N - 2; + int rightMax = arr[N - 1]; + int water = 0; + while (L <= R) { + if (leftMax <= rightMax) { + water += Math.max(0, leftMax - arr[L]); + leftMax = Math.max(leftMax, arr[L++]); + } else { + water += Math.max(0, rightMax - arr[R]); + rightMax = Math.max(rightMax, arr[R--]); + } + } + return water; + } + +} diff --git a/大厂刷题班/class22/Code03_TrappingRainWaterII.java b/大厂刷题班/class22/Code03_TrappingRainWaterII.java new file mode 100644 index 0000000..144dbac --- /dev/null +++ b/大厂刷题班/class22/Code03_TrappingRainWaterII.java @@ -0,0 +1,76 @@ +package class22; + +import java.util.PriorityQueue; + +// 本题测试链接 : https://leetcode.com/problems/trapping-rain-water-ii/ +public class Code03_TrappingRainWaterII { + + public static class Node { + public int value; + public int row; + public int col; + + public Node(int v, int r, int c) { + value = v; + row = r; + col = c; + } + + } + + public static int trapRainWater(int[][] heightMap) { + if (heightMap == null || heightMap.length == 0 || heightMap[0] == null || heightMap[0].length == 0) { + return 0; + } + int N = heightMap.length; + int M = heightMap[0].length; + boolean[][] isEnter = new boolean[N][M]; + PriorityQueue heap = new PriorityQueue<>((a, b) -> a.value - b.value); + for (int col = 0; col < M - 1; col++) { + isEnter[0][col] = true; + heap.add(new Node(heightMap[0][col], 0, col)); + } + for (int row = 0; row < N - 1; row++) { + isEnter[row][M - 1] = true; + heap.add(new Node(heightMap[row][M - 1], row, M - 1)); + } + for (int col = M - 1; col > 0; col--) { + isEnter[N - 1][col] = true; + heap.add(new Node(heightMap[N - 1][col], N - 1, col)); + } + for (int row = N - 1; row > 0; row--) { + isEnter[row][0] = true; + heap.add(new Node(heightMap[row][0], row, 0)); + } + int water = 0; + int max = 0; + while (!heap.isEmpty()) { + Node cur = heap.poll(); + max = Math.max(max, cur.value); + int r = cur.row; + int c = cur.col; + if (r > 0 && !isEnter[r - 1][c]) { + water += Math.max(0, max - heightMap[r - 1][c]); + isEnter[r - 1][c] = true; + heap.add(new Node(heightMap[r - 1][c], r - 1, c)); + } + if (r < N - 1 && !isEnter[r + 1][c]) { + water += Math.max(0, max - heightMap[r + 1][c]); + isEnter[r + 1][c] = true; + heap.add(new Node(heightMap[r + 1][c], r + 1, c)); + } + if (c > 0 && !isEnter[r][c - 1]) { + water += Math.max(0, max - heightMap[r][c - 1]); + isEnter[r][c - 1] = true; + heap.add(new Node(heightMap[r][c - 1], r, c - 1)); + } + if (c < M - 1 && !isEnter[r][c + 1]) { + water += Math.max(0, max - heightMap[r][c + 1]); + isEnter[r][c + 1] = true; + heap.add(new Node(heightMap[r][c + 1], r, c + 1)); + } + } + return water; + } + +} diff --git a/大厂刷题班/class22/Code04_VisibleMountains.java b/大厂刷题班/class22/Code04_VisibleMountains.java new file mode 100644 index 0000000..24dc0be --- /dev/null +++ b/大厂刷题班/class22/Code04_VisibleMountains.java @@ -0,0 +1,194 @@ +package class22; + +import java.util.HashSet; +import java.util.Stack; + +public class Code04_VisibleMountains { + + // 栈中放的记录, + // value就是指,times是收集的个数 + public static class Record { + public int value; + public int times; + + public Record(int value) { + this.value = value; + this.times = 1; + } + } + + public static int getVisibleNum(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int maxIndex = 0; + // 先在环中找到其中一个最大值的位置,哪一个都行 + for (int i = 0; i < N; i++) { + maxIndex = arr[maxIndex] < arr[i] ? i : maxIndex; + } + Stack stack = new Stack(); + // 先把(最大值,1)这个记录放入stack中 + stack.push(new Record(arr[maxIndex])); + // 从最大值位置的下一个位置开始沿next方向遍历 + int index = nextIndex(maxIndex, N); + // 用“小找大”的方式统计所有可见山峰对 + int res = 0; + // 遍历阶段开始,当index再次回到maxIndex的时候,说明转了一圈,遍历阶段就结束 + while (index != maxIndex) { + // 当前数要进入栈,判断会不会破坏第一维的数字从顶到底依次变大 + // 如果破坏了,就依次弹出栈顶记录,并计算山峰对数量 + while (stack.peek().value < arr[index]) { + int k = stack.pop().times; + // 弹出记录为(X,K),如果K==1,产生2对; 如果K>1,产生2*K + C(2,K)对。 + res += getInternalSum(k) + 2 * k; + } + // 当前数字arr[index]要进入栈了,如果和当前栈顶数字一样就合并 + // 不一样就把记录(arr[index],1)放入栈中 + if (stack.peek().value == arr[index]) { + stack.peek().times++; + } else { // > + stack.push(new Record(arr[index])); + } + index = nextIndex(index, N); + } + // 清算阶段开始了 + // 清算阶段的第1小阶段 + while (stack.size() > 2) { + int times = stack.pop().times; + res += getInternalSum(times) + 2 * times; + } + // 清算阶段的第2小阶段 + if (stack.size() == 2) { + int times = stack.pop().times; + res += getInternalSum(times) + + (stack.peek().times == 1 ? times : 2 * times); + } + // 清算阶段的第3小阶段 + res += getInternalSum(stack.pop().times); + return res; + } + + // 如果k==1返回0,如果k>1返回C(2,k) + public static int getInternalSum(int k) { + return k == 1 ? 0 : (k * (k - 1) / 2); + } + + // 环形数组中当前位置为i,数组长度为size,返回i的下一个位置 + public static int nextIndex(int i, int size) { + return i < (size - 1) ? (i + 1) : 0; + } + + // 环形数组中当前位置为i,数组长度为size,返回i的上一个位置 + public static int lastIndex(int i, int size) { + return i > 0 ? (i - 1) : (size - 1); + } + + // for test, O(N^2)的解法,绝对正确 + public static int rightWay(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int res = 0; + HashSet equalCounted = new HashSet<>(); + for (int i = 0; i < arr.length; i++) { + // 枚举从每一个位置出发,根据“小找大”原则能找到多少对儿,并且保证不重复找 + res += getVisibleNumFromIndex(arr, i, equalCounted); + } + return res; + } + + // for test + // 根据“小找大”的原则返回从index出发能找到多少对 + // 相等情况下,比如arr[1]==3,arr[5]==3 + // 之前如果从位置1找过位置5,那么等到从位置5出发时就不再找位置1(去重) + // 之前找过的、所有相等情况的山峰对,都保存在了equalCounted中 + public static int getVisibleNumFromIndex(int[] arr, int index, + HashSet equalCounted) { + int res = 0; + for (int i = 0; i < arr.length; i++) { + if (i != index) { // 不找自己 + if (arr[i] == arr[index]) { + String key = Math.min(index, i) + "_" + Math.max(index, i); + // 相等情况下,确保之前没找过这一对 + if (equalCounted.add(key) && isVisible(arr, index, i)) { + res++; + } + } else if (isVisible(arr, index, i)) { // 不相等的情况下直接找 + res++; + } + } + } + return res; + } + + // for test + // 调用该函数的前提是,lowIndex和highIndex一定不是同一个位置 + // 在“小找大”的策略下,从lowIndex位置能不能看到highIndex位置 + // next方向或者last方向有一个能走通,就返回true,否则返回false + public static boolean isVisible(int[] arr, int lowIndex, int highIndex) { + if (arr[lowIndex] > arr[highIndex]) { // “大找小”的情况直接返回false + return false; + } + int size = arr.length; + boolean walkNext = true; + int mid = nextIndex(lowIndex, size); + // lowIndex通过next方向走到highIndex,沿途不能出现比arr[lowIndex]大的数 + while (mid != highIndex) { + if (arr[mid] > arr[lowIndex]) { + walkNext = false;// next方向失败 + break; + } + mid = nextIndex(mid, size); + } + boolean walkLast = true; + mid = lastIndex(lowIndex, size); + // lowIndex通过last方向走到highIndex,沿途不能出现比arr[lowIndex]大的数 + while (mid != highIndex) { + if (arr[mid] > arr[lowIndex]) { + walkLast = false; // last方向失败 + break; + } + mid = lastIndex(mid, size); + } + return walkNext || walkLast; // 有一个成功就是能相互看见 + } + + // for test + public static int[] getRandomArray(int size, int max) { + int[] arr = new int[(int) (Math.random() * size)]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * max); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null || arr.length == 0) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int size = 10; + int max = 10; + int testTimes = 3000000; + System.out.println("test begin!"); + for (int i = 0; i < testTimes; i++) { + int[] arr = getRandomArray(size, max); + if (rightWay(arr) != getVisibleNum(arr)) { + printArray(arr); + System.out.println(rightWay(arr)); + System.out.println(getVisibleNum(arr)); + break; + } + } + System.out.println("test end!"); + } + +} \ No newline at end of file diff --git a/大厂刷题班/class22/Code05_TallestBillboard.java b/大厂刷题班/class22/Code05_TallestBillboard.java new file mode 100644 index 0000000..ede1d89 --- /dev/null +++ b/大厂刷题班/class22/Code05_TallestBillboard.java @@ -0,0 +1,38 @@ +package class22; + +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.com/problems/tallest-billboard/ +public class Code05_TallestBillboard { + + public int tallestBillboard(int[] rods) { + // key 集合对的某个差 + // value 满足差值为key的集合对中,最好的一对,较小集合的累加和 + // 较大 -> value + key + HashMap dp = new HashMap<>(), cur; + dp.put(0, 0);// 空集 和 空集 + for (int num : rods) { + if (num != 0) { + // cur 内部数据完全和dp一样 + cur = new HashMap<>(dp); // 考虑x之前的集合差值状况拷贝下来 + for (int d : cur.keySet()) { + int diffMore = cur.get(d); // 最好的一对,较小集合的累加和 + // x决定放入,比较大的那个 + dp.put(d + num, Math.max(diffMore, dp.getOrDefault(num + d, 0))); + // x决定放入,比较小的那个 + // 新的差值 Math.abs(x - d) + // 之前差值为Math.abs(x - d),的那一对,就要和这一对,决策一下 + // 之前那一对,较小集合的累加和diffXD + int diffXD = dp.getOrDefault(Math.abs(num - d), 0); + if (d >= num) { // x决定放入比较小的那个, 但是放入之后,没有超过这一对较大的那个 + dp.put(d - num, Math.max(diffMore + num, diffXD)); + } else { // x决定放入比较小的那个, 但是放入之后,没有超过这一对较大的那个 + dp.put(num - d, Math.max(diffMore + d, diffXD)); + } + } + } + } + return dp.get(0); + } + +} diff --git a/大厂刷题班/class23/Code01_LCATarjanAndTreeChainPartition.java b/大厂刷题班/class23/Code01_LCATarjanAndTreeChainPartition.java new file mode 100644 index 0000000..43154cb --- /dev/null +++ b/大厂刷题班/class23/Code01_LCATarjanAndTreeChainPartition.java @@ -0,0 +1,352 @@ +package class23; + +import java.util.HashSet; + +public class Code01_LCATarjanAndTreeChainPartition { + + // 给定数组tree大小为N,表示一共有N个节点 + // tree[i] = j 表示点i的父亲是点j,tree一定是一棵树而不是森林 + // queries是二维数组,大小为M*2,每一个长度为2的数组都表示一条查询 + // [4,9], 表示想查询4和9之间的最低公共祖先 + // [3,7], 表示想查询3和7之间的最低公共祖先 + // tree和queries里面的所有值,都一定在0~N-1之间 + // 返回一个数组ans,大小为M,ans[i]表示第i条查询的答案 + + // 暴力方法 + public static int[] query1(int[] father, int[][] queries) { + int M = queries.length; + int[] ans = new int[M]; + HashSet path = new HashSet<>(); + for (int i = 0; i < M; i++) { + int jump = queries[i][0]; + while (father[jump] != jump) { + path.add(jump); + jump = father[jump]; + } + path.add(jump); + jump = queries[i][1]; + while (!path.contains(jump)) { + jump = father[jump]; + } + ans[i] = jump; + path.clear(); + } + return ans; + } + + // 离线批量查询最优解 -> Tarjan + 并查集 + // 如果有M条查询,时间复杂度O(N + M) + // 但是必须把M条查询一次给全,不支持在线查询 + public static int[] query2(int[] father, int[][] queries) { + int N = father.length; + int M = queries.length; + int[] help = new int[N]; + int h = 0; + for (int i = 0; i < N; i++) { + if (father[i] == i) { + h = i; + } else { + help[father[i]]++; + } + } + int[][] mt = new int[N][]; + for (int i = 0; i < N; i++) { + mt[i] = new int[help[i]]; + } + for (int i = 0; i < N; i++) { + if (i != h) { + mt[father[i]][--help[father[i]]] = i; + } + } + for (int i = 0; i < M; i++) { + if (queries[i][0] != queries[i][1]) { + help[queries[i][0]]++; + help[queries[i][1]]++; + } + } + int[][] mq = new int[N][]; + int[][] mi = new int[N][]; + for (int i = 0; i < N; i++) { + mq[i] = new int[help[i]]; + mi[i] = new int[help[i]]; + } + for (int i = 0; i < M; i++) { + if (queries[i][0] != queries[i][1]) { + mq[queries[i][0]][--help[queries[i][0]]] = queries[i][1]; + mi[queries[i][0]][help[queries[i][0]]] = i; + mq[queries[i][1]][--help[queries[i][1]]] = queries[i][0]; + mi[queries[i][1]][help[queries[i][1]]] = i; + } + } + int[] ans = new int[M]; + UnionFind uf = new UnionFind(N); + process(h, mt, mq, mi, uf, ans); + for (int i = 0; i < M; i++) { + if (queries[i][0] == queries[i][1]) { + ans[i] = queries[i][0]; + } + } + return ans; + } + + // 当前来到head点 + // mt是整棵树 head下方有哪些点 mt[head] = {a,b,c,d} head的孩子是abcd + // mq问题列表 head有哪些问题 mq[head] = {x,y,z} (head,x) (head,y) (head z) + // mi得到问题的答案,填在ans的什么地方 {6,12,34} + // uf 并查集 + public static void process(int head, int[][] mt, int[][] mq, int[][] mi, UnionFind uf, int[] ans) { + for (int next : mt[head]) { // head有哪些孩子,都遍历去吧! + process(next, mt, mq, mi, uf, ans); + uf.union(head, next); + uf.setTag(head, head); + } + // 解决head的问题! + int[] q = mq[head]; + int[] i = mi[head]; + for (int k = 0; k < q.length; k++) { + // head和谁有问题 q[k] 答案填哪 i[k] + int tag = uf.getTag(q[k]); + if (tag != -1) { + ans[i[k]] = tag; + } + } + } + + public static class UnionFind { + private int[] f; // father -> 并查集里面father信息,i -> i的father + private int[] s; // size[] -> 集合 --> i size[i] + private int[] t; // tag[] -> 集合 ---> tag[i] = ? + private int[] h; // 栈?并查集搞扁平化 + + public UnionFind(int N) { + f = new int[N]; + s = new int[N]; + t = new int[N]; + h = new int[N]; + for (int i = 0; i < N; i++) { + f[i] = i; + s[i] = 1; + t[i] = -1; + } + } + + private int find(int i) { + int j = 0; + // i -> j -> k -> s -> a -> a + while (i != f[i]) { + h[j++] = i; + i = f[i]; + } + // i -> a + // j -> a + // k -> a + // s -> a + while (j > 0) { + h[--j] = i; + } + // a + return i; + } + + public void union(int i, int j) { + int fi = find(i); + int fj = find(j); + if (fi != fj) { + int si = s[fi]; + int sj = s[fj]; + int m = si >= sj ? fi : fj; + int l = m == fi ? fj : fi; + f[l] = m; + s[m] += s[l]; + } + } + + // 集合的某个元素是i,请把整个集合打上统一的标签,tag + public void setTag(int i, int tag) { + t[find(i)] = tag; + } + + // 集合的某个元素是i,请把整个集合的tag信息返回 + public int getTag(int i) { + return t[find(i)]; + } + + } + + // 在线查询最优解 -> 树链剖分 + // 空间复杂度O(N), 支持在线查询,单次查询时间复杂度O(logN) + // 如果有M次查询,时间复杂度O(N + M * logN) + public static int[] query3(int[] father, int[][] queries) { + TreeChain tc = new TreeChain(father); + int M = queries.length; + int[] ans = new int[M]; + for (int i = 0; i < M; i++) { + // x y ? + // x x x + if (queries[i][0] == queries[i][1]) { + ans[i] = queries[i][0]; + } else { + ans[i] = tc.lca(queries[i][0], queries[i][1]); + } + } + return ans; + } + + public static class TreeChain { + private int n; + private int h; + private int[][] tree; + private int[] fa; + private int[] dep; + private int[] son; + private int[] siz; + private int[] top; + + public TreeChain(int[] father) { + initTree(father); + dfs1(h, 0); + dfs2(h, h); + } + + private void initTree(int[] father) { + n = father.length + 1; + tree = new int[n][]; + fa = new int[n]; + dep = new int[n]; + son = new int[n]; + siz = new int[n]; + top = new int[n--]; + int[] cnum = new int[n]; + for (int i = 0; i < n; i++) { + if (father[i] == i) { + h = i + 1; + } else { + cnum[father[i]]++; + } + } + tree[0] = new int[0]; + for (int i = 0; i < n; i++) { + tree[i + 1] = new int[cnum[i]]; + } + for (int i = 0; i < n; i++) { + if (i + 1 != h) { + tree[father[i] + 1][--cnum[father[i]]] = i + 1; + } + } + } + + private void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + int maxSize = -1; + for (int v : tree[u]) { + dfs1(v, u); + siz[u] += siz[v]; + if (siz[v] > maxSize) { + maxSize = siz[v]; + son[u] = v; + } + } + } + + private void dfs2(int u, int t) { + top[u] = t; + if (son[u] != 0) { + dfs2(son[u], t); + for (int v : tree[u]) { + if (v != son[u]) { + dfs2(v, v); + } + } + } + } + + public int lca(int a, int b) { + a++; + b++; + while (top[a] != top[b]) { + if (dep[top[a]] > dep[top[b]]) { + a = fa[top[a]]; + } else { + b = fa[top[b]]; + } + } + return (dep[a] < dep[b] ? a : b) - 1; + } + } + + // 为了测试 + // 随机生成N个节点树,可能是多叉树,并且一定不是森林 + // 输入参数N要大于0 + public static int[] generateFatherArray(int N) { + int[] order = new int[N]; + for (int i = 0; i < N; i++) { + order[i] = i; + } + for (int i = N - 1; i >= 0; i--) { + swap(order, i, (int) (Math.random() * (i + 1))); + } + int[] ans = new int[N]; + ans[order[0]] = order[0]; + for (int i = 1; i < N; i++) { + ans[order[i]] = order[(int) (Math.random() * i)]; + } + return ans; + } + + // 为了测试 + // 随机生成M条查询,点有N个,点的编号在0~N-1之间 + // 输入参数M和N都要大于0 + public static int[][] generateQueries(int M, int N) { + int[][] ans = new int[M][2]; + for (int i = 0; i < M; i++) { + ans[i][0] = (int) (Math.random() * N); + ans[i][1] = (int) (Math.random() * N); + } + return ans; + } + + // 为了测试 + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 为了测试 + public static boolean equal(int[] a, int[] b) { + if (a.length != b.length) { + return false; + } + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + // 为了测试 + public static void main(String[] args) { + int N = 1000; + int M = 200; + int testTime = 50000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * N) + 1; + int ques = (int) (Math.random() * M) + 1; + int[] father = generateFatherArray(size); + int[][] queries = generateQueries(ques, size); + int[] ans1 = query1(father, queries); + int[] ans2 = query2(father, queries); + int[] ans3 = query3(father, queries); + if (!equal(ans1, ans2) || !equal(ans1, ans3)) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class23/Code02_MaxABSBetweenLeftAndRight.java b/大厂刷题班/class23/Code02_MaxABSBetweenLeftAndRight.java new file mode 100644 index 0000000..af3cd49 --- /dev/null +++ b/大厂刷题班/class23/Code02_MaxABSBetweenLeftAndRight.java @@ -0,0 +1,63 @@ +package class23; + +public class Code02_MaxABSBetweenLeftAndRight { + + public static int maxABS1(int[] arr) { + int res = Integer.MIN_VALUE; + int maxLeft = 0; + int maxRight = 0; + for (int i = 0; i != arr.length - 1; i++) { + maxLeft = Integer.MIN_VALUE; + for (int j = 0; j != i + 1; j++) { + maxLeft = Math.max(arr[j], maxLeft); + } + maxRight = Integer.MIN_VALUE; + for (int j = i + 1; j != arr.length; j++) { + maxRight = Math.max(arr[j], maxRight); + } + res = Math.max(Math.abs(maxLeft - maxRight), res); + } + return res; + } + + public static int maxABS2(int[] arr) { + int[] lArr = new int[arr.length]; + int[] rArr = new int[arr.length]; + lArr[0] = arr[0]; + rArr[arr.length - 1] = arr[arr.length - 1]; + for (int i = 1; i < arr.length; i++) { + lArr[i] = Math.max(lArr[i - 1], arr[i]); + } + for (int i = arr.length - 2; i > -1; i--) { + rArr[i] = Math.max(rArr[i + 1], arr[i]); + } + int max = 0; + for (int i = 0; i < arr.length - 1; i++) { + max = Math.max(max, Math.abs(lArr[i] - rArr[i + 1])); + } + return max; + } + + public static int maxABS3(int[] arr) { + int max = Integer.MIN_VALUE; + for (int i = 0; i < arr.length; i++) { + max = Math.max(arr[i], max); + } + return max - Math.min(arr[0], arr[arr.length - 1]); + } + + public static int[] generateRandomArray(int length) { + int[] arr = new int[length]; + for (int i = 0; i != arr.length; i++) { + arr[i] = (int) (Math.random() * 1000) - 499; + } + return arr; + } + + public static void main(String[] args) { + int[] arr = generateRandomArray(200); + System.out.println(maxABS1(arr)); + System.out.println(maxABS2(arr)); + System.out.println(maxABS3(arr)); + } +} diff --git a/大厂刷题班/class23/Code03_LongestIntegratedLength.java b/大厂刷题班/class23/Code03_LongestIntegratedLength.java new file mode 100644 index 0000000..8ae031c --- /dev/null +++ b/大厂刷题班/class23/Code03_LongestIntegratedLength.java @@ -0,0 +1,106 @@ +package class23; + +import java.util.Arrays; +import java.util.HashSet; + +public class Code03_LongestIntegratedLength { + + public static int maxLen(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + HashSet set = new HashSet<>(); + int ans = 1; + for (int L = 0; L < N; L++) { + set.clear(); + int min = arr[L]; + int max = arr[L]; + set.add(arr[L]); + // L..R + for (int R = L + 1; R < N; R++) { + // L....R + if(set.contains(arr[R])) { + break; + } + set.add(arr[R]); + min = Math.min(min, arr[R]); + max = Math.max(max, arr[R]); + if(max - min == R - L) { + ans = Math.max(ans, R - L + 1); + } + } + } + return ans; + + } + + public static int getLIL1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int len = 0; + // O(N^3 * log N) + for (int start = 0; start < arr.length; start++) { // 子数组所有可能的开头 + for (int end = start; end < arr.length; end++) { // 开头为start的情况下,所有可能的结尾 + // arr[s ... e] + // O(N * logN) + if (isIntegrated(arr, start, end)) { + len = Math.max(len, end - start + 1); + } + } + } + return len; + } + + public static boolean isIntegrated(int[] arr, int left, int right) { + int[] newArr = Arrays.copyOfRange(arr, left, right + 1); // O(N) + Arrays.sort(newArr); // O(N*logN) + for (int i = 1; i < newArr.length; i++) { + if (newArr[i - 1] != newArr[i] - 1) { + return false; + } + } + return true; + } + + public static int getLIL2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int len = 0; + int max = 0; + int min = 0; + HashSet set = new HashSet(); + for (int L = 0; L < arr.length; L++) { // L 左边界 + // L ....... + set.clear(); + max = Integer.MIN_VALUE; + min = Integer.MAX_VALUE; + for (int R = L; R < arr.length; R++) { // R 右边界 + // arr[L..R]这个子数组在验证 l...R L...r+1 l...r+2 + if (set.contains(arr[R])) { + // arr[L..R]上开始 出现重复值了,arr[L..R往后]不需要验证了, + // 一定不是可整合的 + break; + } + // arr[L..R]上无重复值 + set.add(arr[R]); + max = Math.max(max, arr[R]); + min = Math.min(min, arr[R]); + if (max - min == R - L) { // L..R 是可整合的 + len = Math.max(len, R - L + 1); + } + } + } + return len; + } + + public static void main(String[] args) { + int[] arr = { 5, 5, 3, 2, 6, 4, 3 }; + System.out.println(getLIL1(arr)); + System.out.println(getLIL2(arr)); + + } + +} diff --git a/大厂刷题班/class23/Code04_FindKMajority.java b/大厂刷题班/class23/Code04_FindKMajority.java new file mode 100644 index 0000000..91ea4c5 --- /dev/null +++ b/大厂刷题班/class23/Code04_FindKMajority.java @@ -0,0 +1,113 @@ +package class23; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; + +public class Code04_FindKMajority { + + public static void printHalfMajor(int[] arr) { + int cand = 0; + int HP = 0; + for (int i = 0; i < arr.length; i++) { + if (HP == 0) { + cand = arr[i]; + HP = 1; + } else if (arr[i] == cand) { + HP++; + } else { + HP--; + } + } + if(HP == 0) { + System.out.println("no such number."); + return; + } + HP = 0; + for (int i = 0; i < arr.length; i++) { + if (arr[i] == cand) { + HP++; + } + } + if (HP > arr.length / 2) { + System.out.println(cand); + } else { + System.out.println("no such number."); + } + } + + public static void printKMajor(int[] arr, int K) { + if (K < 2) { + System.out.println("the value of K is invalid."); + return; + } + // 攒候选,cands,候选表,最多K-1条记录! > N / K次的数字,最多有K-1个 + HashMap cands = new HashMap(); + for (int i = 0; i != arr.length; i++) { + if (cands.containsKey(arr[i])) { + cands.put(arr[i], cands.get(arr[i]) + 1); + } else { // arr[i] 不是候选 + if (cands.size() == K - 1) { // 当前数肯定不要!,每一个候选付出1点血量,血量变成0的候选,要删掉! + allCandsMinusOne(cands); + } else { + cands.put(arr[i], 1); + } + } + } + // 所有可能的候选,都在cands表中!遍历一遍arr,每个候选收集真实次数 + + + + + HashMap reals = getReals(arr, cands); + boolean hasPrint = false; + for (Entry set : cands.entrySet()) { + Integer key = set.getKey(); + if (reals.get(key) > arr.length / K) { + hasPrint = true; + System.out.print(key + " "); + } + } + System.out.println(hasPrint ? "" : "no such number."); + } + + public static void allCandsMinusOne(HashMap map) { + List removeList = new LinkedList(); + for (Entry set : map.entrySet()) { + Integer key = set.getKey(); + Integer value = set.getValue(); + if (value == 1) { + removeList.add(key); + } + map.put(key, value - 1); + } + for (Integer removeKey : removeList) { + map.remove(removeKey); + } + } + + public static HashMap getReals(int[] arr, + HashMap cands) { + HashMap reals = new HashMap(); + for (int i = 0; i != arr.length; i++) { + int curNum = arr[i]; + if (cands.containsKey(curNum)) { + if (reals.containsKey(curNum)) { + reals.put(curNum, reals.get(curNum) + 1); + } else { + reals.put(curNum, 1); + } + } + } + return reals; + } + + public static void main(String[] args) { + int[] arr = { 1, 2, 3, 1, 1, 2, 1 }; + printHalfMajor(arr); + int K = 4; + printKMajor(arr, K); + } + +} diff --git a/大厂刷题班/class23/Code05_MinimumCostToMergeStones.java b/大厂刷题班/class23/Code05_MinimumCostToMergeStones.java new file mode 100644 index 0000000..45f6c84 --- /dev/null +++ b/大厂刷题班/class23/Code05_MinimumCostToMergeStones.java @@ -0,0 +1,159 @@ +package class23; + +// 本题测试链接 : https://leetcode.com/problems/minimum-cost-to-merge-stones/ +public class Code05_MinimumCostToMergeStones { + +// // arr[L...R]一定要整出P份,合并的最小代价,返回! +// public static int f(int[] arr, int K, int L, int R, int P) { +// if(从L到R根本不可能弄出P份) { +// return Integer.MAX_VALUE; +// } +// // 真的有可能的! +// if(P == 1) { +// return L == R ? 0 : (f(arr, K, L, R, K) + 最后一次大合并的代价); +// } +// int ans = Integer.MAX_VALUE; +// // 真的有可能,P > 1 +// for(int i = L; i < R;i++) { +// // L..i(1份) i+1...R(P-1份) +// int left = f(arr, K, L, i, 1); +// if(left == Integer.MAX_VALUE) { +// continue; +// } +// int right = f(arr, K, i+1,R,P-1); +// if(right == Integer.MAX_VALUE) { +// continue; +// } +// int all = left + right; +// ans = Math.min(ans, all); +// } +// return ans; +// } + + public static int mergeStones1(int[] stones, int K) { + int n = stones.length; + if ((n - 1) % (K - 1) > 0) { + return -1; + } + int[] presum = new int[n + 1]; + for (int i = 0; i < n; i++) { + presum[i + 1] = presum[i] + stones[i]; + } + return process1(0, n - 1, 1, stones, K, presum); + } + + // part >= 1 + // arr[L..R] 一定要弄出part份,返回最低代价 + // arr、K、presum(前缀累加和数组,求i..j的累加和,就是O(1)了) + public static int process1(int L, int R, int P, int[] arr, int K, int[] presum) { + if (L == R) { // arr[L..R] + return P == 1 ? 0 : -1; + } + // L ... R 不只一个数 + if (P == 1) { + int next = process1(L, R, K, arr, K, presum); + if (next == -1) { + return -1; + } else { + return next + presum[R + 1] - presum[L]; + } + } else { // P > 1 + int ans = Integer.MAX_VALUE; + // L...mid是第1块,剩下的是part-1块 + for (int mid = L; mid < R; mid += K - 1) { + // L..mid(一份) mid+1...R(part - 1) + int next1 = process1(L, mid, 1, arr, K, presum); + int next2 = process1(mid + 1, R, P - 1, arr, K, presum); + if (next1 != -1 && next2 != -1) { + ans = Math.min(ans, next1 + next2); + } + } + return ans; + } + } + + public static int mergeStones2(int[] stones, int K) { + int n = stones.length; + if ((n - 1) % (K - 1) > 0) { // n个数,到底能不能K个相邻的数合并,最终变成1个数! + return -1; + } + int[] presum = new int[n + 1]; + for (int i = 0; i < n; i++) { + presum[i + 1] = presum[i] + stones[i]; + } + int[][][] dp = new int[n][n][K + 1]; + return process2(0, n - 1, 1, stones, K, presum, dp); + } + + public static int process2(int L, int R, int P, int[] arr, int K, int[] presum, int[][][] dp) { + if (dp[L][R][P] != 0) { + return dp[L][R][P]; + } + if (L == R) { + return P == 1 ? 0 : -1; + } + if (P == 1) { + int next = process2(L, R, K, arr, K, presum, dp); + if (next == -1) { + dp[L][R][P] = -1; + return -1; + } else { + dp[L][R][P] = next + presum[R + 1] - presum[L]; + return next + presum[R + 1] - presum[L]; + } + } else { + int ans = Integer.MAX_VALUE; + // i...mid是第1块,剩下的是part-1块 + for (int mid = L; mid < R; mid += K - 1) { + int next1 = process2(L, mid, 1, arr, K, presum, dp); + int next2 = process2(mid + 1, R, P - 1, arr, K, presum, dp); + if (next1 == -1 || next2 == -1) { + dp[L][R][P] = -1; + return -1; + } else { + ans = Math.min(ans, next1 + next2); + } + } + dp[L][R][P] = ans; + return ans; + } + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) (maxSize * Math.random()) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int maxSize = 12; + int maxValue = 100; + System.out.println("Test begin"); + for (int testTime = 0; testTime < 100000; testTime++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int K = (int) (Math.random() * 7) + 2; + int ans1 = mergeStones1(arr, K); + int ans2 = mergeStones2(arr, K); + if (ans1 != ans2) { + System.out.println(ans1); + System.out.println(ans2); + } + } + + } + +} diff --git a/大厂刷题班/class24/Code01_Split4Parts.java b/大厂刷题班/class24/Code01_Split4Parts.java new file mode 100644 index 0000000..8ac489e --- /dev/null +++ b/大厂刷题班/class24/Code01_Split4Parts.java @@ -0,0 +1,89 @@ +package class24; + +import java.util.HashMap; +import java.util.HashSet; + +public class Code01_Split4Parts { + + public static boolean canSplits1(int[] arr) { + if (arr == null || arr.length < 7) { + return false; + } + HashSet set = new HashSet(); + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + } + int leftSum = arr[0]; + for (int i = 1; i < arr.length - 1; i++) { + set.add(String.valueOf(leftSum) + "_" + String.valueOf(sum - leftSum - arr[i])); + leftSum += arr[i]; + } + int l = 1; + int lsum = arr[0]; + int r = arr.length - 2; + int rsum = arr[arr.length - 1]; + while (l < r - 3) { + if (lsum == rsum) { + String lkey = String.valueOf(lsum * 2 + arr[l]); + String rkey = String.valueOf(rsum * 2 + arr[r]); + if (set.contains(lkey + "_" + rkey)) { + return true; + } + lsum += arr[l++]; + } else if (lsum < rsum) { + lsum += arr[l++]; + } else { + rsum += arr[r--]; + } + } + return false; + } + + public static boolean canSplits2(int[] arr) { + if (arr == null || arr.length < 7) { + return false; + } + // key 某一个累加和, value出现的位置 + HashMap map = new HashMap(); + int sum = arr[0]; + for (int i = 1; i < arr.length; i++) { + map.put(sum, i); + sum += arr[i]; + } + int lsum = arr[0]; // 第一刀左侧的累加和 + for (int s1 = 1; s1 < arr.length - 5; s1++) { // s1是第一刀的位置 + int checkSum = lsum * 2 + arr[s1]; // 100 x 100 100*2 + x + if (map.containsKey(checkSum)) { + int s2 = map.get(checkSum); // j -> y + checkSum += lsum + arr[s2]; + if (map.containsKey(checkSum)) { // 100 * 3 + x + y + int s3 = map.get(checkSum); // k -> z + if (checkSum + arr[s3] + lsum == sum) { + return true; + } + } + } + lsum += arr[s1]; + } + return false; + } + + public static int[] generateRondomArray() { + int[] res = new int[(int) (Math.random() * 10) + 7]; + for (int i = 0; i < res.length; i++) { + res[i] = (int) (Math.random() * 10) + 1; + } + return res; + } + + public static void main(String[] args) { + int testTime = 3000000; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRondomArray(); + if (canSplits1(arr) ^ canSplits2(arr)) { + System.out.println("Error"); + } + } + } +} diff --git a/大厂刷题班/class24/Code02_KthMinPair.java b/大厂刷题班/class24/Code02_KthMinPair.java new file mode 100644 index 0000000..ebd3d96 --- /dev/null +++ b/大厂刷题班/class24/Code02_KthMinPair.java @@ -0,0 +1,180 @@ +package class24; + +import java.util.Arrays; +import java.util.Comparator; + +public class Code02_KthMinPair { + + public static class Pair { + public int x; + public int y; + + Pair(int a, int b) { + x = a; + y = b; + } + } + + public static class PairComparator implements Comparator { + + @Override + public int compare(Pair arg0, Pair arg1) { + return arg0.x != arg1.x ? arg0.x - arg1.x : arg0.y - arg1.y; + } + + } + + // O(N^2 * log (N^2))的复杂度,你肯定过不了 + // 返回的int[] 长度是2,{3,1} int[2] = [3,1] + public static int[] kthMinPair1(int[] arr, int k) { + int N = arr.length; + if (k > N * N) { + return null; + } + Pair[] pairs = new Pair[N * N]; + int index = 0; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + pairs[index++] = new Pair(arr[i], arr[j]); + } + } + Arrays.sort(pairs, new PairComparator()); + return new int[] { pairs[k - 1].x, pairs[k - 1].y }; + } + + // O(N*logN)的复杂度,你肯定过了 + public static int[] kthMinPair2(int[] arr, int k) { + int N = arr.length; + if (k > N * N) { + return null; + } + // O(N*logN) + Arrays.sort(arr); + // 第K小的数值对,第一维数字,是什么 是arr中 + int fristNum = arr[(k - 1) / N]; + int lessFristNumSize = 0;// 数出比fristNum小的数有几个 + int fristNumSize = 0; // 数出==fristNum的数有几个 + // <= fristNum + for (int i = 0; i < N && arr[i] <= fristNum; i++) { + if (arr[i] < fristNum) { + lessFristNumSize++; + } else { + fristNumSize++; + } + } + int rest = k - (lessFristNumSize * N); + return new int[] { fristNum, arr[(rest - 1) / fristNumSize] }; + } + + // O(N)的复杂度,你肯定蒙了 + public static int[] kthMinPair3(int[] arr, int k) { + int N = arr.length; + if (k > N * N) { + return null; + } + // 在无序数组中,找到第K小的数,返回值 + // 第K小,以1作为开始 + int fristNum = getMinKth(arr, (k - 1) / N); + // 第1维数字 + int lessFristNumSize = 0; + int fristNumSize = 0; + for (int i = 0; i < N; i++) { + if (arr[i] < fristNum) { + lessFristNumSize++; + } + if (arr[i] == fristNum) { + fristNumSize++; + } + } + int rest = k - (lessFristNumSize * N); + return new int[] { fristNum, getMinKth(arr, (rest - 1) / fristNumSize) }; + } + + // 改写快排,时间复杂度O(N) + // 在无序数组arr中,找到,如果排序的话,arr[index]的数是什么? + public static int getMinKth(int[] arr, int index) { + int L = 0; + int R = arr.length - 1; + int pivot = 0; + int[] range = null; + while (L < R) { + pivot = arr[L + (int) (Math.random() * (R - L + 1))]; + range = partition(arr, L, R, pivot); + if (index < range[0]) { + R = range[0] - 1; + } else if (index > range[1]) { + L = range[1] + 1; + } else { + return pivot; + } + } + return arr[L]; + } + + public static int[] partition(int[] arr, int L, int R, int pivot) { + int less = L - 1; + int more = R + 1; + int cur = L; + while (cur < more) { + if (arr[cur] < pivot) { + swap(arr, ++less, cur++); + } else if (arr[cur] > pivot) { + swap(arr, cur, --more); + } else { + cur++; + } + } + return new int[] { less + 1, more - 1 }; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 为了测试,生成值也随机,长度也随机的随机数组 + public static int[] getRandomArray(int max, int len) { + int[] arr = new int[(int) (Math.random() * len) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * max) - (int) (Math.random() * max); + } + return arr; + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // 随机测试了百万组,保证三种方法都是对的 + public static void main(String[] args) { + int max = 100; + int len = 30; + int testTimes = 100000; + System.out.println("test bagin, test times : " + testTimes); + for (int i = 0; i < testTimes; i++) { + int[] arr = getRandomArray(max, len); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int[] arr3 = copyArray(arr); + int N = arr.length * arr.length; + int k = (int) (Math.random() * N) + 1; + int[] ans1 = kthMinPair1(arr1, k); + int[] ans2 = kthMinPair2(arr2, k); + int[] ans3 = kthMinPair3(arr3, k); + if (ans1[0] != ans2[0] || ans2[0] != ans3[0] || ans1[1] != ans2[1] || ans2[1] != ans3[1]) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/大厂刷题班/class24/Code03_NotContains4.java b/大厂刷题班/class24/Code03_NotContains4.java new file mode 100644 index 0000000..0a52911 --- /dev/null +++ b/大厂刷题班/class24/Code03_NotContains4.java @@ -0,0 +1,144 @@ +package class24; + +// 里程表不能含有4,给定一个数num,返回他是里程表里的第几个 +public class Code03_NotContains4 { + + // num中一定没有4这个数字 + public static long notContains4Nums1(long num) { + long count = 0; + for (long i = 1; i <= num; i++) { + if (isNot4(i)) { + count++; + } + } + return count; + } + + public static boolean isNot4(long num) { + while (num != 0) { + if (num % 10 == 4) { + return false; + } + num /= 10; + } + return true; + } + + // arr[1] : 比1位数还少1位,有几个数(数字里可以包含0但是不可以包含4)?0个 + // arr[2] : 比2位数还少1位,有几个数(数字里可以包含0但是不可以包含4)?9个 + // 1 -> 0 1 2 3 - 5 6 7 8 9 = 9 + // arr[3] :比3位数还少1位,有几个数(数字里可以包含0但是不可以包含4)?81个 + // 1 : 0 (0 1 2 3 - 5 6 7 8 9) = 9 + // 2 : + // 1 (0 1 2 3 - 5 6 7 8 9) = 9 + // 2 (0 1 2 3 - 5 6 7 8 9) = 9 + // 3 (0 1 2 3 - 5 6 7 8 9) = 9 + // 5 (0 1 2 3 - 5 6 7 8 9) = 9 + // ... + // 9 (0 1 2 3 - 5 6 7 8 9) = 9 + public static long[] arr = { 0L, 1L, 9L, 81L, 729L, 6561L, 59049L, 531441L, 4782969L, 43046721L, 387420489L, + 3486784401L, 31381059609L, 282429536481L, 2541865828329L, 22876792454961L, 205891132094649L, + 1853020188851841L, 16677181699666569L, 150094635296999121L, 1350851717672992089L }; + + // num中一定没有4这个数字 + public static long notContains4Nums2(long num) { + if (num <= 0) { + return 0; + } + // num的十进制位数,len + int len = decimalLength(num); + // 35621 + // 10000 + long offset = offset(len); + + // 第一位数字 + long first = num / offset; + return arr[len] - 1 + (first - (first < 4 ? 1 : 2)) * arr[len] + process(num % offset, offset / 10, len - 1); + } + + // num之前,有开头! + // 在算0的情况下,num是第几个数字 + // num 76217 + // 10000 + // 6217 + // 1000 + // len + public static long process(long num, long offset, int len) { + if (len == 0) { + return 1; + } + long first = num / offset; + return (first < 4 ? first : (first - 1)) * arr[len] + process(num % offset, offset / 10, len - 1); + } + + // num的十进制位数 + // num = 7653210 + // 7 + public static int decimalLength(long num) { + int len = 0; + while (num != 0) { + len++; + num /= 10; + } + return len; + } + + // len = 6 + // 100000 + // len = 4 + // 1000 + // 3452168 + // 1000000 + // 3 + public static long offset(int len) { + long offset = 1; + for (int i = 1; i < len; i++) { + offset *= 10L; + } + return offset; + } + + // 讲完之后想到了课上同学的留言 + // 突然意识到,这道题的本质是一个9进制的数转成10进制的数 + // 不过好在课上的解法有实际意义,就是这种求解的方式,很多题目都这么弄 + // 还有课上的时间复杂度和"9进制的数转成10进制的数"的做法,时间复杂度都是O(lg N) + // 不过"9进制的数转成10进制的数"毫无疑问是最优解 + public static long notContains4Nums3(long num) { + if (num <= 0) { + return 0; + } + long ans = 0; + for (long base = 1, cur = 0; num != 0; num /= 10, base *= 9) { + cur = num % 10; + ans += (cur < 4 ? cur : cur - 1) * base; + } + return ans; + } + + public static void main(String[] args) { + long max = 88888888L; + System.out.println("功能测试开始,验证 0 ~ " + max + " 以内所有的结果"); + for (long i = 0; i <= max; i++) { + // 测试的时候,输入的数字i里不能含有4,这是题目的规定! + if (isNot4(i) && notContains4Nums2(i) != notContains4Nums3(i)) { + System.out.println("Oops!"); + } + } + System.out.println("如果没有打印Oops说明验证通过"); + + long num = 8173528638135L; + long start; + long end; + System.out.println("性能测试开始,计算 num = " + num + " 的答案"); + start = System.currentTimeMillis(); + long ans2 = notContains4Nums2(num); + end = System.currentTimeMillis(); + System.out.println("方法二答案 : " + ans2 + ", 运行时间 : " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + long ans3 = notContains4Nums3(num); + end = System.currentTimeMillis(); + System.out.println("方法三答案 : " + ans3 + ", 运行时间 : " + (end - start) + " ms"); + } + +} diff --git a/大厂刷题班/class24/Code04_Painting.java b/大厂刷题班/class24/Code04_Painting.java new file mode 100644 index 0000000..229a906 --- /dev/null +++ b/大厂刷题班/class24/Code04_Painting.java @@ -0,0 +1,75 @@ +package class24; + +import java.util.ArrayList; +import java.util.List; + +public class Code04_Painting { + // N * M的棋盘 + // 每种颜色的格子数必须相同的 + // 相邻格子染的颜色必须不同 + // 所有格子必须染色 + // 返回至少多少种颜色可以完成任务 + + public static int minColors(int N, int M) { + // 颜色数量是i + for (int i = 2; i < N * M; i++) { + int[][] matrix = new int[N][M]; + // 下面这一句可知,需要的最少颜色数i,一定是N*M的某个因子 + if ((N * M) % i == 0 && can(matrix, N, M, i)) { + return i; + } + } + return N * M; + } + + // 在matrix上染色,返回只用pNum种颜色是否可以做到要求 + public static boolean can(int[][] matrix, int N, int M, int pNum) { + int all = N * M; + int every = all / pNum; + ArrayList rest = new ArrayList<>(); + rest.add(0); + for (int i = 1; i <= pNum; i++) { + rest.add(every); + } + return process(matrix, N, M, pNum, 0, 0, rest); + } + + public static boolean process(int[][] matrix, int N, int M, int pNum, int row, int col, List rest) { + if (row == N) { + return true; + } + if (col == M) { + return process(matrix, N, M, pNum, row + 1, 0, rest); + } + int left = col == 0 ? 0 : matrix[row][col - 1]; + int up = row == 0 ? 0 : matrix[row - 1][col]; + for (int color = 1; color <= pNum; color++) { + if (color != left && color != up && rest.get(color) > 0) { + int count = rest.get(color); + rest.set(color, count - 1); + matrix[row][col] = color; + if (process(matrix, N, M, pNum, row, col + 1, rest)) { + return true; + } + rest.set(color, count); + matrix[row][col] = 0; + } + } + return false; + } + + public static void main(String[] args) { + // 根据代码16行的提示,打印出答案,看看是答案是哪个因子 + for (int N = 2; N < 10; N++) { + for (int M = 2; M < 10; M++) { + System.out.println("N = " + N); + System.out.println("M = " + M); + System.out.println("ans = " + minColors(N, M)); + System.out.println("==========="); + } + } + // 打印答案,分析可知,是N*M最小的质数因子,原因不明,也不重要 + // 反正打表法猜出来了 + } + +} diff --git a/大厂刷题班/class24/Code05_MinWindowLength.java b/大厂刷题班/class24/Code05_MinWindowLength.java new file mode 100644 index 0000000..60508cd --- /dev/null +++ b/大厂刷题班/class24/Code05_MinWindowLength.java @@ -0,0 +1,80 @@ +package class24; + +public class Code05_MinWindowLength { + + public static int minLength(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() < s2.length()) { + return Integer.MAX_VALUE; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int[] map = new int[256]; // map[37] = 4 37 4次 + for (int i = 0; i != str2.length; i++) { + map[str2[i]]++; + } + int all = str2.length; + + // [L,R-1] R + // [L,R) -> [0,0) + int L = 0; + int R = 0; + int minLen = Integer.MAX_VALUE; + while (R != str1.length) { + map[str1[R]]--; + if (map[str1[R]] >= 0) { + all--; + } + if (all == 0) { // 还完了 + while (map[str1[L]] < 0) { + map[str1[L++]]++; + } + // [L..R] + minLen = Math.min(minLen, R - L + 1); + all++; + map[str1[L++]]++; + } + R++; + } + return minLen == Integer.MAX_VALUE ? 0 : minLen; + } + + // 测试链接 : https://leetcode.com/problems/minimum-window-substring/ + public static String minWindow(String s, String t) { + if (s.length() < t.length()) { + return ""; + } + char[] str = s.toCharArray(); + char[] target = t.toCharArray(); + int[] map = new int[256]; + for (char cha : target) { + map[cha]++; + } + int all = target.length; + int L = 0; + int R = 0; + int minLen = Integer.MAX_VALUE; + int ansl = -1; + int ansr = -1; + while (R != str.length) { + map[str[R]]--; + if (map[str[R]] >= 0) { + all--; + } + if (all == 0) { + while (map[str[L]] < 0) { + map[str[L++]]++; + } + if (minLen > R - L + 1) { + minLen = R - L + 1; + ansl = L; + ansr = R; + } + all++; + map[str[L++]]++; + } + R++; + } + return minLen == Integer.MAX_VALUE ? "" : s.substring(ansl, ansr + 1); + } + +} diff --git a/大厂刷题班/class24/Code06_RemoveDuplicateLettersLessLexi.java b/大厂刷题班/class24/Code06_RemoveDuplicateLettersLessLexi.java new file mode 100644 index 0000000..2672411 --- /dev/null +++ b/大厂刷题班/class24/Code06_RemoveDuplicateLettersLessLexi.java @@ -0,0 +1,74 @@ +package class24; + +// 本题测试链接 : https://leetcode.com/problems/remove-duplicate-letters/ +public class Code06_RemoveDuplicateLettersLessLexi { + + // 在str中,每种字符都要保留一个,让最后的结果,字典序最小 ,并返回 + public static String removeDuplicateLetters1(String str) { + if (str == null || str.length() < 2) { + return str; + } + int[] map = new int[256]; + for (int i = 0; i < str.length(); i++) { + map[str.charAt(i)]++; + } + int minACSIndex = 0; + for (int i = 0; i < str.length(); i++) { + minACSIndex = str.charAt(minACSIndex) > str.charAt(i) ? i : minACSIndex; + if (--map[str.charAt(i)] == 0) { + break; + } + } + // 0...break(之前) minACSIndex + // str[minACSIndex] 剩下的字符串str[minACSIndex+1...] -> 去掉str[minACSIndex]字符 -> s' + // s'... + return String.valueOf(str.charAt(minACSIndex)) + removeDuplicateLetters1( + str.substring(minACSIndex + 1).replaceAll(String.valueOf(str.charAt(minACSIndex)), "")); + } + + public static String removeDuplicateLetters2(String s) { + char[] str = s.toCharArray(); + // 小写字母ascii码值范围[97~122],所以用长度为26的数组做次数统计 + // 如果map[i] > -1,则代表ascii码值为i的字符的出现次数 + // 如果map[i] == -1,则代表ascii码值为i的字符不再考虑 + int[] map = new int[26]; + for (int i = 0; i < str.length; i++) { + map[str[i] - 'a']++; + } + char[] res = new char[26]; + int index = 0; + int L = 0; + int R = 0; + while (R != str.length) { + // 如果当前字符是不再考虑的,直接跳过 + // 如果当前字符的出现次数减1之后,后面还能出现,直接跳过 + if (map[str[R] - 'a'] == -1 || --map[str[R] - 'a'] > 0) { + R++; + } else { // 当前字符需要考虑并且之后不会再出现了 + // 在str[L..R]上所有需要考虑的字符中,找到ascii码最小字符的位置 + int pick = -1; + for (int i = L; i <= R; i++) { + if (map[str[i] - 'a'] != -1 && (pick == -1 || str[i] < str[pick])) { + pick = i; + } + } + // 把ascii码最小的字符放到挑选结果中 + res[index++] = str[pick]; + // 在上一个的for循环中,str[L..R]范围上每种字符的出现次数都减少了 + // 需要把str[pick + 1..R]上每种字符的出现次数加回来 + for (int i = pick + 1; i <= R; i++) { + if (map[str[i] - 'a'] != -1) { // 只增加以后需要考虑字符的次数 + map[str[i] - 'a']++; + } + } + // 选出的ascii码最小的字符,以后不再考虑了 + map[str[pick] - 'a'] = -1; + // 继续在str[pick + 1......]上重复这个过程 + L = pick + 1; + R = L; + } + } + return String.valueOf(res, 0, index); + } + +} diff --git a/大厂刷题班/class25/Code01_IPToCIDR.java b/大厂刷题班/class25/Code01_IPToCIDR.java new file mode 100644 index 0000000..5cc59bd --- /dev/null +++ b/大厂刷题班/class25/Code01_IPToCIDR.java @@ -0,0 +1,90 @@ +package class25; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +// 本题测试链接 : https://leetcode.com/problems/ip-to-cidr/ +public class Code01_IPToCIDR { + + public static List ipToCIDR(String ip, int n) { + // ip -> 32位整数 + int cur = status(ip); + // cur这个状态,最右侧的1,能表示下2的几次方 + int maxPower = 0; + // 已经解决了多少ip了 + int solved = 0; + // 临时变量 + int power = 0; + List ans = new ArrayList<>(); + while (n > 0) { + // cur最右侧的1,能搞定2的几次方个ip + // cur : 000...000 01001000 + // 3 + maxPower = mostRightPower(cur); + // cur : 0000....0000 00001000 -> 2的3次方的问题 + // sol : 0000....0000 00000001 -> 1 2的0次方 + // sol : 0000....0000 00000010 -> 2 2的1次方 + // sol : 0000....0000 00000100 -> 4 2的2次方 + // sol : 0000....0000 00001000 -> 8 2的3次方 + solved = 1; + power = 0; + // 怕溢出 + // solved + while ((solved << 1) <= n && (power + 1) <= maxPower) { + solved <<= 1; + power++; + } + ans.add(content(cur, power)); + n -= solved; + cur += solved; + } + return ans; + } + + // ip -> int(32位状态) + public static int status(String ip) { + int ans = 0; + int move = 24; + for (String str : ip.split("\\.")) { + // 17.23.16.5 "17" "23" "16" "5" + // "17" -> 17 << 24 + // "23" -> 23 << 16 + // "16" -> 16 << 8 + // "5" -> 5 << 0 + ans |= Integer.valueOf(str) << move; + move -= 8; + } + return ans; + } + + public static HashMap map = new HashMap<>(); + // 1 000000....000000 -> 2的32次方 + + public static int mostRightPower(int num) { + // map只会生成1次,以后直接用 + if (map.isEmpty()) { + map.put(0, 32); + for (int i = 0; i < 32; i++) { + // 00...0000 00000001 2的0次方 + // 00...0000 00000010 2的1次方 + // 00...0000 00000100 2的2次方 + // 00...0000 00001000 2的3次方 + map.put(1 << i, i); + } + } + // num & (-num) -> num & (~num+1) -> 提取出最右侧的1 + return map.get(num & (-num)); + } + + public static String content(int status, int power) { + StringBuilder builder = new StringBuilder(); + for (int move = 24; move >= 0; move -= 8) { + builder.append(((status & (255 << move)) >>> move) + "."); + } + builder.setCharAt(builder.length() - 1, '/'); + builder.append(32 - power); + return builder.toString(); + } + +} diff --git a/大厂刷题班/class25/Code02_3Sum.java b/大厂刷题班/class25/Code02_3Sum.java new file mode 100644 index 0000000..363b4e3 --- /dev/null +++ b/大厂刷题班/class25/Code02_3Sum.java @@ -0,0 +1,70 @@ +package class25; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +// 本题测试链接 : https://leetcode.com/problems/3sum/ +public class Code02_3Sum { + + public static List> threeSum(int[] nums) { + Arrays.sort(nums); + int N = nums.length; + List> ans = new ArrayList<>(); + for (int i = N - 1; i > 1; i--) { // 三元组最后一个数,是arr[i] 之前....二元组 + arr[i] + if (i == N - 1 || nums[i] != nums[i + 1]) { + List> nexts = twoSum(nums, i - 1, -nums[i]); + for (List cur : nexts) { + cur.add(nums[i]); + ans.add(cur); + } + } + } + return ans; + } + + // nums[0...end]这个范围上,有多少个不同二元组,相加==target,全返回 + // {-1,5} K = 4 + // {1, 3} + public static List> twoSum(int[] nums, int end, int target) { + int L = 0; + int R = end; + List> ans = new ArrayList<>(); + while (L < R) { + if (nums[L] + nums[R] > target) { + R--; + } else if (nums[L] + nums[R] < target) { + L++; + } else { // nums[L] + nums[R] == target + if (L == 0 || nums[L - 1] != nums[L]) { + List cur = new ArrayList<>(); + cur.add(nums[L]); + cur.add(nums[R]); + ans.add(cur); + } + L++; + } + } + return ans; + } + + public static int findPairs(int[] nums, int k) { + Arrays.sort(nums); + int left = 0, right = 1; + int result = 0; + while (left < nums.length && right < nums.length) { + if (left == right || nums[right] - nums[left] < k) { + right++; + } else if (nums[right] - nums[left] > k) { + left++; + } else { + left++; + result++; + while (left < nums.length && nums[left] == nums[left - 1]) + left++; + } + } + return result; + } + +} diff --git a/大厂刷题班/class25/Code03_MaxPointsOnALine.java b/大厂刷题班/class25/Code03_MaxPointsOnALine.java new file mode 100644 index 0000000..e1599c7 --- /dev/null +++ b/大厂刷题班/class25/Code03_MaxPointsOnALine.java @@ -0,0 +1,68 @@ +package class25; + +import java.util.HashMap; +import java.util.Map; + +// 本题测试链接: https://leetcode.com/problems/max-points-on-a-line/ +public class Code03_MaxPointsOnALine { + + // [ + // [1,3] + // [4,9] + // [5,7] + // ] + + public static int maxPoints(int[][] points) { + if (points == null) { + return 0; + } + if (points.length <= 2) { + return points.length; + } + // key = 3 + // value = {7 , 10} -> 斜率为3/7的点 有10个 + // {5, 15} -> 斜率为3/5的点 有15个 + Map> map = new HashMap>(); + int result = 0; + for (int i = 0; i < points.length; i++) { + map.clear(); + int samePosition = 1; + int sameX = 0; + int sameY = 0; + int line = 0; + for (int j = i + 1; j < points.length; j++) { // i号点,和j号点,的斜率关系 + int x = points[j][0] - points[i][0]; + int y = points[j][1] - points[i][1]; + if (x == 0 && y == 0) { + samePosition++; + } else if (x == 0) { + sameX++; + } else if (y == 0) { + sameY++; + } else { // 普通斜率 + int gcd = gcd(x, y); + x /= gcd; + y /= gcd; + // x / y + if (!map.containsKey(x)) { + map.put(x, new HashMap()); + } + if (!map.get(x).containsKey(y)) { + map.get(x).put(y, 0); + } + map.get(x).put(y, map.get(x).get(y) + 1); + line = Math.max(line, map.get(x).get(y)); + } + } + result = Math.max(result, Math.max(Math.max(sameX, sameY), line) + samePosition); + } + return result; + } + + // 保证初始调用的时候,a和b不等于0 + // O(1) + public static int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } + +} diff --git a/大厂刷题班/class25/Code04_GasStation.java b/大厂刷题班/class25/Code04_GasStation.java new file mode 100644 index 0000000..cc044e6 --- /dev/null +++ b/大厂刷题班/class25/Code04_GasStation.java @@ -0,0 +1,104 @@ +package class25; + +// 本题测试链接 : https://leetcode.com/problems/gas-station/ +// 注意本题的实现比leetcode上的问法更加通用 +// leetcode只让返回其中一个良好出发点的位置 +// 本题是返回结果数组,每一个出发点是否是良好出发点都求出来了 +// 得到结果数组的过程,时间复杂度O(N),额外空间复杂度O(1) +public class Code04_GasStation { + + public static int canCompleteCircuit(int[] gas, int[] cost) { + if (gas == null || gas.length == 0) { + return -1; + } + if (gas.length == 1) { + return gas[0] < cost[0] ? -1 : 0; + } + boolean[] good = stations(cost, gas); + for (int i = 0; i < gas.length; i++) { + if (good[i]) { + return i; + } + } + return -1; + } + + public static boolean[] stations(int[] cost, int[] gas) { + if (cost == null || gas == null || cost.length < 2 || cost.length != gas.length) { + return null; + } + int init = changeDisArrayGetInit(cost, gas); + return init == -1 ? new boolean[cost.length] : enlargeArea(cost, init); + } + + public static int changeDisArrayGetInit(int[] dis, int[] oil) { + int init = -1; + for (int i = 0; i < dis.length; i++) { + dis[i] = oil[i] - dis[i]; + if (dis[i] >= 0) { + init = i; + } + } + return init; + } + + public static boolean[] enlargeArea(int[] dis, int init) { + boolean[] res = new boolean[dis.length]; + int start = init; + int end = nextIndex(init, dis.length); + int need = 0; + int rest = 0; + do { + // 当前来到的start已经在连通区域中,可以确定后续的开始点一定无法转完一圈 + if (start != init && start == lastIndex(end, dis.length)) { + break; + } + // 当前来到的start不在连通区域中,就扩充连通区域 + // start(5) -> 联通区的头部(7) -> 2 + // start(-2) -> 联通区的头部(7) -> 9 + if (dis[start] < need) { // 当前start无法接到连通区的头部 + need -= dis[start]; + } else { // 当前start可以接到连通区的头部,开始扩充连通区域的尾巴 + // start(7) -> 联通区的头部(5) + rest += dis[start] - need; + need = 0; + while (rest >= 0 && end != start) { + rest += dis[end]; + end = nextIndex(end, dis.length); + } + // 如果连通区域已经覆盖整个环,当前的start是良好出发点,进入2阶段 + if (rest >= 0) { + res[start] = true; + connectGood(dis, lastIndex(start, dis.length), init, res); + break; + } + } + start = lastIndex(start, dis.length); + } while (start != init); + return res; + } + + // 已知start的next方向上有一个良好出发点 + // start如果可以达到这个良好出发点,那么从start出发一定可以转一圈 + public static void connectGood(int[] dis, int start, int init, boolean[] res) { + int need = 0; + while (start != init) { + if (dis[start] < need) { + need -= dis[start]; + } else { + res[start] = true; + need = 0; + } + start = lastIndex(start, dis.length); + } + } + + public static int lastIndex(int index, int size) { + return index == 0 ? (size - 1) : index - 1; + } + + public static int nextIndex(int index, int size) { + return index == size - 1 ? 0 : (index + 1); + } + +} diff --git a/大厂刷题班/class26/Code01_MinRange.java b/大厂刷题班/class26/Code01_MinRange.java new file mode 100644 index 0000000..537cc65 --- /dev/null +++ b/大厂刷题班/class26/Code01_MinRange.java @@ -0,0 +1,119 @@ +package class26; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.TreeSet; + +public class Code01_MinRange { + + // 本题为求最小包含区间 + // 测试链接 : + // https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/ + public static class Node { + public int val; + public int arr; + public int idx; + + public Node(int value, int arrIndex, int index) { + val = value; + arr = arrIndex; + idx = index; + } + } + + public static class NodeComparator implements Comparator { + + @Override + public int compare(Node a, Node b) { + return a.val != b.val ? (a.val - b.val) : (a.arr - b.arr); + } + + } + + public static int[] smallestRange(List> nums) { + int N = nums.size(); + TreeSet set = new TreeSet<>(new NodeComparator()); + for (int i = 0; i < N; i++) { + set.add(new Node(nums.get(i).get(0), i, 0)); + } + int r = Integer.MAX_VALUE; + int a = 0; + int b = 0; + while (set.size() == N) { + Node max = set.last(); + Node min = set.pollFirst(); + if (max.val - min.val < r) { + r = max.val - min.val; + a = min.val; + b = max.val; + } + if (min.idx < nums.get(min.arr).size() - 1) { + set.add(new Node(nums.get(min.arr).get(min.idx + 1), min.arr, min.idx + 1)); + } + } + return new int[] { a, b }; + } + + // 以下为课堂题目,在main函数里有对数器 + public static int minRange1(int[][] m) { + int min = Integer.MAX_VALUE; + for (int i = 0; i < m[0].length; i++) { + for (int j = 0; j < m[1].length; j++) { + for (int k = 0; k < m[2].length; k++) { + min = Math.min(min, + Math.abs(m[0][i] - m[1][j]) + Math.abs(m[1][j] - m[2][k]) + Math.abs(m[2][k] - m[0][i])); + } + } + } + return min; + } + + public static int minRange2(int[][] matrix) { + int N = matrix.length; + TreeSet set = new TreeSet<>(new NodeComparator()); + for (int i = 0; i < N; i++) { + set.add(new Node(matrix[i][0], i, 0)); + } + int min = Integer.MAX_VALUE; + while (set.size() == N) { + min = Math.min(min, set.last().val - set.first().val); + Node cur = set.pollFirst(); + if (cur.idx < matrix[cur.arr].length - 1) { + set.add(new Node(matrix[cur.arr][cur.idx + 1], cur.arr, cur.idx + 1)); + } + } + return min << 1; + } + + public static int[][] generateRandomMatrix(int n, int v) { + int[][] m = new int[3][]; + int s = 0; + for (int i = 0; i < 3; i++) { + s = (int) (Math.random() * n) + 1; + m[i] = new int[s]; + for (int j = 0; j < s; j++) { + m[i][j] = (int) (Math.random() * v); + } + Arrays.sort(m[i]); + } + return m; + } + + public static void main(String[] args) { + int n = 20; + int v = 200; + int t = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < t; i++) { + int[][] m = generateRandomMatrix(n, v); + int ans1 = minRange1(m); + int ans2 = minRange2(m); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class26/Code02_WordSearchII.java b/大厂刷题班/class26/Code02_WordSearchII.java new file mode 100644 index 0000000..fcfaeec --- /dev/null +++ b/大厂刷题班/class26/Code02_WordSearchII.java @@ -0,0 +1,122 @@ +package class26; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +// 本题测试链接 : https://leetcode.com/problems/word-search-ii/ +public class Code02_WordSearchII { + + public static class TrieNode { + public TrieNode[] nexts; + public int pass; + public boolean end; + + public TrieNode() { + nexts = new TrieNode[26]; + pass = 0; + end = false; + } + + } + + public static void fillWord(TrieNode head, String word) { + head.pass++; + char[] chs = word.toCharArray(); + int index = 0; + TrieNode node = head; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new TrieNode(); + } + node = node.nexts[index]; + node.pass++; + } + node.end = true; + } + + public static String generatePath(LinkedList path) { + char[] str = new char[path.size()]; + int index = 0; + for (Character cha : path) { + str[index++] = cha; + } + return String.valueOf(str); + } + + public static List findWords(char[][] board, String[] words) { + TrieNode head = new TrieNode(); // 前缀树最顶端的头 + HashSet set = new HashSet<>(); + for (String word : words) { + if (!set.contains(word)) { + fillWord(head, word); + set.add(word); + } + } + // 答案 + List ans = new ArrayList<>(); + // 沿途走过的字符,收集起来,存在path里 + LinkedList path = new LinkedList<>(); + for (int row = 0; row < board.length; row++) { + for (int col = 0; col < board[0].length; col++) { + // 枚举在board中的所有位置 + // 每一个位置出发的情况下,答案都收集 + process(board, row, col, path, head, ans); + } + } + return ans; + } + + // 从board[row][col]位置的字符出发, + // 之前的路径上,走过的字符,记录在path里 + // cur还没有登上,有待检查能不能登上去的前缀树的节点 + // 如果找到words中的某个str,就记录在 res里 + // 返回值,从row,col 出发,一共找到了多少个str + public static int process( + char[][] board, int row, int col, + LinkedList path, TrieNode cur, + List res) { + char cha = board[row][col]; + if (cha == 0) { // 这个row col位置是之前走过的位置 + return 0; + } + // (row,col) 不是回头路 cha 有效 + + int index = cha - 'a'; + // 如果没路,或者这条路上最终的字符串之前加入过结果里 + if (cur.nexts[index] == null || cur.nexts[index].pass == 0) { + return 0; + } + // 没有走回头路且能登上去 + cur = cur.nexts[index]; + path.addLast(cha);// 当前位置的字符加到路径里去 + int fix = 0; // 从row和col位置出发,后续一共搞定了多少答案 + // 当我来到row col位置,如果决定不往后走了。是不是已经搞定了某个字符串了 + if (cur.end) { + res.add(generatePath(path)); + cur.end = false; + fix++; + } + // 往上、下、左、右,四个方向尝试 + board[row][col] = 0; + if (row > 0) { + fix += process(board, row - 1, col, path, cur, res); + } + if (row < board.length - 1) { + fix += process(board, row + 1, col, path, cur, res); + } + if (col > 0) { + fix += process(board, row, col - 1, path, cur, res); + } + if (col < board[0].length - 1) { + fix += process(board, row, col + 1, path, cur, res); + } + board[row][col] = cha; + path.pollLast(); + cur.pass -= fix; + return fix; + } + +} diff --git a/大厂刷题班/class26/Code03_ExpressionAddOperators.java b/大厂刷题班/class26/Code03_ExpressionAddOperators.java new file mode 100644 index 0000000..2422591 --- /dev/null +++ b/大厂刷题班/class26/Code03_ExpressionAddOperators.java @@ -0,0 +1,65 @@ +package class26; + +import java.util.LinkedList; +import java.util.List; + +// 本题测试链接 : https://leetcode.com/problems/expression-add-operators/ +public class Code03_ExpressionAddOperators { + + public static List addOperators(String num, int target) { + List ret = new LinkedList<>(); + if (num.length() == 0) { + return ret; + } + // 沿途的数字拷贝和+ - * 的决定,放在path里 + char[] path = new char[num.length() * 2 - 1]; + // num -> char[] + char[] digits = num.toCharArray(); + long n = 0; + for (int i = 0; i < digits.length; i++) { // 尝试0~i前缀作为第一部分 + n = n * 10 + digits[i] - '0'; + path[i] = digits[i]; + dfs(ret, path, i + 1, 0, n, digits, i + 1, target); // 后续过程 + if (n == 0) { + break; + } + } + return ret; + } + + // char[] digits 固定参数,字符类型数组,等同于num + // int target 目标 + // char[] path 之前做的决定,已经从左往右依次填写的字符在其中,可能含有'0'~'9' 与 * - + + // int len path[0..len-1]已经填写好,len是终止 + // int pos 字符类型数组num, 使用到了哪 + // left -> 前面固定的部分 cur -> 前一块 + // 默认 left + cur ... + public static void dfs(List res, char[] path, int len, + long left, long cur, + char[] num, int index, int aim) { + if (index == num.length) { + if (left + cur == aim) { + res.add(new String(path, 0, len)); + } + return; + } + long n = 0; + int j = len + 1; + for (int i = index; i < num.length; i++) { // pos ~ i + // 试每一个可能的前缀,作为第一个数字! + // num[index...i] 作为第一个数字! + n = n * 10 + num[i] - '0'; + path[j++] = num[i]; + path[len] = '+'; + dfs(res, path, j, left + cur, n, num, i + 1, aim); + path[len] = '-'; + dfs(res, path, j, left + cur, -n, num, i + 1, aim); + path[len] = '*'; + dfs(res, path, j, left, cur * n, num, i + 1, aim); + if (num[index] == '0') { + break; + } + } + } + +} diff --git a/大厂刷题班/class26/Code04_WordLadderII.java b/大厂刷题班/class26/Code04_WordLadderII.java new file mode 100644 index 0000000..d55ad1e --- /dev/null +++ b/大厂刷题班/class26/Code04_WordLadderII.java @@ -0,0 +1,100 @@ +package class26; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +// 本题测试链接 : https://leetcode.com/problems/word-ladder-ii/ +public class Code04_WordLadderII { + + public static List> findLadders(String start, String end, List list) { + list.add(start); + HashMap> nexts = getNexts(list); + HashMap fromDistances = getDistances(start, nexts); + List> res = new ArrayList<>(); + if (!fromDistances.containsKey(end)) { + return res; + } + HashMap toDistances = getDistances(end, nexts); + LinkedList pathList = new LinkedList<>(); + getShortestPaths(start, end, nexts, fromDistances, toDistances, pathList, res); + return res; + } + + public static HashMap> getNexts(List words) { + HashSet dict = new HashSet<>(words); + HashMap> nexts = new HashMap<>(); + for (int i = 0; i < words.size(); i++) { + nexts.put(words.get(i), getNext(words.get(i), dict)); + } + return nexts; + } + + // word, 在表中,有哪些邻居,把邻居们,生成list返回 + public static List getNext(String word, HashSet dict) { + ArrayList res = new ArrayList(); + char[] chs = word.toCharArray(); + for (char cur = 'a'; cur <= 'z'; cur++) { + for (int i = 0; i < chs.length; i++) { + if (chs[i] != cur) { + char tmp = chs[i]; + chs[i] = cur; + if (dict.contains(String.valueOf(chs))) { + res.add(String.valueOf(chs)); + } + chs[i] = tmp; + } + } + } + return res; + } + + // 生成距离表,从start开始,根据邻居表,宽度优先遍历,对于能够遇到的所有字符串,生成(字符串,距离)这条记录,放入距离表中 + public static HashMap getDistances(String start, HashMap> nexts) { + HashMap distances = new HashMap<>(); + distances.put(start, 0); + Queue queue = new LinkedList<>(); + queue.add(start); + HashSet set = new HashSet<>(); + set.add(start); + while (!queue.isEmpty()) { + String cur = queue.poll(); + for (String next : nexts.get(cur)) { + if (!set.contains(next)) { + distances.put(next, distances.get(cur) + 1); + queue.add(next); + set.add(next); + } + } + } + return distances; + } + + // cur 当前来到的字符串 可变 + // to 目标,固定参数 + // nexts 每一个字符串的邻居表 + // cur 到开头距离5 -> 到开头距离是6的支路 fromDistances距离表 + // cur 到结尾距离5 -> 到开头距离是4的支路 toDistances距离表 + // path : 来到cur之前,深度优先遍历之前的历史是什么 + // res : 当遇到cur,把历史,放入res,作为一个结果 + public static void getShortestPaths(String cur, String to, HashMap> nexts, + HashMap fromDistances, HashMap toDistances, LinkedList path, + List> res) { + path.add(cur); + if (to.equals(cur)) { + res.add(new LinkedList(path)); + } else { + for (String next : nexts.get(cur)) { + if (fromDistances.get(next) == fromDistances.get(cur) + 1 + && toDistances.get(next) == toDistances.get(cur) - 1) { + getShortestPaths(next, to, nexts, fromDistances, toDistances, path, res); + } + } + } + path.pollLast(); + } + +} diff --git a/大厂刷题班/class27/Code01_PickBands.java b/大厂刷题班/class27/Code01_PickBands.java new file mode 100644 index 0000000..eaaa435 --- /dev/null +++ b/大厂刷题班/class27/Code01_PickBands.java @@ -0,0 +1,221 @@ +package class27; + +import java.util.Arrays; + +public class Code01_PickBands { + + // 每一个项目都有三个数,[a,b,c]表示这个项目a和b乐队参演,花费为c + // 每一个乐队可能在多个项目里都出现了,但是只能挑一次 + // nums是可以挑选的项目数量,所以一定会有nums*2只乐队被挑选出来 + // 乐队的全部数量一定是nums*2,且标号一定是0 ~ nums*2-1 + // 返回一共挑nums轮(也就意味着一定请到所有的乐队),最少花费是多少? + public static int minCost(int[][] programs, int nums) { + if (nums == 0 || programs == null || programs.length == 0) { + return 0; + } + int size = clean(programs); + int[] map1 = init(1 << (nums << 1)); + int[] map2 = null; + if ((nums & 1) == 0) { + // nums = 8 , 4 + f(programs, size, 0, 0, 0, nums >> 1, map1); + map2 = map1; + } else { + // nums == 7 4 -> map1 3 -> map2 + f(programs, size, 0, 0, 0, nums >> 1, map1); + map2 = init(1 << (nums << 1)); + f(programs, size, 0, 0, 0, nums - (nums >> 1), map2); + } + // 16 mask : 0..00 1111.1111(16个) + // 12 mask : 0..00 1111.1111(12个) + int mask = (1 << (nums << 1)) - 1; + int ans = Integer.MAX_VALUE; + for (int i = 0; i < map1.length; i++) { + if (map1[i] != Integer.MAX_VALUE && map2[mask & (~i)] != Integer.MAX_VALUE) { + ans = Math.min(ans, map1[i] + map2[mask & (~i)]); + } + } + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + // [ + // [9, 1, 100] + // [2, 9 , 50] + // ... + // ] + public static int clean(int[][] programs) { + int x = 0; + int y = 0; + for (int[] p : programs) { + x = Math.min(p[0], p[1]); + y = Math.max(p[0], p[1]); + p[0] = x; + p[1] = y; + } + Arrays.sort(programs, (a, b) -> a[0] != b[0] ? (a[0] - b[0]) : (a[1] != b[1] ? (a[1] - b[1]) : (a[2] - b[2]))); + x = programs[0][0]; + y = programs[0][1]; + int n = programs.length; + // (0, 1, ? ) + for (int i = 1; i < n; i++) { + if (programs[i][0] == x && programs[i][1] == y) { + programs[i] = null; + } else { + x = programs[i][0]; + y = programs[i][1]; + } + } + int size = 1; + for (int i = 1; i < n; i++) { + if (programs[i] != null) { + programs[size++] = programs[i]; + } + } + // programs[0...size-1] + return size; + } + + public static int[] init(int size) { + int[] arr = new int[size]; + for (int i = 0; i < size; i++) { + arr[i] = Integer.MAX_VALUE; + } + return arr; + } + + public static void f(int[][] programs, int size, int index, int status, int cost, int rest, int[] map) { + if (rest == 0) { + map[status] = Math.min(map[status], cost); + } else { + if (index != size) { + f(programs, size, index + 1, status, cost, rest, map); + int pick = 0 | (1 << programs[index][0]) | (1 << programs[index][1]); + if ((pick & status) == 0) { + f(programs, size, index + 1, status | pick, cost + programs[index][2], rest - 1, map); + } + } + } + } + +// // 如果nums,2 * nums 只乐队 +// // (1 << (nums << 1)) - 1 +// // programs 洗数据 size +// // nums = 8 16只乐队 +// +// // process(programs, size, (1 << (nums << 1)) - 1, 0, 4, 0, 0) +// +// public static int minCost = Integer.MAX_VALUE; +// +// public static int[] map = new int[1 << 16]; // map初始化全变成系统最大 +// +// +// +// public static void process(int[][] programs, int size, int index, int rest, int pick, int cost) { +// if (rest == 0) { +// +// map[pick] = Math.min(map[pick], cost); +// +// } else { // 还有项目可挑 +// if (index != size) { +// // 不考虑当前的项目!programs[index]; +// process(programs, size, index + 1, rest, pick, cost); +// // 考虑当前的项目!programs[index]; +// int x = programs[index][0]; +// int y = programs[index][1]; +// int cur = (1 << x) | (1 << y); +// if ((pick & cur) == 0) { // 终于可以考虑了! +// process(programs, size, index + 1, rest - 1, pick | cur, cost + programs[index][2]); +// } +// } +// } +// } +// +// +// public static void zuo(int[] arr, int index, int rest) { +// if(rest == 0) { +// 停止 +// } +// if(index != arr.length) { +// zuo(arr, index + 1, rest); +// zuo(arr, index + 1, rest - 1); +// } +// } + + // 为了测试 + public static int right(int[][] programs, int nums) { + min = Integer.MAX_VALUE; + r(programs, 0, nums, 0, 0); + return min == Integer.MAX_VALUE ? -1 : min; + } + + public static int min = Integer.MAX_VALUE; + + public static void r(int[][] programs, int index, int rest, int pick, int cost) { + if (rest == 0) { + min = Math.min(min, cost); + } else { + if (index < programs.length) { + r(programs, index + 1, rest, pick, cost); + int cur = (1 << programs[index][0]) | (1 << programs[index][1]); + if ((pick & cur) == 0) { + r(programs, index + 1, rest - 1, pick | cur, cost + programs[index][2]); + } + } + } + } + + // 为了测试 + public static int[][] randomPrograms(int N, int V) { + int nums = N << 1; + int n = nums * (nums - 1); + int[][] programs = new int[n][3]; + for (int i = 0; i < n; i++) { + int a = (int) (Math.random() * nums); + int b = 0; + do { + b = (int) (Math.random() * nums); + } while (b == a); + programs[i][0] = a; + programs[i][1] = b; + programs[i][2] = (int) (Math.random() * V) + 1; + } + return programs; + } + + // 为了测试 + public static void main(String[] args) { + int N = 4; + int V = 100; + int T = 10000; + System.out.println("测试开始"); + for (int i = 0; i < T; i++) { + int nums = (int) (Math.random() * N) + 1; + int[][] programs = randomPrograms(nums, V); + int ans1 = right(programs, nums); + int ans2 = minCost(programs, nums); + if (ans1 != ans2) { + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + + long start; + long end; + int[][] programs; + + programs = randomPrograms(7, V); + start = System.currentTimeMillis(); + right(programs, 7); + end = System.currentTimeMillis(); + System.out.println("right方法,在nums=7时候的运行时间(毫秒) : " + (end - start)); + + programs = randomPrograms(10, V); + start = System.currentTimeMillis(); + minCost(programs, 10); + end = System.currentTimeMillis(); + System.out.println("minCost方法,在nums=10时候的运行时间(毫秒) : " + (end - start)); + + } + +} diff --git a/大厂刷题班/class27/Code02_MinPeople.java b/大厂刷题班/class27/Code02_MinPeople.java new file mode 100644 index 0000000..64dc565 --- /dev/null +++ b/大厂刷题班/class27/Code02_MinPeople.java @@ -0,0 +1,61 @@ +package class27; + +import java.util.Arrays; + +public class Code02_MinPeople { + +// 三道题如下: +// 微信面试题 +// 题目一: +// 股民小 A 有一天穿越回到了 n 天前,他能记住某只股票连续 n 天的价格;他手上有足够多的启动资金,他可以在这 n 天内多次交易,但是有个限制 +// 如果已经购买了一个股票,在卖出它之前就不能再继续购买股票了。 +// 到 n 天之后,小 A 不能再持有股票。 +// 求问 这 n 天内,小 A 能够获得的利润的最大值 +// 如果不需要手续费的话,求最大的利润 +// function(number) { +// return number +// } +// 输入: prices = [3, 1, 2, 8, 5, 9] +// 输出: 11 +// +// 题目二: +// 企鹅厂每年都会发文化衫,文化衫有很多种,厂庆的时候,企鹅们都需要穿文化衫来拍照 +// 一次采访中,记者随机遇到的企鹅,企鹅会告诉记者还有多少企鹅跟他穿一种文化衫 +// 我们将这些回答放在 answers 数组里,返回鹅厂中企鹅的最少数量。 +// 输入: answers = [1] 输出:2 +// 输入: answers = [1, 1, 2] 输出:5 +// Leetcode题目:https://leetcode.com/problems/rabbits-in-forest/ +// +// 题目三: +// WXG 的秘书有一堆的文件袋,现在秘书需要把文件袋嵌套收纳起来。请你帮他计算下,最大嵌套数量。 +// 给你一个二维整数数组 folders ,其中 folders[i] = [wi, hi] ,表示第 i 个文件袋的宽度和长度 +// 当某一个文件袋的宽度和长度都比这个另外一个文件袋大的时候,前者就能把后者装起来,称之为一组文件袋。 +// 请计算,最大的一组文件袋的数量是多少。 +// 实例 +// 输入:[[6,10],[11,14],[6,1],[16,14],[13,2]] +// 输出: 3 + + // 题目一,股票系列专题,大厂刷题班15节 + // 题目三,最长递增子序列专题,大厂刷题班第9节 + // 我们来讲一下题目二 + public static int numRabbits(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + Arrays.sort(arr); + int x = arr[0]; + int c = 1; + int ans = 0; + for (int i = 1; i < arr.length; i++) { + if (x != arr[i]) { + ans += ((c + x) / (x + 1)) * (x + 1); + x = arr[i]; + c = 1; + } else { + c++; + } + } + return ans + ((c + x) / (x + 1)) * (x + 1); + } + +} diff --git a/大厂刷题班/class27/Problem_0001_TwoSum.java b/大厂刷题班/class27/Problem_0001_TwoSum.java new file mode 100644 index 0000000..d1c4619 --- /dev/null +++ b/大厂刷题班/class27/Problem_0001_TwoSum.java @@ -0,0 +1,19 @@ +package class27; + +import java.util.HashMap; + +public class Problem_0001_TwoSum { + + public static int[] twoSum(int[] nums, int target) { + // key 某个之前的数 value 这个数出现的位置 + HashMap map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + return new int[] { map.get(target - nums[i]), i }; + } + map.put(nums[i], i); + } + return new int[] { -1, -1 }; + } + +} diff --git a/大厂刷题班/class27/Problem_0007_ReverseInteger.java b/大厂刷题班/class27/Problem_0007_ReverseInteger.java new file mode 100644 index 0000000..a26a5dc --- /dev/null +++ b/大厂刷题班/class27/Problem_0007_ReverseInteger.java @@ -0,0 +1,21 @@ +package class27; + +public class Problem_0007_ReverseInteger { + + public static int reverse(int x) { + boolean neg = ((x >>> 31) & 1) == 1; + x = neg ? x : -x; + int m = Integer.MIN_VALUE / 10; + int o = Integer.MIN_VALUE % 10; + int res = 0; + while (x != 0) { + if (res < m || (res == m && x % 10 < o)) { + return 0; + } + res = res * 10 + x % 10; + x /= 10; + } + return neg ? res : Math.abs(res); + } + +} diff --git a/大厂刷题班/class27/说明 b/大厂刷题班/class27/说明 new file mode 100644 index 0000000..dc0c1f5 --- /dev/null +++ b/大厂刷题班/class27/说明 @@ -0,0 +1,12 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0001 : 大厂刷题班, 第27节, 本节 +0002 : 算法新手班, 第4节第5题 +0003 : 大厂刷题班, 第3节第1题 +0004 : 大厂刷题班, 第12节第3题 +0005 : 体系学习班, Manacher算法 +0007 : 大厂刷题班, 第27节, 本节 \ No newline at end of file diff --git a/大厂刷题班/class28/Problem_0008_StringToInteger.java b/大厂刷题班/class28/Problem_0008_StringToInteger.java new file mode 100644 index 0000000..ecab645 --- /dev/null +++ b/大厂刷题班/class28/Problem_0008_StringToInteger.java @@ -0,0 +1,80 @@ +package class28; + +public class Problem_0008_StringToInteger { + + public static int myAtoi(String s) { + if (s == null || s.equals("")) { + return 0; + } + s = removeHeadZero(s.trim()); + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + if (!isValid(str)) { + return 0; + } + // str 是符合日常书写的,正经整数形式 + boolean posi = str[0] == '-' ? false : true; + int minq = Integer.MIN_VALUE / 10; + int minr = Integer.MIN_VALUE % 10; + int res = 0; + int cur = 0; + for (int i = (str[0] == '-' || str[0] == '+') ? 1 : 0; i < str.length; i++) { + // 3 cur = -3 '5' cur = -5 '0' cur = 0 + cur = '0' - str[i]; + if ((res < minq) || (res == minq && cur < minr)) { + return posi ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + res = res * 10 + cur; + } + // res 负 + if (posi && res == Integer.MIN_VALUE) { + return Integer.MAX_VALUE; + } + return posi ? -res : res; + } + + public static String removeHeadZero(String str) { + boolean r = (str.startsWith("+") || str.startsWith("-")); + int s = r ? 1 : 0; + for (; s < str.length(); s++) { + if (str.charAt(s) != '0') { + break; + } + } + // s 到了第一个不是'0'字符的位置 + int e = -1; + // 左<-右 + for (int i = str.length() - 1; i >= (r ? 1 : 0); i--) { + if (str.charAt(i) < '0' || str.charAt(i) > '9') { + e = i; + } + } + // e 到了最左的 不是数字字符的位置 + return (r ? String.valueOf(str.charAt(0)) : "") + str.substring(s, e == -1 ? str.length() : e); + } + + public static boolean isValid(char[] chas) { + if (chas[0] != '-' && chas[0] != '+' && (chas[0] < '0' || chas[0] > '9')) { + return false; + } + if ((chas[0] == '-' || chas[0] == '+') && chas.length == 1) { + return false; + } + // 0 +... -... num + for (int i = 1; i < chas.length; i++) { + if (chas[i] < '0' || chas[i] > '9') { + return false; + } + } + return true; + } + + public static void main(String[] args) { + System.out.println(Integer.MAX_VALUE); + } + + + +} diff --git a/大厂刷题班/class28/Problem_0012_IntegerToRoman.java b/大厂刷题班/class28/Problem_0012_IntegerToRoman.java new file mode 100644 index 0000000..d495efc --- /dev/null +++ b/大厂刷题班/class28/Problem_0012_IntegerToRoman.java @@ -0,0 +1,20 @@ +package class28; + +public class Problem_0012_IntegerToRoman { + + public static String intToRoman(int num) { + String[][] c = { + { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }, + { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }, + { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }, + { "", "M", "MM", "MMM" } }; + StringBuilder roman = new StringBuilder(); + roman + .append(c[3][num / 1000 % 10]) + .append(c[2][num / 100 % 10]) + .append(c[1][num / 10 % 10]) + .append(c[0][num % 10]); + return roman.toString(); + } + +} diff --git a/大厂刷题班/class28/Problem_0013_RomanToInteger.java b/大厂刷题班/class28/Problem_0013_RomanToInteger.java new file mode 100644 index 0000000..d8ae1bc --- /dev/null +++ b/大厂刷题班/class28/Problem_0013_RomanToInteger.java @@ -0,0 +1,45 @@ +package class28; + +public class Problem_0013_RomanToInteger { + + public static int romanToInt(String s) { + // C M X C I X + // 100 1000 10 100 1 10 + int nums[] = new int[s.length()]; + for (int i = 0; i < s.length(); i++) { + switch (s.charAt(i)) { + case 'M': + nums[i] = 1000; + break; + case 'D': + nums[i] = 500; + break; + case 'C': + nums[i] = 100; + break; + case 'L': + nums[i] = 50; + break; + case 'X': + nums[i] = 10; + break; + case 'V': + nums[i] = 5; + break; + case 'I': + nums[i] = 1; + break; + } + } + int sum = 0; + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] < nums[i + 1]) { + sum -= nums[i]; + } else { + sum += nums[i]; + } + } + return sum + nums[nums.length - 1]; + } + +} diff --git a/大厂刷题班/class28/Problem_0014_LongestCommonPrefix.java b/大厂刷题班/class28/Problem_0014_LongestCommonPrefix.java new file mode 100644 index 0000000..0ee811c --- /dev/null +++ b/大厂刷题班/class28/Problem_0014_LongestCommonPrefix.java @@ -0,0 +1,28 @@ +package class28; + +public class Problem_0014_LongestCommonPrefix { + + public static String longestCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + char[] chs = strs[0].toCharArray(); + int min = Integer.MAX_VALUE; + for (String str : strs) { + char[] tmp = str.toCharArray(); + int index = 0; + while (index < tmp.length && index < chs.length) { + if (chs[index] != tmp[index]) { + break; + } + index++; + } + min = Math.min(index, min); + if (min == 0) { + return ""; + } + } + return strs[0].substring(0, min); + } + +} diff --git a/大厂刷题班/class28/Problem_0017_LetterCombinationsOfAPhoneNumber.java b/大厂刷题班/class28/Problem_0017_LetterCombinationsOfAPhoneNumber.java new file mode 100644 index 0000000..14fb701 --- /dev/null +++ b/大厂刷题班/class28/Problem_0017_LetterCombinationsOfAPhoneNumber.java @@ -0,0 +1,43 @@ +package class28; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0017_LetterCombinationsOfAPhoneNumber { + + public static char[][] phone = { + { 'a', 'b', 'c' }, // 2 0 + { 'd', 'e', 'f' }, // 3 1 + { 'g', 'h', 'i' }, // 4 2 + { 'j', 'k', 'l' }, // 5 3 + { 'm', 'n', 'o' }, // 6 + { 'p', 'q', 'r', 's' }, // 7 + { 't', 'u', 'v' }, // 8 + { 'w', 'x', 'y', 'z' }, // 9 + }; + + // "23" + public static List letterCombinations(String digits) { + List ans = new ArrayList<>(); + if (digits == null || digits.length() == 0) { + return ans; + } + char[] str = digits.toCharArray(); + char[] path = new char[str.length]; + process(str, 0, path, ans); + return ans; + } + + public static void process(char[] str, int index, char[] path, List ans) { + if (index == str.length) { + ans.add(String.valueOf(path)); + } else { + char[] cands = phone[str[index] - '2']; + for (char cur : cands) { + path[index] = cur; + process(str, index + 1, path, ans); + } + } + } + +} diff --git a/大厂刷题班/class28/Problem_0019_RemoveNthNodeFromEndofList.java b/大厂刷题班/class28/Problem_0019_RemoveNthNodeFromEndofList.java new file mode 100644 index 0000000..97cdc29 --- /dev/null +++ b/大厂刷题班/class28/Problem_0019_RemoveNthNodeFromEndofList.java @@ -0,0 +1,33 @@ +package class28; + +public class Problem_0019_RemoveNthNodeFromEndofList { + + public static class ListNode { + public int val; + public ListNode next; + } + + public static ListNode removeNthFromEnd(ListNode head, int n) { + ListNode cur = head; + ListNode pre = null; + while (cur != null) { + n--; + if (n == -1) { + pre = head; + } + if (n < -1) { + pre = pre.next; + } + cur = cur.next; + } + if (n > 0) { + return head; + } + if (pre == null) { + return head.next; + } + pre.next = pre.next.next; + return head; + } + +} diff --git a/大厂刷题班/class28/Problem_0020_ValidParentheses.java b/大厂刷题班/class28/Problem_0020_ValidParentheses.java new file mode 100644 index 0000000..1914ead --- /dev/null +++ b/大厂刷题班/class28/Problem_0020_ValidParentheses.java @@ -0,0 +1,30 @@ +package class28; + +public class Problem_0020_ValidParentheses { + + public static boolean isValid(String s) { + if (s == null || s.length() == 0) { + return true; + } + char[] str = s.toCharArray(); + int N = str.length; + char[] stack = new char[N]; + int size = 0; + for (int i = 0; i < N; i++) { + char cha = str[i]; + if (cha == '(' || cha == '[' || cha == '{') { + stack[size++] = cha == '(' ? ')' : (cha == '[' ? ']' : '}'); + } else { + if (size == 0) { + return false; + } + char last = stack[--size]; + if (cha != last) { + return false; + } + } + } + return size == 0; + } + +} diff --git a/大厂刷题班/class28/Problem_0022_GenerateParentheses.java b/大厂刷题班/class28/Problem_0022_GenerateParentheses.java new file mode 100644 index 0000000..7ca9625 --- /dev/null +++ b/大厂刷题班/class28/Problem_0022_GenerateParentheses.java @@ -0,0 +1,71 @@ +package class28; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0022_GenerateParentheses { + + public static List generateParenthesis(int n) { + char[] path = new char[n << 1]; + List ans = new ArrayList<>(); + process(path, 0, 0, n, ans); + return ans; + } + + + // path 做的决定 path[0....index-1]做完决定的! + // path[index.....] 还没做决定,当前轮到index位置做决定! + + public static void process(char[] path, int index, int leftMinusRight, int leftRest, List ans) { + if (index == path.length) { + ans.add(String.valueOf(path)); + } else { + // index ( ) + if (leftRest > 0) { + path[index] = '('; + process(path, index + 1, leftMinusRight + 1, leftRest - 1, ans); + } + if (leftMinusRight > 0) { + path[index] = ')'; + process(path, index + 1, leftMinusRight - 1, leftRest, ans); + } + } + } + + // 不剪枝的做法 + public static List generateParenthesis2(int n) { + char[] path = new char[n << 1]; + List ans = new ArrayList<>(); + process2(path, 0, ans); + return ans; + } + + public static void process2(char[] path, int index, List ans) { + if (index == path.length) { + if (isValid(path)) { + ans.add(String.valueOf(path)); + } + } else { + path[index] = '('; + process2(path, index + 1, ans); + path[index] = ')'; + process2(path, index + 1, ans); + } + } + + public static boolean isValid(char[] path) { + int count = 0; + for (char cha : path) { + if (cha == '(') { + count++; + } else { + count--; + } + if (count < 0) { + return false; + } + } + return count == 0; + } + +} diff --git a/大厂刷题班/class28/Problem_0026_RemoveDuplicatesFromSortedArray.java b/大厂刷题班/class28/Problem_0026_RemoveDuplicatesFromSortedArray.java new file mode 100644 index 0000000..fc2e419 --- /dev/null +++ b/大厂刷题班/class28/Problem_0026_RemoveDuplicatesFromSortedArray.java @@ -0,0 +1,21 @@ +package class28; + +public class Problem_0026_RemoveDuplicatesFromSortedArray { + + public static int removeDuplicates(int[] nums) { + if (nums == null) { + return 0; + } + if (nums.length < 2) { + return nums.length; + } + int done = 0; + for (int i = 1; i < nums.length; i++) { + if (nums[i] != nums[done]) { + nums[++done] = nums[i]; + } + } + return done + 1; + } + +} diff --git a/大厂刷题班/class28/Problem_0034_FindFirstAndLastPositionOfElementInSortedArray.java b/大厂刷题班/class28/Problem_0034_FindFirstAndLastPositionOfElementInSortedArray.java new file mode 100644 index 0000000..92be1c0 --- /dev/null +++ b/大厂刷题班/class28/Problem_0034_FindFirstAndLastPositionOfElementInSortedArray.java @@ -0,0 +1,33 @@ +package class28; + +public class Problem_0034_FindFirstAndLastPositionOfElementInSortedArray { + + public static int[] searchRange(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return new int[] { -1, -1 }; + } + int L = lessMostRight(nums, target) + 1; + if (L == nums.length || nums[L] != target) { + return new int[] { -1, -1 }; + } + return new int[] { L, lessMostRight(nums, target + 1) }; + } + + public static int lessMostRight(int[] arr, int num) { + int L = 0; + int R = arr.length - 1; + int M = 0; + int ans = -1; + while (L <= R) { + M = L + ((R - L) >> 1); + if (arr[M] < num) { + ans = M; + L = M + 1; + } else { + R = M - 1; + } + } + return ans; + } + +} diff --git a/大厂刷题班/class28/Problem_0036_ValidSudoku.java b/大厂刷题班/class28/Problem_0036_ValidSudoku.java new file mode 100644 index 0000000..4a89cf5 --- /dev/null +++ b/大厂刷题班/class28/Problem_0036_ValidSudoku.java @@ -0,0 +1,26 @@ +package class28; + +public class Problem_0036_ValidSudoku { + + public static boolean isValidSudoku(char[][] board) { + boolean[][] row = new boolean[9][10]; + boolean[][] col = new boolean[9][10]; + boolean[][] bucket = new boolean[9][10]; + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + int bid = 3 * (i / 3) + (j / 3); + if (board[i][j] != '.') { + int num = board[i][j] - '0'; + if (row[i][num] || col[j][num] || bucket[bid][num]) { + return false; + } + row[i][num] = true; + col[j][num] = true; + bucket[bid][num] = true; + } + } + } + return true; + } + +} diff --git a/大厂刷题班/class28/Problem_0037_SudokuSolver.java b/大厂刷题班/class28/Problem_0037_SudokuSolver.java new file mode 100644 index 0000000..7501622 --- /dev/null +++ b/大厂刷题班/class28/Problem_0037_SudokuSolver.java @@ -0,0 +1,61 @@ +package class28; + +public class Problem_0037_SudokuSolver { + + public static void solveSudoku(char[][] board) { + boolean[][] row = new boolean[9][10]; + boolean[][] col = new boolean[9][10]; + boolean[][] bucket = new boolean[9][10]; + initMaps(board, row, col, bucket); + process(board, 0, 0, row, col, bucket); + } + + public static void initMaps(char[][] board, boolean[][] row, boolean[][] col, boolean[][] bucket) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + int bid = 3 * (i / 3) + (j / 3); + if (board[i][j] != '.') { + int num = board[i][j] - '0'; + row[i][num] = true; + col[j][num] = true; + bucket[bid][num] = true; + } + } + } + } + + // 当前来到(i,j)这个位置,如果已经有数字,跳到下一个位置上 + // 如果没有数字,尝试1~9,不能和row、col、bucket冲突 + public static boolean process(char[][] board, int i, int j, boolean[][] row, boolean[][] col, boolean[][] bucket) { + if (i == 9) { + return true; + } + // 当离开(i,j),应该去哪?(nexti, nextj) + int nexti = j != 8 ? i : i + 1; + int nextj = j != 8 ? j + 1 : 0; + if (board[i][j] != '.') { + return process(board, nexti, nextj, row, col, bucket); + } else { + // 可以尝试1~9 + int bid = 3 * (i / 3) + (j / 3); + for (int num = 1; num <= 9; num++) { // 尝试每一个数字1~9 + if ((!row[i][num]) && (!col[j][num]) && (!bucket[bid][num])) { + // 可以尝试num + row[i][num] = true; + col[j][num] = true; + bucket[bid][num] = true; + board[i][j] = (char) (num + '0'); + if (process(board, nexti, nextj, row, col, bucket)) { + return true; + } + row[i][num] = false; + col[j][num] = false; + bucket[bid][num] = false; + board[i][j] = '.'; + } + } + return false; + } + } + +} diff --git a/大厂刷题班/class28/Problem_0038_CountAndSay.java b/大厂刷题班/class28/Problem_0038_CountAndSay.java new file mode 100644 index 0000000..cd719f3 --- /dev/null +++ b/大厂刷题班/class28/Problem_0038_CountAndSay.java @@ -0,0 +1,33 @@ +package class28; + +public class Problem_0038_CountAndSay { + + public static String countAndSay(int n) { + if (n < 1) { + return ""; + } + if (n == 1) { + return "1"; + } + char[] last = countAndSay(n - 1).toCharArray(); + StringBuilder ans = new StringBuilder(); + int times = 1; + for (int i = 1; i < last.length; i++) { + if (last[i - 1] == last[i]) { + times++; + } else { + ans.append(String.valueOf(times)); + ans.append(String.valueOf(last[i - 1])); + times = 1; + } + } + ans.append(String.valueOf(times)); + ans.append(String.valueOf(last[last.length - 1])); + return ans.toString(); + } + + public static void main(String[] args) { + System.out.println(countAndSay(20)); + } + +} diff --git a/大厂刷题班/class28/Problem_0049_GroupAnagrams.java b/大厂刷题班/class28/Problem_0049_GroupAnagrams.java new file mode 100644 index 0000000..8b325aa --- /dev/null +++ b/大厂刷题班/class28/Problem_0049_GroupAnagrams.java @@ -0,0 +1,52 @@ +package class28; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class Problem_0049_GroupAnagrams { + + public static List> groupAnagrams1(String[] strs) { + HashMap> map = new HashMap>(); + for (String str : strs) { + int[] record = new int[26]; + for (char cha : str.toCharArray()) { + record[cha - 'a']++; + } + StringBuilder builder = new StringBuilder(); + for (int value : record) { + builder.append(String.valueOf(value)).append("_"); + } + String key = builder.toString(); + if (!map.containsKey(key)) { + map.put(key, new ArrayList()); + } + map.get(key).add(str); + } + List> res = new ArrayList>(); + for (List list : map.values()) { + res.add(list); + } + return res; + } + + public static List> groupAnagrams2(String[] strs) { + HashMap> map = new HashMap>(); + for (String str : strs) { + char[] chs = str.toCharArray(); + Arrays.sort(chs); + String key = String.valueOf(chs); + if (!map.containsKey(key)) { + map.put(key, new ArrayList()); + } + map.get(key).add(str); + } + List> res = new ArrayList>(); + for (List list : map.values()) { + res.add(list); + } + return res; + } + +} diff --git a/大厂刷题班/class28/说明 b/大厂刷题班/class28/说明 new file mode 100644 index 0000000..ec23675 --- /dev/null +++ b/大厂刷题班/class28/说明 @@ -0,0 +1,33 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0008 : 大厂刷题班, 第28节, 本节 +0010 : 大厂刷题班, 第12节第4题 +0011 : 大厂刷题班, 第8节第2题 +0012 : 大厂刷题班, 第28节, 本节 +0013 : 大厂刷题班, 第28节, 本节 +0014 : 大厂刷题班, 第28节, 本节 +0015 : 大厂刷题班, 第25节第2题 +0017 : 大厂刷题班, 第28节, 本节 +0019 : 大厂刷题班, 第28节, 本节 +0020 : 大厂刷题班, 第28节, 本节 +0021 : 算法新手班, 第4节第6题 +0022 : 大厂刷题班, 第28节, 本节 +0023 : 算法新手班, 第6节第1题 +0026 : 大厂刷题班, 第28节, 本节 +0028 : 体系学习班, KMP算法 +0029 : 算法新手班, 第5节第3题 +0034 : 大厂刷题班, 第28节, 本节 +0036 : 大厂刷题班, 第28节, 本节 +0037 : 大厂刷题班, 第28节, 本节 +0038 : 大厂刷题班, 第28节, 本节 +0041 : 大厂刷题班, 第14节第6题 +0042 : 大厂刷题班, 第22节第2题 +0044 : 大厂刷题班, 第12节第4题 +0045 : 大厂刷题班, 第10节第1题 +0046 : 体系学习班, 第17节第4题 +0048 : 体系学习班, 第40节第6题 +0049 : 大厂刷题班, 第28节, 本节 \ No newline at end of file diff --git a/大厂刷题班/class29/Problem_0033_SearchInRotatedSortedArray.java b/大厂刷题班/class29/Problem_0033_SearchInRotatedSortedArray.java new file mode 100644 index 0000000..ba8841a --- /dev/null +++ b/大厂刷题班/class29/Problem_0033_SearchInRotatedSortedArray.java @@ -0,0 +1,67 @@ +package class29; + +public class Problem_0033_SearchInRotatedSortedArray { + + // arr,原本是有序数组,旋转过,而且左部分长度不知道 + // 找num + // num所在的位置返回 + public static int search(int[] arr, int num) { + int L = 0; + int R = arr.length - 1; + int M = 0; + while (L <= R) { + // M = L + ((R - L) >> 1) + M = (L + R) / 2; + if (arr[M] == num) { + return M; + } + // arr[M] != num + // [L] == [M] == [R] != num 无法二分 + if (arr[L] == arr[M] && arr[M] == arr[R]) { + while (L != M && arr[L] == arr[M]) { + L++; + } + // 1) L == M L...M 一路都相等 + // 2) 从L到M终于找到了一个不等的位置 + if (L == M) { // L...M 一路都相等 + L = M + 1; + continue; + } + } + // ... + // arr[M] != num + // [L] [M] [R] 不都一样的情况, 如何二分的逻辑 + if (arr[L] != arr[M]) { + if (arr[M] > arr[L]) { // L...M 一定有序 + if (num >= arr[L] && num < arr[M]) { // 3 [L] == 1 [M] = 5 L...M - 1 + R = M - 1; + } else { // 9 [L] == 2 [M] = 7 M... R + L = M + 1; + } + } else { // [L] > [M] L....M 存在断点 + if (num > arr[M] && num <= arr[R]) { + L = M + 1; + } else { + R = M - 1; + } + } + } else { /// [L] [M] [R] 不都一样, [L] === [M] -> [M]!=[R] + if (arr[M] < arr[R]) { + if (num > arr[M] && num <= arr[R]) { + L = M + 1; + } else { + R = M - 1; + } + } else { + if (num >= arr[L] && num < arr[M]) { + R = M - 1; + } else { + L = M + 1; + } + } + } + } + return -1; + } + +} diff --git a/大厂刷题班/class29/Problem_0050_PowXN.java b/大厂刷题班/class29/Problem_0050_PowXN.java new file mode 100644 index 0000000..77d5288 --- /dev/null +++ b/大厂刷题班/class29/Problem_0050_PowXN.java @@ -0,0 +1,39 @@ +package class29; + +public class Problem_0050_PowXN { + + public static int pow(int a, int n) { + int ans = 1; + int t = a; + while (n != 0) { + if ((n & 1) != 0) { + ans *= t; + } + t *= t; + n >>= 1; + } + return ans; + } + + // x的n次方,n可能是负数 + public static double myPow(double x, int n) { + if (n == 0) { + return 1D; + } + int pow = Math.abs(n == Integer.MIN_VALUE ? n + 1 : n); + double t = x; + double ans = 1D; + while (pow != 0) { + if ((pow & 1) != 0) { + ans *= t; + } + pow >>= 1; + t = t * t; + } + if (n == Integer.MIN_VALUE) { + ans *= x; + } + return n < 0 ? (1D / ans) : ans; + } + +} diff --git a/大厂刷题班/class29/Problem_0056_MergeIntervals.java b/大厂刷题班/class29/Problem_0056_MergeIntervals.java new file mode 100644 index 0000000..d912b49 --- /dev/null +++ b/大厂刷题班/class29/Problem_0056_MergeIntervals.java @@ -0,0 +1,30 @@ +package class29; + +import java.util.Arrays; + +public class Problem_0056_MergeIntervals { + + public static int[][] merge(int[][] intervals) { + if (intervals.length == 0) { + return new int[0][0]; + } + Arrays.sort(intervals, (a, b) -> a[0] - b[0]); + int s = intervals[0][0]; + int e = intervals[0][1]; + int size = 0; + for (int i = 1; i < intervals.length; i++) { + if (intervals[i][0] > e) { + intervals[size][0] = s; + intervals[size++][1] = e; + s = intervals[i][0]; + e = intervals[i][1]; + } else { + e = Math.max(e, intervals[i][1]); + } + } + intervals[size][0] = s; + intervals[size++][1] = e; + return Arrays.copyOf(intervals, size); + } + +} diff --git a/大厂刷题班/class29/Problem_0062_UniquePaths.java b/大厂刷题班/class29/Problem_0062_UniquePaths.java new file mode 100644 index 0000000..c5ffd25 --- /dev/null +++ b/大厂刷题班/class29/Problem_0062_UniquePaths.java @@ -0,0 +1,30 @@ +package class29; + +public class Problem_0062_UniquePaths { + + // m 行 + // n 列 + // 下:m-1 + // 右:n-1 + public static int uniquePaths(int m, int n) { + int right = n - 1; + int all = m + n - 2; + long o1 = 1; + long o2 = 1; + // o1乘进去的个数 一定等于 o2乘进去的个数 + for (int i = right + 1, j = 1; i <= all; i++, j++) { + o1 *= i; + o2 *= j; + long gcd = gcd(o1, o2); + o1 /= gcd; + o2 /= gcd; + } + return (int) o1; + } + + // 调用的时候,请保证初次调用时,m和n都不为0 + public static long gcd(long m, long n) { + return n == 0 ? m : gcd(n, m % n); + } + +} diff --git a/大厂刷题班/class29/Problem_0066_PlusOne.java b/大厂刷题班/class29/Problem_0066_PlusOne.java new file mode 100644 index 0000000..3717e40 --- /dev/null +++ b/大厂刷题班/class29/Problem_0066_PlusOne.java @@ -0,0 +1,19 @@ +package class29; + +public class Problem_0066_PlusOne { + + public static int[] plusOne(int[] digits) { + int n = digits.length; + for (int i = n - 1; i >= 0; i--) { + if (digits[i] < 9) { + digits[i]++; + return digits; + } + digits[i] = 0; + } + int[] ans = new int[n + 1]; + ans[0] = 1; + return ans; + } + +} diff --git a/大厂刷题班/class29/Problem_0069_SqrtX.java b/大厂刷题班/class29/Problem_0069_SqrtX.java new file mode 100644 index 0000000..f58a642 --- /dev/null +++ b/大厂刷题班/class29/Problem_0069_SqrtX.java @@ -0,0 +1,30 @@ +package class29; + +public class Problem_0069_SqrtX { + + // x一定非负,输入可以保证 + public static int mySqrt(int x) { + if (x == 0) { + return 0; + } + if (x < 3) { + return 1; + } + // x >= 3 + long ans = 1; + long L = 1; + long R = x; + long M = 0; + while (L <= R) { + M = (L + R) / 2; + if (M * M <= x) { + ans = M; + L = M + 1; + } else { + R = M - 1; + } + } + return (int) ans; + } + +} diff --git a/大厂刷题班/class29/Problem_0073_SetMatrixZeroes.java b/大厂刷题班/class29/Problem_0073_SetMatrixZeroes.java new file mode 100644 index 0000000..be15049 --- /dev/null +++ b/大厂刷题班/class29/Problem_0073_SetMatrixZeroes.java @@ -0,0 +1,79 @@ +package class29; + +public class Problem_0073_SetMatrixZeroes { + + public static void setZeroes1(int[][] matrix) { + boolean row0Zero = false; + boolean col0Zero = false; + int i = 0; + int j = 0; + for (i = 0; i < matrix[0].length; i++) { + if (matrix[0][i] == 0) { + row0Zero = true; + break; + } + } + for (i = 0; i < matrix.length; i++) { + if (matrix[i][0] == 0) { + col0Zero = true; + break; + } + } + for (i = 1; i < matrix.length; i++) { + for (j = 1; j < matrix[0].length; j++) { + if (matrix[i][j] == 0) { + matrix[i][0] = 0; + matrix[0][j] = 0; + } + } + } + for (i = 1; i < matrix.length; i++) { + for (j = 1; j < matrix[0].length; j++) { + if (matrix[i][0] == 0 || matrix[0][j] == 0) { + matrix[i][j] = 0; + } + } + } + if (row0Zero) { + for (i = 0; i < matrix[0].length; i++) { + matrix[0][i] = 0; + } + } + if (col0Zero) { + for (i = 0; i < matrix.length; i++) { + matrix[i][0] = 0; + } + } + } + + public static void setZeroes2(int[][] matrix) { + boolean col0 = false; + int i = 0; + int j = 0; + for (i = 0; i < matrix.length; i++) { + for (j = 0; j < matrix[0].length; j++) { + if (matrix[i][j] == 0) { + matrix[i][0] = 0; + if (j == 0) { + col0 = true; + } else { + matrix[0][j] = 0; + } + } + } + } + for (i = matrix.length - 1; i >= 0; i--) { + for (j = 1; j < matrix[0].length; j++) { + if (matrix[i][0] == 0 || matrix[0][j] == 0) { + matrix[i][j] = 0; + } + } + } + if (col0) { + for (i = 0; i < matrix.length; i++) { + matrix[i][0] = 0; + } + } + } + +} diff --git a/大厂刷题班/class29/说明 b/大厂刷题班/class29/说明 new file mode 100644 index 0000000..75954f5 --- /dev/null +++ b/大厂刷题班/class29/说明 @@ -0,0 +1,20 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0033 : 大厂刷题班, 第29节, 本节 +0050 : 大厂刷题班, 第29节, 本节 +0053 : 体系学习班, 第40节第2题 +0054 : 体系学习班, 第40节第5题 +0055 : 大厂刷题班, 第10节第1题 +0056 : 大厂刷题班, 第29节, 本节 +0062 : 大厂刷题班, 第29节, 本节 +0066 : 大厂刷题班, 第29节, 本节 +0069 : 大厂刷题班, 第29节, 本节 +0070 : 体系学习班, 第26节第2题 +0073 : 大厂刷题班, 第29节, 本节 +0075 : 体系学习班, 第5节第2题, 快排中的荷兰国旗问题 +0076 : 大厂刷题班, 第24节第5题 +0078 : 体系学习班, 第17节题目3, 生成子序列问题和本题一样的 \ No newline at end of file diff --git a/大厂刷题班/class30/Problem_0079_WordSearch.java b/大厂刷题班/class30/Problem_0079_WordSearch.java new file mode 100644 index 0000000..8d3d0da --- /dev/null +++ b/大厂刷题班/class30/Problem_0079_WordSearch.java @@ -0,0 +1,41 @@ +package class30; + +public class Problem_0079_WordSearch { + + public static boolean exist(char[][] board, String word) { + char[] w = word.toCharArray(); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (f(board, i, j, w, 0)) { + return true; + } + } + } + return false; + } + + // 目前到达了b[i][j],word[k....] + // 从b[i][j]出发,能不能搞定word[k....] true false + public static boolean f(char[][] b, int i, int j, char[] w, int k) { + if (k == w.length) { + return true; + } + // word[k.....] 有字符 + // 如果(i,j)越界,返回false + if (i < 0 || i == b.length || j < 0 || j == b[0].length) { + return false; + } + if (b[i][j] != w[k]) { + return false; + } + char tmp = b[i][j]; + b[i][j] = 0; + boolean ans = f(b, i - 1, j, w, k + 1) + || f(b, i + 1, j, w, k + 1) + || f(b, i, j - 1, w, k + 1) + || f(b, i, j + 1, w, k + 1); + b[i][j] = tmp; + return ans; + } + +} diff --git a/大厂刷题班/class30/Problem_0088_MergeSortedArray.java b/大厂刷题班/class30/Problem_0088_MergeSortedArray.java new file mode 100644 index 0000000..4b24fc8 --- /dev/null +++ b/大厂刷题班/class30/Problem_0088_MergeSortedArray.java @@ -0,0 +1,22 @@ +package class30; + +public class Problem_0088_MergeSortedArray { + + public static void merge(int[] nums1, int m, int[] nums2, int n) { + int index = nums1.length; + while (m > 0 && n > 0) { + if (nums1[m - 1] >= nums2[n - 1]) { + nums1[--index] = nums1[--m]; + } else { + nums1[--index] = nums2[--n]; + } + } + while (m > 0) { + nums1[--index] = nums1[--m]; + } + while (n > 0) { + nums1[--index] = nums2[--n]; + } + } + +} diff --git a/大厂刷题班/class30/Problem_0091_DecodeWays.java b/大厂刷题班/class30/Problem_0091_DecodeWays.java new file mode 100644 index 0000000..0db1ac8 --- /dev/null +++ b/大厂刷题班/class30/Problem_0091_DecodeWays.java @@ -0,0 +1,87 @@ +package class30; + +public class Problem_0091_DecodeWays { + + public static int numDecodings1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + return process(str, 0); + } + + // 潜台词:str[0...index-1]已经转化完了,不用操心了 + // str[index....] 能转出多少有效的,返回方法数 + public static int process(char[] str, int index) { + if (index == str.length) { + return 1; + } + if (str[index] == '0') { + return 0; + } + int ways = process(str, index + 1); + if (index + 1 == str.length) { + return ways; + } + int num = (str[index] - '0') * 10 + str[index + 1] - '0'; + if (num < 27) { + ways += process(str, index + 2); + } + return ways; + } + + public static int numDecodings2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + // dp[i] -> process(str, index)返回值 index 0 ~ N + int[] dp = new int[N + 1]; + dp[N] = 1; + + // dp依次填好 dp[i] dp[i+1] dp[i+2] + for (int i = N - 1; i >= 0; i--) { + if (str[i] != '0') { + dp[i] = dp[i + 1]; + if (i + 1 == str.length) { + continue; + } + int num = (str[i] - '0') * 10 + str[i + 1] - '0'; + if (num <= 26) { + dp[i] += dp[i + 2]; + } + } + } + return dp[0]; + } + + public static int numDecodings(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + if (str[i] == '0') { + dp[i] = 0; + } else if (str[i] == '1') { + dp[i] = dp[i + 1]; + if (i + 1 < N) { + dp[i] += dp[i + 2]; + } + } else if (str[i] == '2') { + dp[i] = dp[i + 1]; + if (i + 1 < str.length && (str[i + 1] >= '0' && str[i + 1] <= '6')) { + dp[i] += dp[i + 2]; + } + } else { + dp[i] = dp[i + 1]; + } + } + return dp[0]; + } + +} diff --git a/大厂刷题班/class30/Problem_0098_ValidateBinarySearchTree.java b/大厂刷题班/class30/Problem_0098_ValidateBinarySearchTree.java new file mode 100644 index 0000000..d3471d8 --- /dev/null +++ b/大厂刷题班/class30/Problem_0098_ValidateBinarySearchTree.java @@ -0,0 +1,42 @@ +package class30; + +public class Problem_0098_ValidateBinarySearchTree { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + } + + public boolean isValidBST(TreeNode root) { + if (root == null) { + return true; + } + TreeNode cur = root; + TreeNode mostRight = null; + Integer pre = null; + boolean ans = true; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } + if (pre != null && pre >= cur.val) { + ans = false; + } + pre = cur.val; + cur = cur.right; + } + return ans; + } + +} diff --git a/大厂刷题班/class30/Problem_0101_SymmetricTree.java b/大厂刷题班/class30/Problem_0101_SymmetricTree.java new file mode 100644 index 0000000..f5cdfe7 --- /dev/null +++ b/大厂刷题班/class30/Problem_0101_SymmetricTree.java @@ -0,0 +1,30 @@ +package class30; + +public class Problem_0101_SymmetricTree { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + } + + public boolean isSymmetric(TreeNode root) { + return isMirror(root, root); + } + + // 一棵树是原始树 head1 + // 另一棵是翻面树 head2 + public static boolean isMirror(TreeNode head1, TreeNode head2) { + if (head1 == null && head2 == null) { + return true; + } + if (head1 != null && head2 != null) { + return head1.val == head2.val + && isMirror(head1.left, head2.right) + && isMirror(head1.right, head2.left); + } + // 一个为空,一个不为空 false + return false; + } + +} diff --git a/大厂刷题班/class30/Problem_0103_BinaryTreeZigzagLevelOrderTraversal.java b/大厂刷题班/class30/Problem_0103_BinaryTreeZigzagLevelOrderTraversal.java new file mode 100644 index 0000000..546fab2 --- /dev/null +++ b/大厂刷题班/class30/Problem_0103_BinaryTreeZigzagLevelOrderTraversal.java @@ -0,0 +1,52 @@ +package class30; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class Problem_0103_BinaryTreeZigzagLevelOrderTraversal { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + } + + public static List> zigzagLevelOrder(TreeNode root) { + List> ans = new ArrayList<>(); + if (root == null) { + return ans; + } + LinkedList deque = new LinkedList<>(); + deque.add(root); + int size = 0; + boolean isHead = true; + while (!deque.isEmpty()) { + size = deque.size(); + List curLevel = new ArrayList<>(); + for (int i = 0; i < size; i++) { + TreeNode cur = isHead ? deque.pollFirst() : deque.pollLast(); + curLevel.add(cur.val); + if(isHead) { + if (cur.left != null) { + deque.addLast(cur.left); + } + if (cur.right != null) { + deque.addLast(cur.right); + } + }else { + if (cur.right != null) { + deque.addFirst(cur.right); + } + if (cur.left != null) { + deque.addFirst(cur.left); + } + } + } + ans.add(curLevel); + isHead = !isHead; + } + return ans; + } + +} diff --git a/大厂刷题班/class30/Problem_0108_ConvertSortedArrayToBinarySearchTree.java b/大厂刷题班/class30/Problem_0108_ConvertSortedArrayToBinarySearchTree.java new file mode 100644 index 0000000..0914140 --- /dev/null +++ b/大厂刷题班/class30/Problem_0108_ConvertSortedArrayToBinarySearchTree.java @@ -0,0 +1,33 @@ +package class30; + +public class Problem_0108_ConvertSortedArrayToBinarySearchTree { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int val) { + this.val = val; + } + } + + public TreeNode sortedArrayToBST(int[] nums) { + return process(nums, 0, nums.length - 1); + } + + public static TreeNode process(int[] nums, int L, int R) { + if (L > R) { + return null; + } + if (L == R) { + return new TreeNode(nums[L]); + } + int M = (L + R) / 2; + TreeNode head = new TreeNode(nums[M]); + head.left = process(nums, L, M - 1); + head.right = process(nums, M + 1, R); + return head; + } + +} diff --git a/大厂刷题班/class30/Problem_0116_PopulatingNextRightPointersInEachNode.java b/大厂刷题班/class30/Problem_0116_PopulatingNextRightPointersInEachNode.java new file mode 100644 index 0000000..f3d82de --- /dev/null +++ b/大厂刷题班/class30/Problem_0116_PopulatingNextRightPointersInEachNode.java @@ -0,0 +1,77 @@ +package class30; + +public class Problem_0116_PopulatingNextRightPointersInEachNode { + + // 不要提交这个类 + public static class Node { + public int val; + public Node left; + public Node right; + public Node next; + } + + // 提交下面的代码 + public static Node connect(Node root) { + if (root == null) { + return root; + } + MyQueue queue = new MyQueue(); + queue.offer(root); + while (!queue.isEmpty()) { + // 第一个弹出的节点 + Node pre = null; + int size = queue.size; + for (int i = 0; i < size; i++) { + Node cur = queue.poll(); + if (cur.left != null) { + queue.offer(cur.left); + } + if (cur.right != null) { + queue.offer(cur.right); + } + if (pre != null) { + pre.next = cur; + } + pre = cur; + } + } + return root; + } + + public static class MyQueue { + public Node head; + public Node tail; + public int size; + + public MyQueue() { + head = null; + tail = null; + size = 0; + } + + public boolean isEmpty() { + return size == 0; + } + + public void offer(Node cur) { + size++; + if (head == null) { + head = cur; + tail = cur; + } else { + tail.next = cur; + tail = cur; + } + } + + public Node poll() { + size--; + Node ans = head; + head = head.next; + ans.next = null; + return ans; + } + + } + +} diff --git a/大厂刷题班/class30/Problem_0118_PascalTriangle.java b/大厂刷题班/class30/Problem_0118_PascalTriangle.java new file mode 100644 index 0000000..867990f --- /dev/null +++ b/大厂刷题班/class30/Problem_0118_PascalTriangle.java @@ -0,0 +1,23 @@ +package class30; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0118_PascalTriangle { + + public static List> generate(int numRows) { + List> ans = new ArrayList<>(); + for (int i = 0; i < numRows; i++) { + ans.add(new ArrayList<>()); + ans.get(i).add(1); + } + for (int i = 1; i < numRows; i++) { + for (int j = 1; j < i; j++) { + ans.get(i).add(ans.get(i - 1).get(j - 1) + ans.get(i - 1).get(j)); + } + ans.get(i).add(1); + } + return ans; + } + +} diff --git a/大厂刷题班/class30/Problem_0119_PascalTriangleII.java b/大厂刷题班/class30/Problem_0119_PascalTriangleII.java new file mode 100644 index 0000000..79bfc78 --- /dev/null +++ b/大厂刷题班/class30/Problem_0119_PascalTriangleII.java @@ -0,0 +1,19 @@ +package class30; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0119_PascalTriangleII { + + public List getRow(int rowIndex) { + List ans = new ArrayList<>(); + for (int i = 0; i <= rowIndex; i++) { + for (int j = i - 1; j > 0; j--) { + ans.set(j, ans.get(j - 1) + ans.get(j)); + } + ans.add(1); + } + return ans; + } + +} diff --git a/大厂刷题班/class30/Problem_0124_BinaryTreeMaximumPathSum.java b/大厂刷题班/class30/Problem_0124_BinaryTreeMaximumPathSum.java new file mode 100644 index 0000000..de6b07c --- /dev/null +++ b/大厂刷题班/class30/Problem_0124_BinaryTreeMaximumPathSum.java @@ -0,0 +1,206 @@ +package class30; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +// follow up : 如果要求返回整个路径怎么做? +public class Problem_0124_BinaryTreeMaximumPathSum { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + + public TreeNode(int v) { + val = v; + } + + } + + public static int maxPathSum(TreeNode root) { + if (root == null) { + return 0; + } + return process(root).maxPathSum; + } + + // 任何一棵树,必须汇报上来的信息 + public static class Info { + public int maxPathSum; + public int maxPathSumFromHead; + + public Info(int path, int head) { + maxPathSum = path; + maxPathSumFromHead = head; + } + } + + public static Info process(TreeNode x) { + if (x == null) { + return null; + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + // x 1)只有x 2)x往左扎 3)x往右扎 + int maxPathSumFromHead = x.val; + if (leftInfo != null) { + maxPathSumFromHead = Math.max(maxPathSumFromHead, x.val + leftInfo.maxPathSumFromHead); + } + if (rightInfo != null) { + maxPathSumFromHead = Math.max(maxPathSumFromHead, x.val + rightInfo.maxPathSumFromHead); + } + // x整棵树最大路径和 1) 只有x 2)左树整体的最大路径和 3) 右树整体的最大路径和 + int maxPathSum = x.val; + if (leftInfo != null) { + maxPathSum = Math.max(maxPathSum, leftInfo.maxPathSum); + } + if (rightInfo != null) { + maxPathSum = Math.max(maxPathSum, rightInfo.maxPathSum); + } + // 4) x只往左扎 5)x只往右扎 + maxPathSum = Math.max(maxPathSumFromHead, maxPathSum); + // 6)一起扎 + if (leftInfo != null && rightInfo != null && leftInfo.maxPathSumFromHead > 0 + && rightInfo.maxPathSumFromHead > 0) { + maxPathSum = Math.max(maxPathSum, leftInfo.maxPathSumFromHead + rightInfo.maxPathSumFromHead + x.val); + } + return new Info(maxPathSum, maxPathSumFromHead); + } + + // 如果要返回路径的做法 + public static List getMaxSumPath(TreeNode head) { + List ans = new ArrayList<>(); + if (head != null) { + Data data = f(head); + HashMap fmap = new HashMap<>(); + fmap.put(head, head); + fatherMap(head, fmap); + fillPath(fmap, data.from, data.to, ans); + } + return ans; + } + + public static class Data { + public int maxAllSum; + public TreeNode from; + public TreeNode to; + public int maxHeadSum; + public TreeNode end; + + public Data(int a, TreeNode b, TreeNode c, int d, TreeNode e) { + maxAllSum = a; + from = b; + to = c; + maxHeadSum = d; + end = e; + } + } + + public static Data f(TreeNode x) { + if (x == null) { + return null; + } + Data l = f(x.left); + Data r = f(x.right); + int maxHeadSum = x.val; + TreeNode end = x; + if (l != null && l.maxHeadSum > 0 && (r == null || l.maxHeadSum > r.maxHeadSum)) { + maxHeadSum += l.maxHeadSum; + end = l.end; + } + if (r != null && r.maxHeadSum > 0 && (l == null || r.maxHeadSum > l.maxHeadSum)) { + maxHeadSum += r.maxHeadSum; + end = r.end; + } + int maxAllSum = Integer.MIN_VALUE; + TreeNode from = null; + TreeNode to = null; + if (l != null) { + maxAllSum = l.maxAllSum; + from = l.from; + to = l.to; + } + if (r != null && r.maxAllSum > maxAllSum) { + maxAllSum = r.maxAllSum; + from = r.from; + to = r.to; + } + int p3 = x.val + (l != null && l.maxHeadSum > 0 ? l.maxHeadSum : 0) + + (r != null && r.maxHeadSum > 0 ? r.maxHeadSum : 0); + if (p3 > maxAllSum) { + maxAllSum = p3; + from = (l != null && l.maxHeadSum > 0) ? l.end : x; + to = (r != null && r.maxHeadSum > 0) ? r.end : x; + } + return new Data(maxAllSum, from, to, maxHeadSum, end); + } + + public static void fatherMap(TreeNode h, HashMap map) { + if (h.left == null && h.right == null) { + return; + } + if (h.left != null) { + map.put(h.left, h); + fatherMap(h.left, map); + } + if (h.right != null) { + map.put(h.right, h); + fatherMap(h.right, map); + } + } + + public static void fillPath(HashMap fmap, TreeNode a, TreeNode b, List ans) { + if (a == b) { + ans.add(a); + } else { + HashSet ap = new HashSet<>(); + TreeNode cur = a; + while (cur != fmap.get(cur)) { + ap.add(cur); + cur = fmap.get(cur); + } + ap.add(cur); + cur = b; + TreeNode lca = null; + while (lca == null) { + if (ap.contains(cur)) { + lca = cur; + } else { + cur = fmap.get(cur); + } + } + while (a != lca) { + ans.add(a); + a = fmap.get(a); + } + ans.add(lca); + ArrayList right = new ArrayList<>(); + while (b != lca) { + right.add(b); + b = fmap.get(b); + } + for (int i = right.size() - 1; i >= 0; i--) { + ans.add(right.get(i)); + } + } + } + + public static void main(String[] args) { + TreeNode head = new TreeNode(4); + head.left = new TreeNode(-7); + head.right = new TreeNode(-5); + head.left.left = new TreeNode(9); + head.left.right = new TreeNode(9); + head.right.left = new TreeNode(4); + head.right.right = new TreeNode(3); + + List maxPath = getMaxSumPath(head); + + for (TreeNode n : maxPath) { + System.out.println(n.val); + } + } + +} \ No newline at end of file diff --git a/大厂刷题班/class30/Problem_0639_DecodeWaysII.java b/大厂刷题班/class30/Problem_0639_DecodeWaysII.java new file mode 100644 index 0000000..a04422b --- /dev/null +++ b/大厂刷题班/class30/Problem_0639_DecodeWaysII.java @@ -0,0 +1,161 @@ +package class30; + +public class Problem_0639_DecodeWaysII { + + public static int numDecodings0(String str) { + return f(str.toCharArray(), 0); + } + + public static int f(char[] str, int i) { + if (i == str.length) { + return 1; + } + if (str[i] == '0') { + return 0; + } + // str[index]有字符且不是'0' + if (str[i] != '*') { + // str[index] = 1~9 + // i -> 单转 + int p1 = f(str, i + 1); + if (i + 1 == str.length) { + return p1; + } + if (str[i + 1] != '*') { + int num = (str[i] - '0') * 10 + str[i + 1] - '0'; + int p2 = 0; + if (num < 27) { + p2 = f(str, i + 2); + } + return p1 + p2; + } else { // str[i+1] == '*' + // i i+1 -> 一起转 1* 2* 3* 9* + int p2 = 0; + if (str[i] < '3') { + p2 = f(str, i + 2) * (str[i] == '1' ? 9 : 6); + } + return p1 + p2; + } + } else { // str[i] == '*' 1~9 + // i 单转 9种 + int p1 = 9 * f(str, i + 1); + if (i + 1 == str.length) { + return p1; + } + if (str[i + 1] != '*') { + // * 0 10 20 + // * 1 11 21 + // * 2 12 22 + // * 3 13 23 + // * 6 16 26 + // * 7 17 + // * 8 18 + // * 9 19 + int p2 = (str[i + 1] < '7' ? 2 : 1) * f(str, i + 2); + return p1 + p2; + } else { // str[i+1] == * + // ** + // 11~19 9 + // 21 ~26 6 + // 15 + int p2 = 15 * f(str, i + 2); + return p1 + p2; + } + } + } + + public static long mod = 1000000007; + + public static int numDecodings1(String str) { + long[] dp = new long[str.length()]; + return (int) ways1(str.toCharArray(), 0, dp); + } + + public static long ways1(char[] s, int i, long[] dp) { + if (i == s.length) { + return 1; + } + if (s[i] == '0') { + return 0; + } + if (dp[i] != 0) { + return dp[i]; + } + long ans = ways1(s, i + 1, dp) * (s[i] == '*' ? 9 : 1); + if (s[i] == '1' || s[i] == '2' || s[i] == '*') { + if (i + 1 < s.length) { + if (s[i + 1] == '*') { + ans += ways1(s, i + 2, dp) * (s[i] == '*' ? 15 : (s[i] == '1' ? 9 : 6)); + } else { + if (s[i] == '*') { + ans += ways1(s, i + 2, dp) * (s[i + 1] < '7' ? 2 : 1); + } else { + ans += ((s[i] - '0') * 10 + s[i + 1] - '0') < 27 ? ways1(s, i + 2, dp) : 0; + } + } + } + } + ans %= mod; + dp[i] = ans; + return ans; + } + + public static int numDecodings2(String str) { + char[] s = str.toCharArray(); + int n = s.length; + long[] dp = new long[n + 1]; + dp[n] = 1; + for (int i = n - 1; i >= 0; i--) { + if (s[i] != '0') { + dp[i] = dp[i + 1] * (s[i] == '*' ? 9 : 1); + if (s[i] == '1' || s[i] == '2' || s[i] == '*') { + if (i + 1 < n) { + if (s[i + 1] == '*') { + dp[i] += dp[i + 2] * (s[i] == '*' ? 15 : (s[i] == '1' ? 9 : 6)); + } else { + if (s[i] == '*') { + dp[i] += dp[i + 2] * (s[i + 1] < '7' ? 2 : 1); + } else { + dp[i] += ((s[i] - '0') * 10 + s[i + 1] - '0') < 27 ? dp[i + 2] : 0; + } + } + } + } + dp[i] %= mod; + } + } + return (int) dp[0]; + } + + public static int numDecodings3(String str) { + char[] s = str.toCharArray(); + int n = s.length; + long a = 1; + long b = 1; + long c = 0; + for (int i = n - 1; i >= 0; i--) { + if (s[i] != '0') { + c = b * (s[i] == '*' ? 9 : 1); + if (s[i] == '1' || s[i] == '2' || s[i] == '*') { + if (i + 1 < n) { + if (s[i + 1] == '*') { + c += a * (s[i] == '*' ? 15 : (s[i] == '1' ? 9 : 6)); + } else { + if (s[i] == '*') { + c += a * (s[i + 1] < '7' ? 2 : 1); + } else { + c += a * (((s[i] - '0') * 10 + s[i + 1] - '0') < 27 ? 1 : 0); + } + } + } + } + } + c %= mod; + a = b; + b = c; + c = 0; + } + return (int) b; + } + +} diff --git a/大厂刷题班/class30/说明 b/大厂刷题班/class30/说明 new file mode 100644 index 0000000..81b1132 --- /dev/null +++ b/大厂刷题班/class30/说明 @@ -0,0 +1,26 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0079 : 大厂刷题班, 第30节, 本节 +0084 : 体系学习班, 第25节第3题 +0088 : 大厂刷题班, 第30节, 本节 +0091 : 体系学习班, 第19节第2题 +0639 : 本题不再高频题列表中, 但本题是0091的难度加强题, 相似度很强, 大厂刷题班, 第30节, 本节 +0094 : 体系学习班, 第30节第1题, Morris遍历 +0098 : 大厂刷题班, 第30节, 本节 +0101 : 大厂刷题班, 第30节, 本节 +0102 : 新手班, 第7节第1题 +0103 : 大厂刷题班, 第30节, 本节 +0104 : 太简单了, 体系学习班, 二叉树的递归套路、Morris遍历都可以做, 跳过 +0105 : 新手班, 第6节第5题 +0108 : 大厂刷题班, 第30节, 本节 +0116 : 大厂刷题班, 第30节, 本节 +0118 : 大厂刷题班, 第30节, 本节 +0119 : 本题不在高频题列表中,但和0118类似, 大厂刷题班, 第30节, 本节 +0121 : 大厂刷题班, 第15节第1题 +0122 : 大厂刷题班, 第15节第2题 +0123 : 大厂刷题班, 第15节第3题 +0124 : 大厂刷题班, 第30节, 本节 \ No newline at end of file diff --git a/大厂刷题班/class31/Problem_0125_ValidPalindrome.java b/大厂刷题班/class31/Problem_0125_ValidPalindrome.java new file mode 100644 index 0000000..e55e04b --- /dev/null +++ b/大厂刷题班/class31/Problem_0125_ValidPalindrome.java @@ -0,0 +1,52 @@ +package class31; + +public class Problem_0125_ValidPalindrome { + + // 忽略空格、忽略大小写 -> 是不是回文 + // 数字不在忽略大小写的范围内 + public static boolean isPalindrome(String s) { + if (s == null || s.length() == 0) { + return true; + } + char[] str = s.toCharArray(); + int L = 0; + int R = str.length - 1; + while (L < R) { + // 英文(大小写) + 数字 + if (validChar(str[L]) && validChar(str[R])) { + if (!equal(str[L], str[R])) { + return false; + } + L++; + R--; + } else { + L += validChar(str[L]) ? 0 : 1; + R -= validChar(str[R]) ? 0 : 1; + } + } + return true; + } + + public static boolean validChar(char c) { + return isLetter(c) || isNumber(c); + } + + public static boolean equal(char c1, char c2) { + if (isNumber(c1) || isNumber(c2)) { + return c1 == c2; + } + // a A 32 + // b B 32 + // c C 32 + return (c1 == c2) || (Math.max(c1, c2) - Math.min(c1, c2) == 32); + } + + public static boolean isLetter(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + public static boolean isNumber(char c) { + return (c >= '0' && c <= '9'); + } + +} diff --git a/大厂刷题班/class31/Problem_0127_WordLadder.java b/大厂刷题班/class31/Problem_0127_WordLadder.java new file mode 100644 index 0000000..6a4056f --- /dev/null +++ b/大厂刷题班/class31/Problem_0127_WordLadder.java @@ -0,0 +1,124 @@ +package class31; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class Problem_0127_WordLadder { + + // start,出发的单词 + // to, 目标单位 + // list, 列表 + // to 一定属于list + // start未必 + // 返回变幻的最短路径长度 + public static int ladderLength1(String start, String to, List list) { + list.add(start); + + // key : 列表中的单词,每一个单词都会有记录! + // value : key这个单词,有哪些邻居! + HashMap> nexts = getNexts(list); + // abc 出发 abc -> abc 0 + // + // bbc 1 + HashMap distanceMap = new HashMap<>(); + distanceMap.put(start, 1); + HashSet set = new HashSet<>(); + set.add(start); + Queue queue = new LinkedList<>(); + queue.add(start); + while (!queue.isEmpty()) { + String cur = queue.poll(); + Integer distance = distanceMap.get(cur); + for (String next : nexts.get(cur)) { + if (next.equals(to)) { + return distance + 1; + } + if (!set.contains(next)) { + set.add(next); + queue.add(next); + distanceMap.put(next, distance + 1); + } + } + + } + return 0; + } + + public static HashMap> getNexts(List words) { + HashSet dict = new HashSet<>(words); + HashMap> nexts = new HashMap<>(); + for (int i = 0; i < words.size(); i++) { + nexts.put(words.get(i), getNext(words.get(i), dict)); + } + return nexts; + } + + // 应该根据具体数据状况决定用什么来找邻居 + // 1)如果字符串长度比较短,字符串数量比较多,以下方法适合 + // 2)如果字符串长度比较长,字符串数量比较少,以下方法不适合 + public static ArrayList getNext(String word, HashSet dict) { + ArrayList res = new ArrayList(); + char[] chs = word.toCharArray(); + for (int i = 0; i < chs.length; i++) { + for (char cur = 'a'; cur <= 'z'; cur++) { + if (chs[i] != cur) { + char tmp = chs[i]; + chs[i] = cur; + if (dict.contains(String.valueOf(chs))) { + res.add(String.valueOf(chs)); + } + chs[i] = tmp; + } + } + } + return res; + } + + public static int ladderLength2(String beginWord, String endWord, List wordList) { + HashSet dict = new HashSet<>(wordList); + if (!dict.contains(endWord)) { + return 0; + } + HashSet startSet = new HashSet<>(); + HashSet endSet = new HashSet<>(); + HashSet visit = new HashSet<>(); + startSet.add(beginWord); + endSet.add(endWord); + for (int len = 2; !startSet.isEmpty(); len++) { + // startSet是较小的,endSet是较大的 + HashSet nextSet = new HashSet<>(); + for (String w : startSet) { + // w -> a(nextSet) + // a b c + // 0 + // 1 + // 2 + for (int j = 0; j < w.length(); j++) { + char[] ch = w.toCharArray(); + for (char c = 'a'; c <= 'z'; c++) { + if (c != w.charAt(j)) { + ch[j] = c; + String next = String.valueOf(ch); + if (endSet.contains(next)) { + return len; + } + if (dict.contains(next) && !visit.contains(next)) { + nextSet.add(next); + visit.add(next); + } + } + } + } + } + // startSet(小) -> nextSet(某个大小) 和 endSet大小来比 + startSet = (nextSet.size() < endSet.size()) ? nextSet : endSet; + endSet = (startSet == nextSet) ? endSet : nextSet; + } + return 0; + } + +} diff --git a/大厂刷题班/class31/Problem_0130_SurroundedRegions.java b/大厂刷题班/class31/Problem_0130_SurroundedRegions.java new file mode 100644 index 0000000..209ae30 --- /dev/null +++ b/大厂刷题班/class31/Problem_0130_SurroundedRegions.java @@ -0,0 +1,115 @@ +package class31; + +public class Problem_0130_SurroundedRegions { + +// // m -> 二维数组, 不是0就是1 +// // +// public static void infect(int[][] m, int i, int j) { +// if (i < 0 || i == m.length || j < 0 || j == m[0].length || m[i][j] != 1) { +// return; +// } +// // m[i][j] == 1 +// m[i][j] = 2; +// infect(m, i - 1, j); +// infect(m, i + 1, j); +// infect(m, i, j - 1); +// infect(m, i, j + 1); +// } + + public static void solve1(char[][] board) { + boolean[] ans = new boolean[1]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == 'O') { + ans[0] = true; + can(board, i, j, ans); + board[i][j] = ans[0] ? 'T' : 'F'; + } + } + } + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + char can = board[i][j]; + if (can == 'T' || can == 'F') { + board[i][j] = '.'; + change(board, i, j, can); + } + } + } + + } + + public static void can(char[][] board, int i, int j, boolean[] ans) { + if (i < 0 || i == board.length || j < 0 || j == board[0].length) { + ans[0] = false; + return; + } + if (board[i][j] == 'O') { + board[i][j] = '.'; + can(board, i - 1, j, ans); + can(board, i + 1, j, ans); + can(board, i, j - 1, ans); + can(board, i, j + 1, ans); + } + } + + public static void change(char[][] board, int i, int j, char can) { + if (i < 0 || i == board.length || j < 0 || j == board[0].length) { + return; + } + if (board[i][j] == '.') { + board[i][j] = can == 'T' ? 'X' : 'O'; + change(board, i - 1, j, can); + change(board, i + 1, j, can); + change(board, i, j - 1, can); + change(board, i, j + 1, can); + } + } + + // 从边界开始感染的方法,比第一种方法更好 + public static void solve2(char[][] board) { + if (board == null || board.length == 0 || board[0] == null || board[0].length == 0) { + return; + } + int N = board.length; + int M = board[0].length; + for (int j = 0; j < M; j++) { + if (board[0][j] == 'O') { + free(board, 0, j); + } + if (board[N - 1][j] == 'O') { + free(board, N - 1, j); + } + } + for (int i = 1; i < N - 1; i++) { + if (board[i][0] == 'O') { + free(board, i, 0); + } + if (board[i][M - 1] == 'O') { + free(board, i, M - 1); + } + } + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + if (board[i][j] == 'F') { + board[i][j] = 'O'; + } + } + } + } + + public static void free(char[][] board, int i, int j) { + if (i < 0 || i == board.length || j < 0 || j == board[0].length || board[i][j] != 'O') { + return; + } + board[i][j] = 'F'; + free(board, i + 1, j); + free(board, i - 1, j); + free(board, i, j + 1); + free(board, i, j - 1); + } + +} diff --git a/大厂刷题班/class31/Problem_0139_WordBreak.java b/大厂刷题班/class31/Problem_0139_WordBreak.java new file mode 100644 index 0000000..245e1d6 --- /dev/null +++ b/大厂刷题班/class31/Problem_0139_WordBreak.java @@ -0,0 +1,97 @@ +package class31; + +import java.util.List; + +// lintcode也有测试,数据量比leetcode大很多 : https://www.lintcode.com/problem/107/ +public class Problem_0139_WordBreak { + + public static class Node { + public boolean end; + public Node[] nexts; + + public Node() { + end = false; + nexts = new Node[26]; + } + } + + public static boolean wordBreak1(String s, List wordDict) { + Node root = new Node(); + for (String str : wordDict) { + char[] chs = str.toCharArray(); + Node node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new Node(); + } + node = node.nexts[index]; + } + node.end = true; + } + char[] str = s.toCharArray(); + int N = str.length; + boolean[] dp = new boolean[N + 1]; + dp[N] = true; // dp[i] word[i.....] 能不能被分解 + // dp[N] word[N...] -> "" 能不能够被分解 + // dp[i] ... dp[i+1....] + for (int i = N - 1; i >= 0; i--) { + // i + // word[i....] 能不能够被分解 + // i..i i+1.... + // i..i+1 i+2... + Node cur = root; + for (int end = i; end < N; end++) { + cur = cur.nexts[str[end] - 'a']; + if (cur == null) { + break; + } + // 有路! + if (cur.end) { + // i...end 真的是一个有效的前缀串 end+1.... 能不能被分解 + dp[i] |= dp[end + 1]; + } + if (dp[i]) { + break; + } + } + } + return dp[0]; + } + + public static int wordBreak2(String s, List wordDict) { + Node root = new Node(); + for (String str : wordDict) { + char[] chs = str.toCharArray(); + Node node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new Node(); + } + node = node.nexts[index]; + } + node.end = true; + } + char[] str = s.toCharArray(); + int N = str.length; + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + Node cur = root; + for (int end = i; end < N; end++) { + cur = cur.nexts[str[end] - 'a']; + if (cur == null) { + break; + } + if (cur.end) { + dp[i] += dp[end + 1]; + } + } + } + return dp[0]; + } + +} diff --git a/大厂刷题班/class31/Problem_0140_WordBreakII.java b/大厂刷题班/class31/Problem_0140_WordBreakII.java new file mode 100644 index 0000000..3290b88 --- /dev/null +++ b/大厂刷题班/class31/Problem_0140_WordBreakII.java @@ -0,0 +1,104 @@ +package class31; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0140_WordBreakII { + + public static class Node { + public String path; + public boolean end; + public Node[] nexts; + + public Node() { + path = null; + end = false; + nexts = new Node[26]; + } + } + + public static List wordBreak(String s, List wordDict) { + char[] str = s.toCharArray(); + Node root = gettrie(wordDict); + boolean[] dp = getdp(s, root); + ArrayList path = new ArrayList<>(); + List ans = new ArrayList<>(); + process(str, 0, root, dp, path, ans); + return ans; + } + + // str[index.....] 是要搞定的字符串 + // dp[0...N-1] 0... 1.... 2... N-1... 在dp里 + // root 单词表所有单词生成的前缀树头节点 + // path str[0..index-1]做过决定了,做的决定放在path里 + public static void process(char[] str, int index, Node root, boolean[] dp, ArrayList path, + List ans) { + if (index == str.length) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < path.size() - 1; i++) { + builder.append(path.get(i) + " "); + } + builder.append(path.get(path.size() - 1)); + ans.add(builder.toString()); + } else { + Node cur = root; + for (int end = index; end < str.length; end++) { + // str[i..end] (能不能拆出来) + int road = str[end] - 'a'; + if (cur.nexts[road] == null) { + break; + } + cur = cur.nexts[road]; + if (cur.end && dp[end + 1]) { + // [i...end] 前缀串 + // str.subString(i,end+1) [i..end] + path.add(cur.path); + process(str, end + 1, root, dp, path, ans); + path.remove(path.size() - 1); + } + } + } + } + + public static Node gettrie(List wordDict) { + Node root = new Node(); + for (String str : wordDict) { + char[] chs = str.toCharArray(); + Node node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new Node(); + } + node = node.nexts[index]; + } + node.path = str; + node.end = true; + } + return root; + } + + public static boolean[] getdp(String s, Node root) { + char[] str = s.toCharArray(); + int N = str.length; + boolean[] dp = new boolean[N + 1]; + dp[N] = true; + for (int i = N - 1; i >= 0; i--) { + Node cur = root; + for (int end = i; end < N; end++) { + int path = str[end] - 'a'; + if (cur.nexts[path] == null) { + break; + } + cur = cur.nexts[path]; + if (cur.end && dp[end + 1]) { + dp[i] = true; + break; + } + } + } + return dp; + } + +} diff --git a/大厂刷题班/class31/Problem_0148_SortList.java b/大厂刷题班/class31/Problem_0148_SortList.java new file mode 100644 index 0000000..8b1bf26 --- /dev/null +++ b/大厂刷题班/class31/Problem_0148_SortList.java @@ -0,0 +1,227 @@ +package class31; + +public class Problem_0148_SortList { + + public static class ListNode { + int val; + ListNode next; + + public ListNode(int v) { + val = v; + } + } + + // 链表的归并排序 + // 时间复杂度O(N*logN), 因为是链表所以空间复杂度O(1) + public static ListNode sortList1(ListNode head) { + int N = 0; + ListNode cur = head; + while (cur != null) { + N++; + cur = cur.next; + } + ListNode h = head; + ListNode teamFirst = head; + ListNode pre = null; + for (int len = 1; len < N; len <<= 1) { + while (teamFirst != null) { + // 左组从哪到哪 ls le + // 右组从哪到哪 rs re + // 左 右 next + ListNode[] hthtn = hthtn(teamFirst, len); + // ls...le rs...re -> merge去 + // 整体的头、整体的尾 + ListNode[] mhmt = merge(hthtn[0], hthtn[1], hthtn[2], hthtn[3]); + if (h == teamFirst) { + h = mhmt[0]; + pre = mhmt[1]; + } else { + pre.next = mhmt[0]; + pre = mhmt[1]; + } + teamFirst = hthtn[4]; + } + teamFirst = h; + pre = null; + } + return h; + } + + public static ListNode[] hthtn(ListNode teamFirst, int len) { + ListNode ls = teamFirst; + ListNode le = teamFirst; + ListNode rs = null; + ListNode re = null; + ListNode next = null; + int pass = 0; + while (teamFirst != null) { + pass++; + if (pass <= len) { + le = teamFirst; + } + if (pass == len + 1) { + rs = teamFirst; + } + if (pass > len) { + re = teamFirst; + } + if (pass == (len << 1)) { + break; + } + teamFirst = teamFirst.next; + } + le.next = null; + if (re != null) { + next = re.next; + re.next = null; + } + return new ListNode[] { ls, le, rs, re, next }; + } + + public static ListNode[] merge(ListNode ls, ListNode le, ListNode rs, ListNode re) { + if (rs == null) { + return new ListNode[] { ls, le }; + } + ListNode head = null; + ListNode pre = null; + ListNode cur = null; + ListNode tail = null; + while (ls != le.next && rs != re.next) { + if (ls.val <= rs.val) { + cur = ls; + ls = ls.next; + } else { + cur = rs; + rs = rs.next; + } + if (pre == null) { + head = cur; + pre = cur; + } else { + pre.next = cur; + pre = cur; + } + } + if (ls != le.next) { + while (ls != le.next) { + pre.next = ls; + pre = ls; + tail = ls; + ls = ls.next; + } + } else { + while (rs != re.next) { + pre.next = rs; + pre = rs; + tail = rs; + rs = rs.next; + } + } + return new ListNode[] { head, tail }; + } + + // 链表的快速排序 + // 时间复杂度O(N*logN), 空间复杂度O(logN) + public static ListNode sortList2(ListNode head) { + int n = 0; + ListNode cur = head; + while (cur != null) { + cur = cur.next; + n++; + } + return process(head, n).head; + } + + public static class HeadAndTail { + public ListNode head; + public ListNode tail; + + public HeadAndTail(ListNode h, ListNode t) { + head = h; + tail = t; + } + } + + public static HeadAndTail process(ListNode head, int n) { + if (n == 0) { + return new HeadAndTail(head, head); + } + int index = (int) (Math.random() * n); + ListNode cur = head; + while (index-- != 0) { + cur = cur.next; + } + Record r = partition(head, cur); + HeadAndTail lht = process(r.lhead, r.lsize); + HeadAndTail rht = process(r.rhead, r.rsize); + if (lht.tail != null) { + lht.tail.next = r.mhead; + } + r.mtail.next = rht.head; + return new HeadAndTail(lht.head != null ? lht.head : r.mhead, rht.tail != null ? rht.tail : r.mtail); + } + + public static class Record { + public ListNode lhead; + public int lsize; + public ListNode rhead; + public int rsize; + public ListNode mhead; + public ListNode mtail; + + public Record(ListNode lh, int ls, ListNode rh, int rs, ListNode mh, ListNode mt) { + lhead = lh; + lsize = ls; + rhead = rh; + rsize = rs; + mhead = mh; + mtail = mt; + } + } + + public static Record partition(ListNode head, ListNode mid) { + ListNode lh = null; + ListNode lt = null; + int ls = 0; + ListNode mh = null; + ListNode mt = null; + ListNode rh = null; + ListNode rt = null; + int rs = 0; + ListNode tmp = null; + while (head != null) { + tmp = head.next; + head.next = null; + if (head.val < mid.val) { + if (lh == null) { + lh = head; + lt = head; + } else { + lt.next = head; + lt = head; + } + ls++; + } else if (head.val > mid.val) { + if (rh == null) { + rh = head; + rt = head; + } else { + rt.next = head; + rt = head; + } + rs++; + } else { + if (mh == null) { + mh = head; + mt = head; + } else { + mt.next = head; + mt = head; + } + } + head = tmp; + } + return new Record(lh, ls, rh, rs, mh, mt); + } + +} diff --git a/大厂刷题班/class31/Problem_0150_EvaluateReversePolishNotation.java b/大厂刷题班/class31/Problem_0150_EvaluateReversePolishNotation.java new file mode 100644 index 0000000..54cf55e --- /dev/null +++ b/大厂刷题班/class31/Problem_0150_EvaluateReversePolishNotation.java @@ -0,0 +1,40 @@ +package class31; + +import java.util.Stack; + +public class Problem_0150_EvaluateReversePolishNotation { + + public static int evalRPN(String[] tokens) { + Stack stack = new Stack<>(); + for (String str : tokens) { + if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/")) { + compute(stack, str); + } else { + stack.push(Integer.valueOf(str)); + } + } + return stack.peek(); + } + + public static void compute(Stack stack, String op) { + int num2 = stack.pop(); + int num1 = stack.pop(); + int ans = 0; + switch (op) { + case "+": + ans = num1 + num2; + break; + case "-": + ans = num1 - num2; + break; + case "*": + ans = num1 * num2; + break; + case "/": + ans = num1 / num2; + break; + } + stack.push(ans); + } + +} diff --git a/大厂刷题班/class31/说明 b/大厂刷题班/class31/说明 new file mode 100644 index 0000000..a3b9f3f --- /dev/null +++ b/大厂刷题班/class31/说明 @@ -0,0 +1,21 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0125 : 大厂刷题班, 第31节, 本节 +0127 : 大厂刷题班, 第31节, 本节 +0128 : 大厂刷题班, 第12节第3题 +0130 : 大厂刷题班, 第31节, 本节 +0131 : 大厂刷题班, 第11节第2题 +0134 : 体系学习班, 第24节第3题 & 大厂刷题班, 第25节第4题 +0136 : 体系学习班, 第2节第2题 +0138 : 体系学习班, 第9节第4题 +0139 : 大厂刷题班, 第31节, 本节 +0140 : 大厂刷题班, 第31节, 本节 +0141 : 体系学习班, 第10节第1题 +0146 : 大厂刷题班, 第19节第1题 +0148 : 大厂刷题班, 第31节, 本节 +0149 : 大厂刷题班, 第25节第3题 +0150 : 大厂刷题班, 第31节, 本节 \ No newline at end of file diff --git a/大厂刷题班/class32/Problem_0152_MaximumProductSubarray.java b/大厂刷题班/class32/Problem_0152_MaximumProductSubarray.java new file mode 100644 index 0000000..92e5deb --- /dev/null +++ b/大厂刷题班/class32/Problem_0152_MaximumProductSubarray.java @@ -0,0 +1,45 @@ +package class32; + +public class Problem_0152_MaximumProductSubarray { + + + public static double max(double[] arr) { + if(arr == null || arr.length == 0) { + return 0; // 报错! + } + int n = arr.length; + // 上一步的最大 + double premax = arr[0]; + // 上一步的最小 + double premin = arr[0]; + double ans = arr[0]; + for(int i = 1; i < n; i++) { + double p1 = arr[i]; + double p2 = arr[i] * premax; + double p3 = arr[i] * premin; + double curmax = Math.max(Math.max(p1, p2), p3); + double curmin = Math.min(Math.min(p1, p2), p3); + ans = Math.max(ans, curmax); + premax = curmax; + premin = curmin; + } + return ans; + } + + + + public static int maxProduct(int[] nums) { + int ans = nums[0]; + int min = nums[0]; + int max = nums[0]; + for (int i = 1; i < nums.length; i++) { + int curmin = Math.min(nums[i], Math.min(min * nums[i], max * nums[i])); + int curmax = Math.max(nums[i], Math.max(min * nums[i], max * nums[i])); + min = curmin; + max = curmax; + ans = Math.max(ans, max); + } + return ans; + } + +} diff --git a/大厂刷题班/class32/Problem_0163_MissingRanges.java b/大厂刷题班/class32/Problem_0163_MissingRanges.java new file mode 100644 index 0000000..e6ca7fd --- /dev/null +++ b/大厂刷题班/class32/Problem_0163_MissingRanges.java @@ -0,0 +1,35 @@ +package class32; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0163_MissingRanges { + + public static List findMissingRanges(int[] nums, int lower, int upper) { + List ans = new ArrayList<>(); + for (int num : nums) { + if (num > lower) { + ans.add(miss(lower, num - 1)); + } + if (num == upper) { + return ans; + } + lower = num + 1; + } + if (lower <= upper) { + ans.add(miss(lower, upper)); + } + return ans; + } + + // 生成"lower->upper"的字符串,如果lower==upper,只用生成"lower" + public static String miss(int lower, int upper) { + String left = String.valueOf(lower); + String right = ""; + if (upper > lower) { + right = "->" + String.valueOf(upper); + } + return left + right; + } + +} diff --git a/大厂刷题班/class32/Problem_0166_FractionToRecurringDecimal.java b/大厂刷题班/class32/Problem_0166_FractionToRecurringDecimal.java new file mode 100644 index 0000000..9dbef7e --- /dev/null +++ b/大厂刷题班/class32/Problem_0166_FractionToRecurringDecimal.java @@ -0,0 +1,46 @@ +package class32; + +import java.util.HashMap; + +public class Problem_0166_FractionToRecurringDecimal { + + public static String fractionToDecimal(int numerator, int denominator) { + if (numerator == 0) { + return "0"; + } + StringBuilder res = new StringBuilder(); + // "+" or "-" + res.append(((numerator > 0) ^ (denominator > 0)) ? "-" : ""); + long num = Math.abs((long) numerator); + long den = Math.abs((long) denominator); + // integral part + res.append(num / den); + num %= den; + if (num == 0) { + return res.toString(); + } + // fractional part + res.append("."); + HashMap map = new HashMap(); + map.put(num, res.length()); + while (num != 0) { + num *= 10; + res.append(num / den); + num %= den; + if (map.containsKey(num)) { + int index = map.get(num); + res.insert(index, "("); + res.append(")"); + break; + } else { + map.put(num, res.length()); + } + } + return res.toString(); + } + + public static void main(String[] args) { + System.out.println(fractionToDecimal(127, 999)); + } + +} diff --git a/大厂刷题班/class32/Problem_0171_ExcelSheetColumnNumber.java b/大厂刷题班/class32/Problem_0171_ExcelSheetColumnNumber.java new file mode 100644 index 0000000..bed8659 --- /dev/null +++ b/大厂刷题班/class32/Problem_0171_ExcelSheetColumnNumber.java @@ -0,0 +1,15 @@ +package class32; + +public class Problem_0171_ExcelSheetColumnNumber { + + // 这道题反过来也要会写 + public static int titleToNumber(String s) { + char[] str = s.toCharArray(); + int ans = 0; + for (int i = 0; i < str.length; i++) { + ans = ans * 26 + (str[i] - 'A') + 1; + } + return ans; + } + +} diff --git a/大厂刷题班/class32/Problem_0172_FactorialTrailingZeroes.java b/大厂刷题班/class32/Problem_0172_FactorialTrailingZeroes.java new file mode 100644 index 0000000..c0ca71e --- /dev/null +++ b/大厂刷题班/class32/Problem_0172_FactorialTrailingZeroes.java @@ -0,0 +1,14 @@ +package class32; + +public class Problem_0172_FactorialTrailingZeroes { + + public static int trailingZeroes(int n) { + int ans = 0; + while (n != 0) { + n /= 5; + ans += n; + } + return ans; + } + +} diff --git a/大厂刷题班/class32/Problem_0189_RotateArray.java b/大厂刷题班/class32/Problem_0189_RotateArray.java new file mode 100644 index 0000000..4eb4480 --- /dev/null +++ b/大厂刷题班/class32/Problem_0189_RotateArray.java @@ -0,0 +1,60 @@ +package class32; + +public class Problem_0189_RotateArray { + + public void rotate1(int[] nums, int k) { + int N = nums.length; + k = k % N; + reverse(nums, 0, N - k - 1); + reverse(nums, N - k, N - 1); + reverse(nums, 0, N - 1); + } + + public static void reverse(int[] nums, int L, int R) { + while (L < R) { + int tmp = nums[L]; + nums[L++] = nums[R]; + nums[R--] = tmp; + } + } + + public static void rotate2(int[] nums, int k) { + int N = nums.length; + k = k % N; + if (k == 0) { + return; + } + int L = 0; + int R = N - 1; + int lpart = N - k; + int rpart = k; + int same = Math.min(lpart, rpart); + int diff = lpart - rpart; + exchange(nums, L, R, same); + while (diff != 0) { + if (diff > 0) { + L += same; + lpart = diff; + } else { + R -= same; + rpart = -diff; + } + same = Math.min(lpart, rpart); + diff = lpart - rpart; + exchange(nums, L, R, same); + } + } + + public static void exchange(int[] nums, int start, int end, int size) { + int i = end - size + 1; + int tmp = 0; + while (size-- != 0) { + tmp = nums[start]; + nums[start] = nums[i]; + nums[i] = tmp; + start++; + i++; + } + } + +} diff --git a/大厂刷题班/class32/Problem_0190_ReverseBits.java b/大厂刷题班/class32/Problem_0190_ReverseBits.java new file mode 100644 index 0000000..5851d87 --- /dev/null +++ b/大厂刷题班/class32/Problem_0190_ReverseBits.java @@ -0,0 +1,48 @@ +package class32; + +public class Problem_0190_ReverseBits { + + // 代码看着很魔幻吧? + // 给个例子,假设n二进制为: + // 1011 0111 0011 1001 0011 1111 0110 1010 + // 解释一下,第一行,是把n左边16位,和n右边16位交换 + // n = (n >>> 16) | (n << 16); + // 因为 n >>> 16 就是左边16位被移动到了右侧 + // 同时 n << 16 就是右边16位被移动到了左侧 + // 又 | 在了一起,所以,n变成了 + // 0011 1111 0110 1010 1011 0111 0011 1001 + + // 第二行, + // n = ((n & 0xff00ff00) >>> 8) | ((n & 0x00ff00ff) << 8); + // (n & 0xff00ff00) + // 这一句意思是,左侧开始算0~7位,保留;8~15位,全变0;16~23位,保留;24~31位,全变0 + // 0011 1111 0000 0000 1011 0111 0000 0000 + // (n & 0xff00ff00) >>> 8 这句就是上面的值,统一向右移动8位,变成: + // 0000 0000 0011 1111 0000 0000 1011 0111 + // + // + // (n & 0x00ff00ff) + // 这一句意思是,左侧开始算0~7位,全变0;8~15位,保留;16~23位,全变0;24~31位,保留 + // 0000 0000 0110 1010 0000 0000 0011 1001 + // (n & 0x00ff00ff) << 8 这句就是上面的值,统一向左移动8位,变成: + // 0110 1010 0000 0000 0011 1001 0000 0000 + // 那么 ((n & 0xff00ff00) >>> 8) | ((n & 0x00ff00ff) << 8) + // 什么效果?就是n的0~7位和8~15位交换了,16~23位和24~31位交换了 + // 0110 1010 0011 1111 0011 1001 1011 0111 + + // 也就是说,整个过程是n的左16位,和右16位交换 + // n的左16位的内部,左8位和右8位交换;n的右16位的内部,左8位和右8位交换 + // 接下来的一行,其实是,从左边开始算,0~7位内部,左4和右4交换;8~15位,左4和右4交换;... + // 接下来的一行,其实是,从左边开始算,0~3位内部,左2和右2交换;4~7位,左2和右2交换;... + // 最后的一行,其实是,从左边开始算,0~1位内部,左1和右1交换;2~3位,左1和右1交换;... + public static int reverseBits(int n) { + // n的高16位,和n的低16位,交换 + n = (n >>> 16) | (n << 16); + n = ((n & 0xff00ff00) >>> 8) | ((n & 0x00ff00ff) << 8); + n = ((n & 0xf0f0f0f0) >>> 4) | ((n & 0x0f0f0f0f) << 4); + n = ((n & 0xcccccccc) >>> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xaaaaaaaa) >>> 1) | ((n & 0x55555555) << 1); + return n; + } + +} diff --git a/大厂刷题班/class32/Problem_0191_NumberOf1Bits.java b/大厂刷题班/class32/Problem_0191_NumberOf1Bits.java new file mode 100644 index 0000000..f5973f0 --- /dev/null +++ b/大厂刷题班/class32/Problem_0191_NumberOf1Bits.java @@ -0,0 +1,26 @@ +package class32; + +public class Problem_0191_NumberOf1Bits { + + // n的二进制形式,有几个1? + public static int hammingWeight1(int n) { + int bits = 0; + int rightOne = 0; + while(n != 0) { + bits++; + rightOne = n & (-n); + n ^= rightOne; + } + return bits; + } + + public static int hammingWeight2(int n) { + n = (n & 0x55555555) + ((n >>> 1) & 0x55555555); + n = (n & 0x33333333) + ((n >>> 2) & 0x33333333); + n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f); + n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff); + n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff); + return n; + } + +} diff --git a/大厂刷题班/class32/Problem_0202_HappyNumber.java b/大厂刷题班/class32/Problem_0202_HappyNumber.java new file mode 100644 index 0000000..3e54bbc --- /dev/null +++ b/大厂刷题班/class32/Problem_0202_HappyNumber.java @@ -0,0 +1,59 @@ +package class32; + +import java.util.HashSet; +import java.util.TreeSet; + +public class Problem_0202_HappyNumber { + + public static boolean isHappy1(int n) { + HashSet set = new HashSet<>(); + while (n != 1) { + int sum = 0; + while (n != 0) { + int r = n % 10; + sum += r * r; + n /= 10; + } + n = sum; + if (set.contains(n)) { + break; + } + set.add(n); + } + return n == 1; + } + + // 实验代码 + public static TreeSet sum(int n) { + TreeSet set = new TreeSet<>(); + while (!set.contains(n)) { + set.add(n); + int sum = 0; + while (n != 0) { + sum += (n % 10) * (n % 10); + n /= 10; + } + n = sum; + } + return set; + } + + public static boolean isHappy2(int n) { + while (n != 1 && n != 4) { + int sum = 0; + while (n != 0) { + sum += (n % 10) * (n % 10); + n /= 10; + } + n = sum; + } + return n == 1; + } + + public static void main(String[] args) { + for (int i = 1; i < 1000; i++) { + System.out.println(sum(i)); + } + } + +} diff --git a/大厂刷题班/class32/Problem_0204_CountPrimes.java b/大厂刷题班/class32/Problem_0204_CountPrimes.java new file mode 100644 index 0000000..0245051 --- /dev/null +++ b/大厂刷题班/class32/Problem_0204_CountPrimes.java @@ -0,0 +1,30 @@ +package class32; + +public class Problem_0204_CountPrimes { + + public static int countPrimes(int n) { + if (n < 3) { + return 0; + } + // j已经不是素数了,f[j] = true; + boolean[] f = new boolean[n]; + int count = n / 2; // 所有偶数都不要,还剩几个数 + // 跳过了1、2 3、5、7、 + for (int i = 3; i * i < n; i += 2) { + if (f[i]) { + continue; + } + // 3 -> 3 * 3 = 9 3 * 5 = 15 3 * 7 = 21 + // 7 -> 7 * 7 = 49 7 * 9 = 63 + // 13 -> 13 * 13 13 * 15 + for (int j = i * i; j < n; j += 2 * i) { + if (!f[j]) { + --count; + f[j] = true; + } + } + } + return count; + } + +} diff --git a/大厂刷题班/class32/SequenceM.java b/大厂刷题班/class32/SequenceM.java new file mode 100644 index 0000000..daa764f --- /dev/null +++ b/大厂刷题班/class32/SequenceM.java @@ -0,0 +1,162 @@ +package class32; + +import java.util.Arrays; + +// 给定一个数组arr,arr[i] = j,表示第i号试题的难度为j。给定一个非负数M +// 想出一张卷子,对于任何相邻的两道题目,前一题的难度不能超过后一题的难度+M +// 返回所有可能的卷子种数 +public class SequenceM { + + // 纯暴力方法,生成所有排列,一个一个验证 + public static int ways1(int[] arr, int m) { + if (arr == null || arr.length == 0) { + return 0; + } + return process(arr, 0, m); + } + + public static int process(int[] arr, int index, int m) { + if (index == arr.length) { + for (int i = 1; i < index; i++) { + if (arr[i - 1] > arr[i] + m) { + return 0; + } + } + return 1; + } + int ans = 0; + for (int i = index; i < arr.length; i++) { + swap(arr, index, i); + ans += process(arr, index + 1, m); + swap(arr, index, i); + } + return ans; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 时间复杂度O(N * logN) + // 从左往右的动态规划 + 范围上二分 + public static int ways2(int[] arr, int m) { + if (arr == null || arr.length == 0) { + return 0; + } + Arrays.sort(arr); + int all = 1; + for (int i = 1; i < arr.length; i++) { + all = all * (num(arr, i - 1, arr[i] - m) + 1); + } + return all; + } + + // arr[0..r]上返回>=t的数有几个, 二分的方法 + // 找到 >=t 最左的位置a, 然后返回r - a + 1就是个数 + public static int num(int[] arr, int r, int t) { + int i = 0; + int j = r; + int m = 0; + int a = r + 1; + while (i <= j) { + m = (i + j) / 2; + if (arr[m] >= t) { + a = m; + j = m - 1; + } else { + i = m + 1; + } + } + return r - a + 1; + } + + // 时间复杂度O(N * logV) + // 从左往右的动态规划 + IndexTree + public static int ways3(int[] arr, int m) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + int min = Integer.MAX_VALUE; + for (int num : arr) { + max = Math.max(max, num); + min = Math.min(min, num); + } + IndexTree itree = new IndexTree(max - min + 2); + Arrays.sort(arr); + int a = 0; + int b = 0; + int all = 1; + itree.add(arr[0] - min + 1, 1); + for (int i = 1; i < arr.length; i++) { + a = arr[i] - min + 1; + b = i - (a - m - 1 >= 1 ? itree.sum(a - m - 1) : 0); + all = all * (b + 1); + itree.add(a, 1); + } + return all; + } + + // 注意开始下标是1,不是0 + public static class IndexTree { + + private int[] tree; + private int N; + + public IndexTree(int size) { + N = size; + tree = new int[N + 1]; + } + + public int sum(int index) { + int ret = 0; + while (index > 0) { + ret += tree[index]; + index -= index & -index; + } + return ret; + } + + public void add(int index, int d) { + while (index <= N) { + tree[index] += d; + index += index & -index; + } + } + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * (value + 1)); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int N = 10; + int value = 20; + int testTimes = 1000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int len = (int) (Math.random() * (N + 1)); + int[] arr = randomArray(len, value); + int m = (int) (Math.random() * (value + 1)); + int ans1 = ways1(arr, m); + int ans2 = ways2(arr, m); + int ans3 = ways3(arr, m); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class32/说明 b/大厂刷题班/class32/说明 new file mode 100644 index 0000000..4f72a2d --- /dev/null +++ b/大厂刷题班/class32/说明 @@ -0,0 +1,25 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0152 : 大厂刷题班, 第32节, 本节 +0155 : 体系学习班, 第3节第5题 +0160 : 体系学习班, 第10节第1题 +0162 : 体系学习班, 第1节第6题 +0163 : 大厂刷题班, 第32节, 本节 +0166 : 大厂刷题班, 第32节, 本节 +0169 : 大厂刷题班, 第23节第4题 +0171 : 大厂刷题班, 第32节, 本节 +0172 : 大厂刷题班, 第32节, 本节 +0179 : 体系学习班, 第13节第5题 +0188 : 大厂刷题班, 第15节第4题 +0189 : 大厂刷题班, 第32节, 本节 +0190 : 大厂刷题班, 第32节, 本节 +0191 : 大厂刷题班, 第32节, 本节 +0198 : 大厂刷题班, 第4节第4题 +0200 : 体系学习班, 第15节第2题、第3题 +0202 : 大厂刷题班, 第32节, 本节 +0204 : 大厂刷题班, 第32节, 本节 +补充题 SequenceM : 拼多多, 卷子合法数量问题 \ No newline at end of file diff --git a/大厂刷题班/class33/Problem_0207_CourseSchedule.java b/大厂刷题班/class33/Problem_0207_CourseSchedule.java new file mode 100644 index 0000000..282454a --- /dev/null +++ b/大厂刷题班/class33/Problem_0207_CourseSchedule.java @@ -0,0 +1,113 @@ +package class33; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; + +public class Problem_0207_CourseSchedule { + + // 一个node,就是一个课程 + // name是课程的编号 + // in是课程的入度 + public static class Course { + public int name; + public int in; + public ArrayList nexts; + + public Course(int n) { + name = n; + in = 0; + nexts = new ArrayList<>(); + } + } + + public static boolean canFinish1(int numCourses, int[][] prerequisites) { + if (prerequisites == null || prerequisites.length == 0) { + return true; + } + // 一个编号 对应 一个课的实例 + HashMap nodes = new HashMap<>(); + for (int[] arr : prerequisites) { + int to = arr[0]; + int from = arr[1]; // from -> to + if (!nodes.containsKey(to)) { + nodes.put(to, new Course(to)); + } + if (!nodes.containsKey(from)) { + nodes.put(from, new Course(from)); + } + Course t = nodes.get(to); + Course f = nodes.get(from); + f.nexts.add(t); + t.in++; + } + int needPrerequisiteNums = nodes.size(); + Queue zeroInQueue = new LinkedList<>(); + for (Course node : nodes.values()) { + if (node.in == 0) { + zeroInQueue.add(node); + } + } + int count = 0; + while (!zeroInQueue.isEmpty()) { + Course cur = zeroInQueue.poll(); + count++; + for (Course next : cur.nexts) { + if (--next.in == 0) { + zeroInQueue.add(next); + } + } + } + return count == needPrerequisiteNums; + } + + // 和方法1算法过程一样 + // 但是写法优化了 + public static boolean canFinish2(int courses, int[][] relation) { + if (relation == null || relation.length == 0) { + return true; + } + // 3 : 0 1 2 + // nexts : 0 {} + // 1 {} + // 2 {} + // 3 {0,1,2} + ArrayList> nexts = new ArrayList<>(); + for (int i = 0; i < courses; i++) { + nexts.add(new ArrayList<>()); + } + // 3 入度 1 in[3] == 1 + int[] in = new int[courses]; + for (int[] arr : relation) { + // arr[1] from arr[0] to + nexts.get(arr[1]).add(arr[0]); + in[arr[0]]++; + } + + // 队列 + int[] zero = new int[courses]; + // 该队列有效范围是[l,r) + // 新来的数,放哪?r位置,r++ + // 出队列的数,从哪拿?l位置,l++ + // l == r 队列无元素 l < r 队列有元素 + int l = 0; + int r = 0; + for (int i = 0; i < courses; i++) { + if (in[i] == 0) { + zero[r++] = i; + } + } + int count = 0; + while (l != r) { + count++; // zero[l] 出队列 l++ + for (int next : nexts.get(zero[l++])) { + if (--in[next] == 0) { + zero[r++] = next; + } + } + } + return count == nexts.size(); + } + +} diff --git a/大厂刷题班/class33/Problem_0210_CourseScheduleII.java b/大厂刷题班/class33/Problem_0210_CourseScheduleII.java new file mode 100644 index 0000000..33017c2 --- /dev/null +++ b/大厂刷题班/class33/Problem_0210_CourseScheduleII.java @@ -0,0 +1,71 @@ +package class33; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; + +public class Problem_0210_CourseScheduleII { + + public static class Node { + public int name; + public int in; + public ArrayList nexts; + + public Node(int n) { + name = n; + in = 0; + nexts = new ArrayList<>(); + } + } + + public int[] findOrder(int numCourses, int[][] prerequisites) { + int[] ans = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + ans[i] = i; + } + if (prerequisites == null || prerequisites.length == 0) { + return ans; + } + HashMap nodes = new HashMap<>(); + for (int[] arr : prerequisites) { + int to = arr[0]; + int from = arr[1]; + if (!nodes.containsKey(to)) { + nodes.put(to, new Node(to)); + } + if (!nodes.containsKey(from)) { + nodes.put(from, new Node(from)); + } + Node t = nodes.get(to); + Node f = nodes.get(from); + f.nexts.add(t); + t.in++; + } + int index = 0; + Queue zeroInQueue = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (!nodes.containsKey(i)) { + ans[index++] = i; + } else { + if (nodes.get(i).in == 0) { + zeroInQueue.add(nodes.get(i)); + } + } + } + int needPrerequisiteNums = nodes.size(); + int count = 0; + while (!zeroInQueue.isEmpty()) { + Node cur = zeroInQueue.poll(); + ans[index++] = cur.name; + count++; + for (Node next : cur.nexts) { + if (--next.in == 0) { + zeroInQueue.add(next); + } + } + } + return count == needPrerequisiteNums ? ans : new int[0]; + } + +} diff --git a/大厂刷题班/class33/Problem_0213_HouseRobberII.java b/大厂刷题班/class33/Problem_0213_HouseRobberII.java new file mode 100644 index 0000000..cc8dcb2 --- /dev/null +++ b/大厂刷题班/class33/Problem_0213_HouseRobberII.java @@ -0,0 +1,50 @@ +package class33; + +public class Problem_0213_HouseRobberII { + + // arr 长度大于等于1 + public static int pickMaxSum(int[] arr) { + int n = arr.length; + // dp[i] : arr[0..i]范围上,随意选择,但是,任何两数不能相邻。得到的最大累加和是多少? + int[] dp = new int[n]; + dp[0] = arr[0]; + dp[1] = Math.max(arr[0], arr[1]); + for (int i = 2; i < n; i++) { + int p1 = arr[i]; + int p2 = dp[i - 1]; + int p3 = arr[i] + dp[i - 2]; + dp[i] = Math.max(p1, Math.max(p2, p3)); + } + return dp[n - 1]; + } + + public static int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if (nums.length == 1) { + return nums[0]; + } + if (nums.length == 2) { + return Math.max(nums[0], nums[1]); + } + int pre2 = nums[0]; + int pre1 = Math.max(nums[0], nums[1]); + for (int i = 2; i < nums.length - 1; i++) { + int tmp = Math.max(pre1, nums[i] + pre2); + pre2 = pre1; + pre1 = tmp; + } + int ans1 = pre1; + pre2 = nums[1]; + pre1 = Math.max(nums[1], nums[2]); + for (int i = 3; i < nums.length; i++) { + int tmp = Math.max(pre1, nums[i] + pre2); + pre2 = pre1; + pre1 = tmp; + } + int ans2 = pre1; + return Math.max(ans1, ans2); + } + +} diff --git a/大厂刷题班/class33/Problem_0237_DeleteNodeInLinkedList.java b/大厂刷题班/class33/Problem_0237_DeleteNodeInLinkedList.java new file mode 100644 index 0000000..9aeab6c --- /dev/null +++ b/大厂刷题班/class33/Problem_0237_DeleteNodeInLinkedList.java @@ -0,0 +1,15 @@ +package class33; + +public class Problem_0237_DeleteNodeInLinkedList { + + public static class ListNode { + int val; + ListNode next; + } + + public void deleteNode(ListNode node) { + node.val = node.next.val; + node.next = node.next.next; + } + +} diff --git a/大厂刷题班/class33/Problem_0238_ProductOfArrayExceptSelf.java b/大厂刷题班/class33/Problem_0238_ProductOfArrayExceptSelf.java new file mode 100644 index 0000000..a605d46 --- /dev/null +++ b/大厂刷题班/class33/Problem_0238_ProductOfArrayExceptSelf.java @@ -0,0 +1,24 @@ +package class33; + +public class Problem_0238_ProductOfArrayExceptSelf { + + public int[] productExceptSelf(int[] nums) { + int n = nums.length; + int[] ans = new int[n]; + ans[0] = nums[0]; + for (int i = 1; i < n; i++) { + ans[i] = ans[i - 1] * nums[i]; + } + int right = 1; + for (int i = n - 1; i > 0; i--) { + ans[i] = ans[i - 1] * right; + right *= nums[i]; + } + ans[0] = right; + return ans; + } + + // 扩展 : 如果仅仅是不能用除号,把结果直接填在nums里呢? + // 解法:数一共几个0;每一个位得到结果就是,a / b,位运算替代 /,之前的课讲过(新手班) + +} diff --git a/大厂刷题班/class33/Problem_0242_ValidAnagram.java b/大厂刷题班/class33/Problem_0242_ValidAnagram.java new file mode 100644 index 0000000..c6f9e49 --- /dev/null +++ b/大厂刷题班/class33/Problem_0242_ValidAnagram.java @@ -0,0 +1,23 @@ +package class33; + +public class Problem_0242_ValidAnagram { + + public static boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] str1 = s.toCharArray(); + char[] str2 = t.toCharArray(); + int[] count = new int[256]; + for (char cha : str1) { + count[cha]++; + } + for (char cha : str2) { + if (--count[cha] < 0) { + return false; + } + } + return true; + } + +} diff --git a/大厂刷题班/class33/Problem_0251_Flatten2DVector.java b/大厂刷题班/class33/Problem_0251_Flatten2DVector.java new file mode 100644 index 0000000..6dd56e1 --- /dev/null +++ b/大厂刷题班/class33/Problem_0251_Flatten2DVector.java @@ -0,0 +1,53 @@ +package class33; + +public class Problem_0251_Flatten2DVector { + + public static class Vector2D { + private int[][] matrix; + private int row; + private int col; + private boolean curUse; + + public Vector2D(int[][] v) { + matrix = v; + row = 0; + col = -1; + curUse = true; + hasNext(); + } + + public int next() { + int ans = matrix[row][col]; + curUse = true; + hasNext(); + return ans; + } + + public boolean hasNext() { + if (row == matrix.length) { + return false; + } + if (!curUse) { + return true; + } + // (row,col)用过了 + if (col < matrix[row].length - 1) { + col++; + } else { + col = 0; + do { + row++; + } while (row < matrix.length && matrix[row].length == 0); + } + // 新的(row,col) + if (row != matrix.length) { + curUse = false; + return true; + } else { + return false; + } + } + + } + +} diff --git a/大厂刷题班/class33/Problem_0269_AlienDictionary.java b/大厂刷题班/class33/Problem_0269_AlienDictionary.java new file mode 100644 index 0000000..a89e295 --- /dev/null +++ b/大厂刷题班/class33/Problem_0269_AlienDictionary.java @@ -0,0 +1,65 @@ +package class33; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; + +public class Problem_0269_AlienDictionary { + + public static String alienOrder(String[] words) { + if (words == null || words.length == 0) { + return ""; + } + int N = words.length; + HashMap indegree = new HashMap<>(); + for (int i = 0; i < N; i++) { + for (char c : words[i].toCharArray()) { + indegree.put(c, 0); + } + } + HashMap> graph = new HashMap<>(); + for (int i = 0; i < N - 1; i++) { + char[] cur = words[i].toCharArray(); + char[] nex = words[i + 1].toCharArray(); + int len = Math.min(cur.length, nex.length); + int j = 0; + for (; j < len; j++) { + if (cur[j] != nex[j]) { + if (!graph.containsKey(cur[j])) { + graph.put(cur[j], new HashSet<>()); + } + if (!graph.get(cur[j]).contains(nex[j])) { + graph.get(cur[j]).add(nex[j]); + indegree.put(nex[j], indegree.get(nex[j]) + 1); + } + break; + } + } + if (j < cur.length && j == nex.length) { + return ""; + } + } + StringBuilder ans = new StringBuilder(); + Queue q = new LinkedList<>(); + for (Character key : indegree.keySet()) { + if (indegree.get(key) == 0) { + q.offer(key); + } + } + while (!q.isEmpty()) { + char cur = q.poll(); + ans.append(cur); + if (graph.containsKey(cur)) { + for (char next : graph.get(cur)) { + indegree.put(next, indegree.get(next) - 1); + if (indegree.get(next) == 0) { + q.offer(next); + } + } + } + } + return ans.length() == indegree.size() ? ans.toString() : ""; + } + +} diff --git a/大厂刷题班/class33/Problem_0277_FindTheCelebrity.java b/大厂刷题班/class33/Problem_0277_FindTheCelebrity.java new file mode 100644 index 0000000..a91abc4 --- /dev/null +++ b/大厂刷题班/class33/Problem_0277_FindTheCelebrity.java @@ -0,0 +1,38 @@ +package class33; + +public class Problem_0277_FindTheCelebrity { + + // 提交时不要提交这个函数,因为默认系统会给你这个函数 + // knows方法,自己不认识自己 + public static boolean knows(int x, int i) { + return true; + } + + // 只提交下面的方法 0 ~ n-1 + public int findCelebrity(int n) { + // 谁可能成为明星,谁就是cand + int cand = 0; + for (int i = 0; i < n; ++i) { + if (knows(cand, i)) { + cand = i; + } + } + // cand是什么?唯一可能是明星的人! + // 下一步就是验证,它到底是不是明星 + // 1) cand是不是不认识所有的人 cand...(右侧cand都不认识) + // 所以,只用验证 ....cand的左侧即可 + for (int i = 0; i < cand; ++i) { + if (knows(cand, i)) { + return -1; + } + } + // 2) 是不是所有的人都认识cand + for (int i = 0; i < n; ++i) { + if (!knows(i, cand)) { + return -1; + } + } + return cand; + } + +} diff --git a/大厂刷题班/class33/Problem_0279_PerfectSquares.java b/大厂刷题班/class33/Problem_0279_PerfectSquares.java new file mode 100644 index 0000000..645680b --- /dev/null +++ b/大厂刷题班/class33/Problem_0279_PerfectSquares.java @@ -0,0 +1,70 @@ +package class33; + +public class Problem_0279_PerfectSquares { + + // 暴力解 + public static int numSquares1(int n) { + int res = n, num = 2; + while (num * num <= n) { + int a = n / (num * num), b = n % (num * num); + res = Math.min(res, a + numSquares1(b)); + num++; + } + return res; + } + + // 1 : 1, 4, 9, 16, 25, 36, ... + // 4 : 7, 15, 23, 28, 31, 39, 47, 55, 60, 63, 71, ... + // 规律解 + // 规律一:个数不超过4 + // 规律二:出现1个的时候,显而易见 + // 规律三:任何数 % 8 == 7,一定是4个 + // 规律四:任何数消去4的因子之后,剩下rest,rest % 8 == 7,一定是4个 + public static int numSquares2(int n) { + int rest = n; + while (rest % 4 == 0) { + rest /= 4; + } + if (rest % 8 == 7) { + return 4; + } + int f = (int) Math.sqrt(n); + if (f * f == n) { + return 1; + } + for (int first = 1; first * first <= n; first++) { + int second = (int) Math.sqrt(n - first * first); + if (first * first + second * second == n) { + return 2; + } + } + return 3; + } + + // 数学解 + // 1)四平方和定理 + // 2)任何数消掉4的因子,结论不变 + public static int numSquares3(int n) { + while (n % 4 == 0) { + n /= 4; + } + if (n % 8 == 7) { + return 4; + } + for (int a = 0; a * a <= n; ++a) { + // a * a + b * b = n + int b = (int) Math.sqrt(n - a * a); + if (a * a + b * b == n) { + return (a > 0 && b > 0) ? 2 : 1; + } + } + return 3; + } + + public static void main(String[] args) { + for (int i = 1; i < 1000; i++) { + System.out.println(i + " , " + numSquares1(i)); + } + } + +} diff --git a/大厂刷题班/class33/Problem_0283_MoveZeroes.java b/大厂刷题班/class33/Problem_0283_MoveZeroes.java new file mode 100644 index 0000000..168e59d --- /dev/null +++ b/大厂刷题班/class33/Problem_0283_MoveZeroes.java @@ -0,0 +1,20 @@ +package class33; + +public class Problem_0283_MoveZeroes { + + public static void moveZeroes(int[] nums) { + int to = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + swap(nums, to++, i); + } + } + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + +} diff --git a/大厂刷题班/class33/说明 b/大厂刷题班/class33/说明 new file mode 100644 index 0000000..d91e8cc --- /dev/null +++ b/大厂刷题班/class33/说明 @@ -0,0 +1,32 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0206 : 太简单,跳过 +0207 : 大厂刷题班, 第33节, 本节 +0208 : 体系学习班, 第8节第1题 +0210 : 大厂刷题班, 第33节, 本节 +0212 : 大厂刷题班, 第26节第1题 +0213 : 大厂刷题班, 第33节, 本节 +0215 : 体系学习班, 第29节第1题 +0217 : 太简单,跳过 +0218 : 大厂刷题班, 第4节第8题 +0227 : 大厂刷题班, 第8节第1题 +0230 : 太简单了,跳过。二叉树基本遍历、Morris遍历都可以解决 +0234 : 体系学习班, 第9节第2题 +0236 : 体系学习班, 第13节第3题 扩展在 大厂刷题班, 第23节第1题 +0237 : 大厂刷题班, 第33节, 本节 +0238 : 大厂刷题班, 第33节, 本节 +0239 : 体系学习班, 第24节第1题 +0240 : 大厂刷题班, 第17节第1题 +0242 : 大厂刷题班, 第33节, 本节 +0251 : 大厂刷题班, 第33节, 本节 +0253 : 体系学习班, 第14节第3题 +0268 : 大厂刷题班, 第14节第6题 +0269 : 大厂刷题班, 第33节, 本节 +0277 : 大厂刷题班, 第33节, 本节 +0279 : 大厂刷题班, 第33节, 本节 +0283 : 大厂刷题班, 第33节, 本节 +0285 : 太简单了,跳过。用二叉树普通遍历或者Morris遍历都可以解决 \ No newline at end of file diff --git a/大厂刷题班/class34/Problem_0287_FindTheDuplicateNumber.java b/大厂刷题班/class34/Problem_0287_FindTheDuplicateNumber.java new file mode 100644 index 0000000..dcd64b5 --- /dev/null +++ b/大厂刷题班/class34/Problem_0287_FindTheDuplicateNumber.java @@ -0,0 +1,23 @@ +package class34; + +public class Problem_0287_FindTheDuplicateNumber { + + public static int findDuplicate(int[] nums) { + if (nums == null || nums.length < 2) { + return -1; + } + int slow = nums[0]; + int fast = nums[nums[0]]; + while (slow != fast) { + slow = nums[slow]; + fast = nums[nums[fast]]; + } + fast = 0; + while (slow != fast) { + fast = nums[fast]; + slow = nums[slow]; + } + return slow; + } + +} diff --git a/大厂刷题班/class34/Problem_0289_GameOfLife.java b/大厂刷题班/class34/Problem_0289_GameOfLife.java new file mode 100644 index 0000000..c986ef0 --- /dev/null +++ b/大厂刷题班/class34/Problem_0289_GameOfLife.java @@ -0,0 +1,43 @@ +package class34; + +// 有关这个游戏更有意思、更完整的内容: +// https://www.bilibili.com/video/BV1rJ411n7ri +// 也推荐这个up主 +public class Problem_0289_GameOfLife { + + public static void gameOfLife(int[][] board) { + int N = board.length; + int M = board[0].length; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + int neighbors = neighbors(board, i, j); + if (neighbors == 3 || (board[i][j] == 1 && neighbors == 2)) { + board[i][j] |= 2; + } + } + } + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + board[i][j] >>= 1; + } + } + } + + // b[i][j] 这个位置的数,周围有几个1 + public static int neighbors(int[][] b, int i, int j) { + return f(b, i - 1, j - 1) + + f(b, i - 1, j) + + f(b, i - 1, j + 1) + + f(b, i, j - 1) + + f(b, i, j + 1) + + f(b, i + 1, j - 1) + + f(b, i + 1, j) + + f(b, i + 1, j + 1); + } + + // b[i][j] 上面有1,就返回1,上面不是1,就返回0 + public static int f(int[][] b, int i, int j) { + return (i >= 0 && i < b.length && j >= 0 && j < b[0].length && (b[i][j] & 1) == 1) ? 1 : 0; + } + +} diff --git a/大厂刷题班/class34/Problem_0295_FindMedianFromDataStream.java b/大厂刷题班/class34/Problem_0295_FindMedianFromDataStream.java new file mode 100644 index 0000000..4fa279f --- /dev/null +++ b/大厂刷题班/class34/Problem_0295_FindMedianFromDataStream.java @@ -0,0 +1,46 @@ +package class34; + +import java.util.PriorityQueue; + +public class Problem_0295_FindMedianFromDataStream { + + class MedianFinder { + + private PriorityQueue maxh; + private PriorityQueue minh; + + public MedianFinder() { + maxh = new PriorityQueue<>((a, b) -> b - a); + minh = new PriorityQueue<>((a, b) -> a - b); + } + + public void addNum(int num) { + if (maxh.isEmpty() || maxh.peek() >= num) { + maxh.add(num); + } else { + minh.add(num); + } + balance(); + } + + public double findMedian() { + if (maxh.size() == minh.size()) { + return (double) (maxh.peek() + minh.peek()) / 2; + } else { + return maxh.size() > minh.size() ? maxh.peek() : minh.peek(); + } + } + + private void balance() { + if (Math.abs(maxh.size() - minh.size()) == 2) { + if (maxh.size() > minh.size()) { + minh.add(maxh.poll()); + } else { + maxh.add(minh.poll()); + } + } + } + + } + +} \ No newline at end of file diff --git a/大厂刷题班/class34/Problem_0315_CountOfSmallerNumbersAfterSelf.java b/大厂刷题班/class34/Problem_0315_CountOfSmallerNumbersAfterSelf.java new file mode 100644 index 0000000..7cd8821 --- /dev/null +++ b/大厂刷题班/class34/Problem_0315_CountOfSmallerNumbersAfterSelf.java @@ -0,0 +1,69 @@ +package class34; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0315_CountOfSmallerNumbersAfterSelf { + + public static class Node { + public int value; + public int index; + + public Node(int v, int i) { + value = v; + index = i; + } + } + + public static List countSmaller(int[] nums) { + List ans = new ArrayList<>(); + if (nums == null) { + return ans; + } + for (int i = 0; i < nums.length; i++) { + ans.add(0); + } + if (nums.length < 2) { + return ans; + } + Node[] arr = new Node[nums.length]; + for (int i = 0; i < arr.length; i++) { + arr[i] = new Node(nums[i], i); + } + process(arr, 0, arr.length - 1, ans); + return ans; + } + + public static void process(Node[] arr, int l, int r, List ans) { + if (l == r) { + return; + } + int mid = l + ((r - l) >> 1); + process(arr, l, mid, ans); + process(arr, mid + 1, r, ans); + merge(arr, l, mid, r, ans); + } + + public static void merge(Node[] arr, int l, int m, int r, List ans) { + Node[] help = new Node[r - l + 1]; + int i = help.length - 1; + int p1 = m; + int p2 = r; + while (p1 >= l && p2 >= m + 1) { + if (arr[p1].value > arr[p2].value) { + ans.set(arr[p1].index, ans.get(arr[p1].index) + p2 - m); + } + help[i--] = arr[p1].value > arr[p2].value ? arr[p1--] : arr[p2--]; + } + while (p1 >= l) { + help[i--] = arr[p1--]; + } + while (p2 >= m + 1) { + help[i--] = arr[p2--]; + } + for (i = 0; i < help.length; i++) { + arr[l + i] = help[i]; + } + } + +} diff --git a/大厂刷题班/class34/Problem_0324_WiggleSortII.java b/大厂刷题班/class34/Problem_0324_WiggleSortII.java new file mode 100644 index 0000000..bd3d929 --- /dev/null +++ b/大厂刷题班/class34/Problem_0324_WiggleSortII.java @@ -0,0 +1,186 @@ +package class34; + +public class Problem_0324_WiggleSortII { + + // 时间复杂度O(N),额外空间复杂度O(1) + public static void wiggleSort(int[] nums) { + if (nums == null || nums.length < 2) { + return; + } + int N = nums.length; + // 小 中 右 + findIndexNum(nums, 0, nums.length - 1, N / 2); + if ((N & 1) == 0) { + // R L -> L R + shuffle(nums, 0, nums.length - 1); + // R1 L1 R2 L2 R3 L3 R4 L4 + // L4 R4 L3 R3 L2 R2 L1 R1 -> 代码中的方式,可以的! + // L1 R1 L2 R2 L3 R3 L4 R4 -> 课上的分析,是不行的!不能两两交换! + reverse(nums, 0, nums.length - 1); + // 做个实验,如果把上一行的code注释掉(reverse过程),然后跑下面注释掉的for循环代码 + // for循环的代码就是两两交换,会发现对数器报错,说明两两交换是不行的, 必须整体逆序 +// for (int i = 0; i < nums.length; i += 2) { +// swap(nums, i, i + 1); +// } + } else { + shuffle(nums, 1, nums.length - 1); + } + } + + public static int findIndexNum(int[] arr, int L, int R, int index) { + int pivot = 0; + int[] range = null; + while (L < R) { + pivot = arr[L + (int) (Math.random() * (R - L + 1))]; + range = partition(arr, L, R, pivot); + if (index >= range[0] && index <= range[1]) { + return arr[index]; + } else if (index < range[0]) { + R = range[0] - 1; + } else { + L = range[1] + 1; + } + } + return arr[L]; + } + + public static int[] partition(int[] arr, int L, int R, int pivot) { + int less = L - 1; + int more = R + 1; + int cur = L; + while (cur < more) { + if (arr[cur] < pivot) { + swap(arr, ++less, cur++); + } else if (arr[cur] > pivot) { + swap(arr, cur, --more); + } else { + cur++; + } + } + return new int[] { less + 1, more - 1 }; + } + + public static void shuffle(int[] nums, int l, int r) { + while (r - l + 1 > 0) { + int lenAndOne = r - l + 2; + int bloom = 3; + int k = 1; + while (bloom <= lenAndOne / 3) { + bloom *= 3; + k++; + } + int m = (bloom - 1) / 2; + int mid = (l + r) / 2; + rotate(nums, l + m, mid, mid + m); + cycles(nums, l - 1, bloom, k); + l = l + bloom - 1; + } + } + + public static void cycles(int[] nums, int base, int bloom, int k) { + for (int i = 0, trigger = 1; i < k; i++, trigger *= 3) { + int next = (2 * trigger) % bloom; + int cur = next; + int record = nums[next + base]; + int tmp = 0; + nums[next + base] = nums[trigger + base]; + while (cur != trigger) { + next = (2 * cur) % bloom; + tmp = nums[next + base]; + nums[next + base] = record; + cur = next; + record = tmp; + } + } + } + + public static void rotate(int[] arr, int l, int m, int r) { + reverse(arr, l, m); + reverse(arr, m + 1, r); + reverse(arr, l, r); + } + + public static void reverse(int[] arr, int l, int r) { + while (l < r) { + swap(arr, l++, r--); + } + } + + public static void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + // 为了测试,暴力方法 + // 把arr全排列尝试一遍, + // 其中任何一个排列能做到 < > < > ... 返回true; + // 如果没有任何一个排列能做到,返回false; + public static boolean test(int[] arr) { + return process(arr, 0); + } + + // 为了测试 + public static boolean process(int[] arr, int index) { + if (index == arr.length) { + return valid(arr); + } + for (int i = index; i < arr.length; i++) { + swap(arr, index, i); + if (process(arr, index + 1)) { + return true; + } + swap(arr, index, i); + } + return false; + } + + // 为了测试 + public static boolean valid(int[] arr) { + boolean more = true; + for (int i = 1; i < arr.length; i++) { + if ((more && arr[i - 1] >= arr[i]) || (!more && arr[i - 1] <= arr[i])) { + return false; + } + more = !more; + } + return true; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * v); + } + return ans; + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 10; + int V = 10; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr1 = randomArray(n, V); + int[] arr2 = copyArray(arr1); + wiggleSort(arr1); + if (valid(arr1) != test(arr2)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class34/Problem_0326_PowerOfThree.java b/大厂刷题班/class34/Problem_0326_PowerOfThree.java new file mode 100644 index 0000000..48b59c1 --- /dev/null +++ b/大厂刷题班/class34/Problem_0326_PowerOfThree.java @@ -0,0 +1,14 @@ +package class34; + +public class Problem_0326_PowerOfThree { + + // 如果一个数字是3的某次幂,那么这个数一定只含有3这个质数因子 + // 1162261467是int型范围内,最大的3的幂,它是3的19次方 + // 这个1162261467只含有3这个质数因子,如果n也是只含有3这个质数因子,那么 + // 1162261467 % n == 0 + // 反之如果1162261467 % n != 0 说明n一定含有其他因子 + public static boolean isPowerOfThree(int n) { + return (n > 0 && 1162261467 % n == 0); + } + +} diff --git a/大厂刷题班/class34/Problem_0328_OddEvenLinkedList.java b/大厂刷题班/class34/Problem_0328_OddEvenLinkedList.java new file mode 100644 index 0000000..0718c20 --- /dev/null +++ b/大厂刷题班/class34/Problem_0328_OddEvenLinkedList.java @@ -0,0 +1,43 @@ +package class34; + +public class Problem_0328_OddEvenLinkedList { + + // 提交时不要提交这个类 + public static class ListNode { + int val; + ListNode next; + } + + public ListNode oddEvenList(ListNode head) { + ListNode firstOdd = null; + ListNode firstEven = null; + ListNode odd = null; + ListNode even = null; + ListNode next = null; + int count = 1; + while (head != null) { + next = head.next; + head.next = null; + if ((count & 1) == 1) { + firstOdd = firstOdd == null ? head : firstOdd; + if (odd != null) { + odd.next = head; + } + odd = head; + } else { + firstEven = firstEven == null ? head : firstEven; + if (even != null) { + even.next = head; + } + even = head; + } + count++; + head = next; + } + if (odd != null) { + odd.next = firstEven; + } + return firstOdd != null ? firstOdd : firstEven; + } + +} diff --git a/大厂刷题班/class34/Problem_0340_LongestSubstringWithAtMostKDistinctCharacters.java b/大厂刷题班/class34/Problem_0340_LongestSubstringWithAtMostKDistinctCharacters.java new file mode 100644 index 0000000..37124f5 --- /dev/null +++ b/大厂刷题班/class34/Problem_0340_LongestSubstringWithAtMostKDistinctCharacters.java @@ -0,0 +1,29 @@ +package class34; + +public class Problem_0340_LongestSubstringWithAtMostKDistinctCharacters { + + public static int lengthOfLongestSubstringKDistinct(String s, int k) { + if (s == null || s.length() == 0 || k < 1) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[] count = new int[256]; + int diff = 0; + int R = 0; + int ans = 0; + for (int i = 0; i < N; i++) { + // R 窗口的右边界 + while (R < N && (diff < k || (diff == k && count[str[R]] > 0))) { + diff += count[str[R]] == 0 ? 1 : 0; + count[str[R++]]++; + } + // R 来到违规的第一个位置 + ans = Math.max(ans, R - i); + diff -= count[str[i]] == 1 ? 1 : 0; + count[str[i]]--; + } + return ans; + } + +} diff --git a/大厂刷题班/class34/Problem_0341_FlattenNestedListIterator.java b/大厂刷题班/class34/Problem_0341_FlattenNestedListIterator.java new file mode 100644 index 0000000..a5bc724 --- /dev/null +++ b/大厂刷题班/class34/Problem_0341_FlattenNestedListIterator.java @@ -0,0 +1,110 @@ +package class34; + +import java.util.Iterator; +import java.util.List; +import java.util.Stack; + +public class Problem_0341_FlattenNestedListIterator { + + // 不要提交这个接口类 + public interface NestedInteger { + + // @return true if this NestedInteger holds a single integer, rather than a + // nested list. + public boolean isInteger(); + + // @return the single integer that this NestedInteger holds, if it holds a + // single integer + // Return null if this NestedInteger holds a nested list + public Integer getInteger(); + + // @return the nested list that this NestedInteger holds, if it holds a nested + // list + // Return null if this NestedInteger holds a single integer + public List getList(); + } + + public class NestedIterator implements Iterator { + + private List list; + private Stack stack; + private boolean used; + + public NestedIterator(List nestedList) { + list = nestedList; + stack = new Stack<>(); + stack.push(-1); + used = true; + hasNext(); + } + + @Override + public Integer next() { + Integer ans = null; + if (!used) { + ans = get(list, stack); + used = true; + hasNext(); + } + return ans; + } + + @Override + public boolean hasNext() { + if (stack.isEmpty()) { + return false; + } + if (!used) { + return true; + } + if (findNext(list, stack)) { + used = false; + } + return !used; + } + + private Integer get(List nestedList, Stack stack) { + int index = stack.pop(); + Integer ans = null; + if (!stack.isEmpty()) { + ans = get(nestedList.get(index).getList(), stack); + } else { + ans = nestedList.get(index).getInteger(); + } + stack.push(index); + return ans; + } + + private boolean findNext(List nestedList, Stack stack) { + int index = stack.pop(); + if (!stack.isEmpty() && findNext(nestedList.get(index).getList(), stack)) { + stack.push(index); + return true; + } + for (int i = index + 1; i < nestedList.size(); i++) { + if (pickFirst(nestedList.get(i), i, stack)) { + return true; + } + } + return false; + } + + private boolean pickFirst(NestedInteger nested, int position, Stack stack) { + if (nested.isInteger()) { + stack.add(position); + return true; + } else { + List actualList = nested.getList(); + for (int i = 0; i < actualList.size(); i++) { + if (pickFirst(actualList.get(i), i, stack)) { + stack.add(position); + return true; + } + } + } + return false; + } + + } + +} diff --git a/大厂刷题班/class34/Problem_0348_DesignTicTacToe.java b/大厂刷题班/class34/Problem_0348_DesignTicTacToe.java new file mode 100644 index 0000000..58f4851 --- /dev/null +++ b/大厂刷题班/class34/Problem_0348_DesignTicTacToe.java @@ -0,0 +1,47 @@ +package class34; + +public class Problem_0348_DesignTicTacToe { + + class TicTacToe { + private int[][] rows; + private int[][] cols; + private int[] leftUp; + private int[] rightUp; + private boolean[][] matrix; + private int N; + + public TicTacToe(int n) { + // rows[a][1] : 1这个人,在a行上,下了几个 + // rows[b][2] : 2这个人,在b行上,下了几个 + rows = new int[n][3]; //0 1 2 + cols = new int[n][3]; + // leftUp[2] = 7 : 2这个人,在左对角线上,下了7个 + leftUp = new int[3]; + // rightUp[1] = 9 : 1这个人,在右对角线上,下了9个 + rightUp = new int[3]; + matrix = new boolean[n][n]; + N = n; + } + + public int move(int row, int col, int player) { + if (matrix[row][col]) { + return 0; + } + matrix[row][col] = true; + rows[row][player]++; + cols[col][player]++; + if (row == col) { + leftUp[player]++; + } + if (row + col == N - 1) { + rightUp[player]++; + } + if (rows[row][player] == N || cols[col][player] == N || leftUp[player] == N || rightUp[player] == N) { + return player; + } + return 0; + } + + } + +} diff --git a/大厂刷题班/class34/Problem_0380_InsertDeleteGetRandom.java b/大厂刷题班/class34/Problem_0380_InsertDeleteGetRandom.java new file mode 100644 index 0000000..2a81aca --- /dev/null +++ b/大厂刷题班/class34/Problem_0380_InsertDeleteGetRandom.java @@ -0,0 +1,52 @@ +package class34; + +import java.util.HashMap; + +// 测试链接 : https://leetcode.com/problems/insert-delete-getrandom-o1/ +public class Problem_0380_InsertDeleteGetRandom { + + public class RandomizedSet { + + private HashMap keyIndexMap; + private HashMap indexKeyMap; + private int size; + + public RandomizedSet() { + keyIndexMap = new HashMap(); + indexKeyMap = new HashMap(); + size = 0; + } + + public boolean insert(int val) { + if (!keyIndexMap.containsKey(val)) { + keyIndexMap.put(val, size); + indexKeyMap.put(size++, val); + return true; + } + return false; + } + + public boolean remove(int val) { + if (keyIndexMap.containsKey(val)) { + int deleteIndex = keyIndexMap.get(val); + int lastIndex = --size; + int lastKey = indexKeyMap.get(lastIndex); + keyIndexMap.put(lastKey, deleteIndex); + indexKeyMap.put(deleteIndex, lastKey); + keyIndexMap.remove(val); + indexKeyMap.remove(lastIndex); + return true; + } + return false; + } + + public int getRandom() { + if (size == 0) { + return -1; + } + int randomIndex = (int) (Math.random() * size); + return indexKeyMap.get(randomIndex); + } + } + +} diff --git a/大厂刷题班/class34/Problem_0384_ShuffleAnArray.java b/大厂刷题班/class34/Problem_0384_ShuffleAnArray.java new file mode 100644 index 0000000..dfb0e0b --- /dev/null +++ b/大厂刷题班/class34/Problem_0384_ShuffleAnArray.java @@ -0,0 +1,34 @@ +package class34; + +public class Problem_0384_ShuffleAnArray { + + class Solution { + private int[] origin; + private int[] shuffle; + private int N; + + public Solution(int[] nums) { + origin = nums; + N = nums.length; + shuffle = new int[N]; + for (int i = 0; i < N; i++) { + shuffle[i] = origin[i]; + } + } + + public int[] reset() { + return origin; + } + + public int[] shuffle() { + for (int i = N - 1; i >= 0; i--) { + int r = (int) (Math.random() * (i + 1)); + int tmp = shuffle[r]; + shuffle[r] = shuffle[i]; + shuffle[i] = tmp; + } + return shuffle; + } + } + +} diff --git a/大厂刷题班/class34/说明 b/大厂刷题班/class34/说明 new file mode 100644 index 0000000..c23f435 --- /dev/null +++ b/大厂刷题班/class34/说明 @@ -0,0 +1,30 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0287 : 大厂刷题班, 第34节, 本节 +0289 : 大厂刷题班, 第34节, 本节 +0295 : 大厂刷题班, 第34节, 本节 +0297 : 体系学习班, 第11节第2题 +0300 : 大厂刷题班, 第9节第3题 +0308 : 体系学习班, 第32节第2题 +0309 : 大厂刷题班, 第15节第5题 +0315 : 大厂刷题班, 第34节, 本节 +0322 : 体系学习班, 硬币找零专题 : 第21节第2、3、4题, 第22节第2题, 第24节第4题 +0324 : 大厂刷题班, 第34节, 本节 +0326 : 大厂刷题班, 第34节, 本节 +0328 : 大厂刷题班, 第34节, 本节 +0329 : 大厂刷题班, 第1节第5题 +0334 : 大厂刷题班, 第9节第3题的变形, 问 : 最长递增子序列长度能否超过2而已, 跳过 +0340 : 大厂刷题班, 第34节, 本节 +0341 : 大厂刷题班, 第34节, 本节 +0344 : 太简单了, 跳过 +0348 : 大厂刷题班, 第34节, 本节 +0350 : 太简单了, 跳过 +0371 : 新手班, 第5节第3题 +0378 : 大厂刷题班, 第17节第2题 +0380 : 大厂刷题班, 第34节, 本节 +0384 : 大厂刷题班, 第34节, 本节 +0387 : 太简单了, 跳过 \ No newline at end of file diff --git a/大厂刷题班/class35/Code01_StringKth.java b/大厂刷题班/class35/Code01_StringKth.java new file mode 100644 index 0000000..9ed97dc --- /dev/null +++ b/大厂刷题班/class35/Code01_StringKth.java @@ -0,0 +1,96 @@ +package class35; + +import java.util.ArrayList; +import java.util.List; + +// 给定一个长度len,表示一共有几位 +// 所有字符都是小写(a~z),可以生成长度为1,长度为2, +// 长度为3...长度为len的所有字符串 +// 如果把所有字符串根据字典序排序,每个字符串都有所在的位置。 +// 给定一个字符串str,给定len,请返回str是总序列中的第几个 +// 比如len = 4,字典序的前几个字符串为: +// a aa aaa aaaa aaab ... aaaz ... azzz b ba baa baaa ... bzzz c ... +// a是这个序列中的第1个,bzzz是这个序列中的第36558个 +public class Code01_StringKth { + // 思路: + // cdb,总共长度为7,请问cdb是第几个? + // 第一位c : + // 以a开头,剩下长度为(0~6)的所有可能性有几个 + // + + // 以b开头,剩下长度为(0~6)的所有可能性有几个 + // + + // 以c开头,剩下长度为(0)的所有可能性有几个 + // 第二位d : + // + + // 以ca开头的情况下,剩下长度为(0~5)的所有可能性有几个 + // + + // 以cb开头的情况下,剩下长度为(0~5)的所有可能性有几个 + // + + // 以cc开头的情况下,剩下长度为(0~5)的所有可能性有几个 + // + + // 以cd开头的情况下,剩下长度为(0)的所有可能性有几个 + // 第三位b + // + + // 以cda开头的情况下,剩下长度为(0~4)的所有可能性有几个 + // + + // 以cdb开头的情况下,剩下长度为(0)的所有可能性有几个 + public static int kth(String s, int len) { + if (s == null || s.length() == 0 || s.length() > len) { + return -1; + } + char[] num = s.toCharArray(); + int ans = 0; + for (int i = 0, rest = len - 1; i < num.length; i++, rest--) { + ans += (num[i] - 'a') * f(rest) + 1; + } + return ans; + } + + // 不管以什么开头,剩下长度为(0~len)的所有可能性有几个 + public static int f(int len) { + int ans = 1; + for (int i = 1, base = 26; i <= len; i++, base *= 26) { + ans += base; + } + return ans; + } + + // 为了测试 + public static List all(int len) { + List ans = new ArrayList<>(); + for (int i = 1; i <= len; i++) { + char[] path = new char[i]; + process(path, 0, ans); + } + return ans; + } + + // 为了测试 + public static void process(char[] path, int index, List ans) { + if (index == path.length) { + ans.add(String.valueOf(path)); + } else { + for (char c = 'a'; c <= 'z'; c++) { + path[index] = c; + process(path, index + 1, ans); + } + } + } + + public static void main(String[] args) { + int len = 4; + // 暴力方法得到所有字符串 + List ans = all(len); + // 根据字典序排序,所有字符串都在其中 + ans.sort((a, b) -> a.compareTo(b)); + + String test = "bzzz"; + // 根据我们的方法算出test是第几个? + // 注意我们算出的第几个,是从1开始的 + // 而下标是从0开始的,所以变成index,还需要-1 + int index = kth(test, len) - 1; + // 验证 + System.out.println(ans.get(index)); + } + +} diff --git a/大厂刷题班/class35/Code02_MagicStone.java b/大厂刷题班/class35/Code02_MagicStone.java new file mode 100644 index 0000000..9e77c80 --- /dev/null +++ b/大厂刷题班/class35/Code02_MagicStone.java @@ -0,0 +1,49 @@ +package class35; + +import java.util.Arrays; + +// 来自小红书 +// [0,4,7] : 0表示这里石头没有颜色,如果变红代价是4,如果变蓝代价是7 +// [1,X,X] : 1表示这里石头已经是红,而且不能改颜色,所以后两个数X无意义 +// [2,X,X] : 2表示这里石头已经是蓝,而且不能改颜色,所以后两个数X无意义 +// 颜色只可能是0、1、2,代价一定>=0 +// 给你一批这样的小数组,要求最后必须所有石头都有颜色,且红色和蓝色一样多,返回最小代价 +// 如果怎么都无法做到所有石头都有颜色、且红色和蓝色一样多,返回-1 +public class Code02_MagicStone { + + public static int minCost(int[][] stones) { + int n = stones.length; + if ((n & 1) != 0) { + return -1; + } + Arrays.sort(stones, (a, b) -> a[0] == 0 && b[0] == 0 ? (b[1] - b[2] - a[1] + a[2]) : (a[0] - b[0])); + int zero = 0; + int red = 0; + int blue = 0; + int cost = 0; + for (int i = 0; i < n; i++) { + if (stones[i][0] == 0) { + zero++; + cost += stones[i][1]; + } else if (stones[i][0] == 1) { + red++; + } else { + blue++; + } + } + if (red > (n >> 1) || blue > (n >> 1)) { + return -1; + } + blue = zero - ((n >> 1) - red); + for (int i = 0; i < blue; i++) { + cost += stones[i][2] - stones[i][1]; + } + return cost; + } + + public static void main(String[] args) { + int[][] stones = { { 1, 5, 3 }, { 2, 7, 9 }, { 0, 6, 4 }, { 0, 7, 9 }, { 0, 2, 1 }, { 0, 5, 9 } }; + System.out.println(minCost(stones)); + } + +} diff --git a/大厂刷题班/class35/Code03_WatchMovieMaxTime.java b/大厂刷题班/class35/Code03_WatchMovieMaxTime.java new file mode 100644 index 0000000..137845c --- /dev/null +++ b/大厂刷题班/class35/Code03_WatchMovieMaxTime.java @@ -0,0 +1,136 @@ +package class35; + +import java.util.Arrays; + +// 来自小红书 +// 一场电影开始和结束时间可以用一个小数组来表示["07:30","12:00"] +// 已知有2000场电影开始和结束都在同一天,这一天从00:00开始到23:59结束 +// 一定要选3场完全不冲突的电影来观看,返回最大的观影时间 +// 如果无法选出3场完全不冲突的电影来观看,返回-1 +public class Code03_WatchMovieMaxTime { + + // 暴力方法,枚举前三场所有的可能全排列 + public static int maxEnjoy1(int[][] movies) { + if (movies.length < 3) { + return -1; + } + return process1(movies, 0); + } + + public static int process1(int[][] movies, int index) { + if (index == 3) { + int start = 0; + int watch = 0; + for (int i = 0; i < 3; i++) { + if (start > movies[i][0]) { + return -1; + } + watch += movies[i][1] - movies[i][0]; + start = movies[i][1]; + } + return watch; + } else { + int ans = -1; + for (int i = index; i < movies.length; i++) { + swap(movies, index, i); + ans = Math.max(ans, process1(movies, index + 1)); + swap(movies, index, i); + } + return ans; + } + } + + public static void swap(int[][] movies, int i, int j) { + int[] tmp = movies[i]; + movies[i] = movies[j]; + movies[j] = tmp; + } + + // 优化后的递归解 + public static int maxEnjoy2(int[][] movies) { + Arrays.sort(movies, (a, b) -> a[0] != b[0] ? (a[0] - b[0]) : (a[1] - b[1])); + return process2(movies, 0, 0, 3); + } + + public static int process2(int[][] movies, int index, int time, int rest) { + if (index == movies.length) { + return rest == 0 ? 0 : -1; + } + int p1 = process2(movies, index + 1, time, rest); + int next = movies[index][0] >= time && rest > 0 ? process2(movies, index + 1, movies[index][1], rest - 1) : -1; + int p2 = next != -1 ? (movies[index][1] - movies[index][0] + next) : -1; + return Math.max(p1, p2); + } + + // 记忆化搜索的动态规划 + public static int maxEnjoy3(int[][] movies) { + Arrays.sort(movies, (a, b) -> a[0] != b[0] ? (a[0] - b[0]) : (a[1] - b[1])); + + int max = 0; + for (int[] movie : movies) { + max = Math.max(max, movie[1]); + } + + int[][][] dp = new int[movies.length][max + 1][4]; + for (int i = 0; i < movies.length; i++) { + for (int j = 0; j <= max; j++) { + for (int k = 0; k <= 3; k++) { + dp[i][j][k] = -2;// 用-2代表没算过这个过程 + } + } + } + return process3(movies, 0, 0, 3, dp); + } + + public static int process3(int[][] movies, int index, int time, int rest, int[][][] dp) { + if (index == movies.length) { + return rest == 0 ? 0 : -1; + } + if (dp[index][time][rest] != -2) { + return dp[index][time][rest]; + } + int p1 = process3(movies, index + 1, time, rest, dp); + int next = movies[index][0] >= time && rest > 0 ? process3(movies, index + 1, movies[index][1], rest - 1, dp) + : -1; + int p2 = next != -1 ? (movies[index][1] - movies[index][0] + next) : -1; + int ans = Math.max(p1, p2); + dp[index][time][rest] = ans; + return ans; + } + + // 为了测试 + public static int[][] randomMovies(int len, int time) { + int[][] movies = new int[len][2]; + for (int i = 0; i < len; i++) { + int a = (int) (Math.random() * time); + int b = (int) (Math.random() * time); + movies[i][0] = Math.min(a, b); + movies[i][1] = Math.max(a, b); + } + return movies; + } + + public static void main(String[] args) { + int n = 10; + int t = 20; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n) + 1; + int[][] movies = randomMovies(len, t); + int ans1 = maxEnjoy1(movies); + int ans2 = maxEnjoy2(movies); + int ans3 = maxEnjoy3(movies); + if (ans1 != ans2 || ans1 != ans3) { + for (int[] m : movies) { + System.out.println(m[0] + " , " + m[1]); + } + System.out.println(ans1); + System.out.println(ans2); + System.out.println("出错了"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class35/Code04_WalkToEnd.java b/大厂刷题班/class35/Code04_WalkToEnd.java new file mode 100644 index 0000000..50ba235 --- /dev/null +++ b/大厂刷题班/class35/Code04_WalkToEnd.java @@ -0,0 +1,53 @@ +package class35; + +import java.util.PriorityQueue; + +// 来自网易 +// map[i][j] == 0,代表(i,j)是海洋,渡过的话代价是2 +// map[i][j] == 1,代表(i,j)是陆地,渡过的话代价是1 +// map[i][j] == 2,代表(i,j)是障碍,无法渡过 +// 每一步上、下、左、右都能走,返回从左上角走到右下角最小代价是多少,如果无法到达返回-1 +public class Code04_WalkToEnd { + + public static int minCost(int[][] map) { + if (map[0][0] == 2) { + return -1; + } + int n = map.length; + int m = map[0].length; + PriorityQueue heap = new PriorityQueue<>((a, b) -> a.cost - b.cost); + boolean[][] visited = new boolean[n][m]; + add(map, 0, 0, 0, heap, visited); + while (!heap.isEmpty()) { + Node cur = heap.poll(); + if (cur.row == n - 1 && cur.col == m - 1) { + return cur.cost; + } + add(map, cur.row - 1, cur.col, cur.cost, heap, visited); + add(map, cur.row + 1, cur.col, cur.cost, heap, visited); + add(map, cur.row, cur.col - 1, cur.cost, heap, visited); + add(map, cur.row, cur.col + 1, cur.cost, heap, visited); + } + return -1; + } + + public static void add(int[][] m, int i, int j, int pre, PriorityQueue heap, boolean[][] visited) { + if (i >= 0 && i < m.length && j >= 0 && j < m[0].length && m[i][j] != 2 && !visited[i][j]) { + heap.add(new Node(i, j, pre + (m[i][j] == 0 ? 2 : 1))); + visited[i][j] = true; + } + } + + public static class Node { + public int row; + public int col; + public int cost; + + public Node(int a, int b, int c) { + row = a; + col = b; + cost = c; + } + } + +} diff --git a/大厂刷题班/class35/Code05_CircleCandy.java b/大厂刷题班/class35/Code05_CircleCandy.java new file mode 100644 index 0000000..e027a13 --- /dev/null +++ b/大厂刷题班/class35/Code05_CircleCandy.java @@ -0,0 +1,58 @@ +package class35; + +// 来自网易 +// 给定一个正数数组arr,表示每个小朋友的得分 +// 任何两个相邻的小朋友,如果得分一样,怎么分糖果无所谓,但如果得分不一样,分数大的一定要比分数少的多拿一些糖果 +// 假设所有的小朋友坐成一个环形,返回在不破坏上一条规则的情况下,需要的最少糖果数 +public class Code05_CircleCandy { + + public static int minCandy(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return 1; + } + int n = arr.length; + int minIndex = 0; + for (int i = 0; i < n; i++) { + if (arr[i] <= arr[lastIndex(i, n)] && arr[i] <= arr[nextIndex(i, n)]) { + minIndex = i; + break; + } + } + int[] nums = new int[n + 1]; + for (int i = 0; i <= n; i++, minIndex = nextIndex(minIndex, n)) { + nums[i] = arr[minIndex]; + } + int[] left = new int[n + 1]; + left[0] = 1; + for (int i = 1; i <= n; i++) { + left[i] = nums[i] > nums[i - 1] ? (left[i - 1] + 1) : 1; + } + int[] right = new int[n + 1]; + right[n] = 1; + for (int i = n - 1; i >= 0; i--) { + right[i] = nums[i] > nums[i + 1] ? (right[i + 1] + 1) : 1; + } + int ans = 0; + for (int i = 0; i < n; i++) { + ans += Math.max(left[i], right[i]); + } + return ans; + } + + public static int nextIndex(int i, int n) { + return i == n - 1 ? 0 : (i + 1); + } + + public static int lastIndex(int i, int n) { + return i == 0 ? (n - 1) : (i - 1); + } + + public static void main(String[] args) { + int[] arr = { 3, 4, 2, 3, 2 }; + System.out.println(minCandy(arr)); + } + +} diff --git a/大厂刷题班/class35/Problem_0347_TopKFrequentElements.java b/大厂刷题班/class35/Problem_0347_TopKFrequentElements.java new file mode 100644 index 0000000..656e584 --- /dev/null +++ b/大厂刷题班/class35/Problem_0347_TopKFrequentElements.java @@ -0,0 +1,54 @@ +package class35; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.PriorityQueue; + +public class Problem_0347_TopKFrequentElements { + + public static class Node { + public int num; + public int count; + + public Node(int k) { + num = k; + count = 1; + } + } + + public static class CountComparator implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.count - o2.count; + } + + } + + public static int[] topKFrequent(int[] nums, int k) { + HashMap map = new HashMap<>(); + for (int num : nums) { + if (!map.containsKey(num)) { + map.put(num, new Node(num)); + } else { + map.get(num).count++; + } + } + PriorityQueue heap = new PriorityQueue<>(new CountComparator()); + for (Node node : map.values()) { + if (heap.size() < k || (heap.size() == k && node.count > heap.peek().count)) { + heap.add(node); + } + if (heap.size() > k) { + heap.poll(); + } + } + int[] ans = new int[k]; + int index = 0; + while (!heap.isEmpty()) { + ans[index++] = heap.poll().num; + } + return ans; + } + +} diff --git a/大厂刷题班/class35/Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters.java b/大厂刷题班/class35/Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters.java new file mode 100644 index 0000000..9298e38 --- /dev/null +++ b/大厂刷题班/class35/Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters.java @@ -0,0 +1,110 @@ +package class35; + +public class Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters { + + public static int longestSubstring1(String s, int k) { + char[] str = s.toCharArray(); + int N = str.length; + int max = 0; + for (int i = 0; i < N; i++) { + int[] count = new int[256]; + int collect = 0; + int satisfy = 0; + for (int j = i; j < N; j++) { + if (count[str[j]] == 0) { + collect++; + } + if (count[str[j]] == k - 1) { + satisfy++; + } + count[str[j]]++; + if (collect == satisfy) { + max = Math.max(max, j - i + 1); + } + } + } + return max; + } + + public static int longestSubstring2(String s, int k) { + char[] str = s.toCharArray(); + int N = str.length; + int max = 0; + for (int require = 1; require <= 26; require++) { + // 3种 + // a~z 出现次数 + int[] count = new int[26]; + // 目前窗口内收集了几种字符了 + int collect = 0; + // 目前窗口内出现次数>=k次的字符,满足了几种 + int satisfy = 0; + // 窗口右边界 + int R = -1; + for (int L = 0; L < N; L++) { // L要尝试每一个窗口的最左位置 + // [L..R] R+1 + while (R + 1 < N && !(collect == require && count[str[R + 1] - 'a'] == 0)) { + R++; + if (count[str[R] - 'a'] == 0) { + collect++; + } + if (count[str[R] - 'a'] == k - 1) { + satisfy++; + } + count[str[R] - 'a']++; + } + // [L...R] + if (satisfy == require) { + max = Math.max(max, R - L + 1); + } + // L++ + if (count[str[L] - 'a'] == 1) { + collect--; + } + if (count[str[L] - 'a'] == k) { + satisfy--; + } + count[str[L] - 'a']--; + } + } + return max; + } + + // 会超时,但是思路的确是正确的 + public static int longestSubstring3(String s, int k) { + return process(s.toCharArray(), 0, s.length() - 1, k); + } + + public static int process(char[] str, int L, int R, int k) { + if (L > R) { + return 0; + } + int[] counts = new int[26]; + for (int i = L; i <= R; i++) { + counts[str[i] - 'a']++; + } + char few = 0; + int min = Integer.MAX_VALUE; + for (int i = 0; i < 26; i++) { + if (counts[i] != 0 && min > counts[i]) { + few = (char) (i + 'a'); + min = counts[i]; + } + } + if (min >= k) { + return R - L + 1; + } + int pre = 0; + int max = Integer.MIN_VALUE; + for (int i = L; i <= R; i++) { + if (str[i] == few) { + max = Math.max(max, process(str, pre, i - 1, k)); + pre = i + 1; + } + } + if (pre != R + 1) { + max = Math.max(max, process(str, pre, R, k)); + } + return max; + } + +} diff --git a/大厂刷题班/class35/Problem_0412_FizzBuzz.java b/大厂刷题班/class35/Problem_0412_FizzBuzz.java new file mode 100644 index 0000000..82268d9 --- /dev/null +++ b/大厂刷题班/class35/Problem_0412_FizzBuzz.java @@ -0,0 +1,24 @@ +package class35; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0412_FizzBuzz { + + public static List fizzBuzz(int n) { + ArrayList ans = new ArrayList<>(); + for (int i = 1; i <= n; i++) { + if (i % 15 == 0) { + ans.add("FizzBuzz"); + } else if (i % 5 == 0) { + ans.add("Buzz"); + } else if (i % 3 == 0) { + ans.add("Fizz"); + } else { + ans.add(String.valueOf(i)); + } + } + return ans; + } + +} diff --git a/大厂刷题班/class35/Problem_0454_4SumII.java b/大厂刷题班/class35/Problem_0454_4SumII.java new file mode 100644 index 0000000..809fdfa --- /dev/null +++ b/大厂刷题班/class35/Problem_0454_4SumII.java @@ -0,0 +1,32 @@ +package class35; + +import java.util.HashMap; + +public class Problem_0454_4SumII { + + public static int fourSumCount(int[] A, int[] B, int[] C, int[] D) { + HashMap map = new HashMap<>(); + int sum = 0; + for (int i = 0; i < A.length; i++) { + for (int j = 0; j < B.length; j++) { + sum = A[i] + B[j]; + if (!map.containsKey(sum)) { + map.put(sum, 1); + } else { + map.put(sum, map.get(sum) + 1); + } + } + } + int ans = 0; + for (int i = 0; i < C.length; i++) { + for (int j = 0; j < D.length; j++) { + sum = C[i] + D[j]; + if (map.containsKey(-sum)) { + ans += map.get(-sum); + } + } + } + return ans; + } + +} diff --git a/大厂刷题班/class35/Problem_0673_NumberOfLongestIncreasingSubsequence.java b/大厂刷题班/class35/Problem_0673_NumberOfLongestIncreasingSubsequence.java new file mode 100644 index 0000000..72d76b2 --- /dev/null +++ b/大厂刷题班/class35/Problem_0673_NumberOfLongestIncreasingSubsequence.java @@ -0,0 +1,90 @@ +package class35; + +import java.util.ArrayList; +import java.util.TreeMap; + +public class Problem_0673_NumberOfLongestIncreasingSubsequence { + + // 好理解的方法,时间复杂度O(N^2) + public static int findNumberOfLIS1(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] lens = new int[n]; + int[] cnts = new int[n]; + lens[0] = 1; + cnts[0] = 1; + int maxLen = 1; + int allCnt = 1; + for (int i = 1; i < n; i++) { + int preLen = 0; + int preCnt = 1; + for (int j = 0; j < i; j++) { + if (nums[j] >= nums[i] || preLen > lens[j]) { + continue; + } + if (preLen < lens[j]) { + preLen = lens[j]; + preCnt = cnts[j]; + } else { + preCnt += cnts[j]; + } + } + lens[i] = preLen + 1; + cnts[i] = preCnt; + if (maxLen < lens[i]) { + maxLen = lens[i]; + allCnt = cnts[i]; + } else if (maxLen == lens[i]) { + allCnt += cnts[i]; + } + } + return allCnt; + } + + // 优化后的最优解,时间复杂度O(N*logN) + public static int findNumberOfLIS2(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + ArrayList> dp = new ArrayList<>(); + int len = 0; + int cnt = 0; + for (int num : nums) { + // num之前的长度,num到哪个长度len+1 + len = search(dp, num); + // cnt : 最终要去加底下的记录,才是应该填入的value + if (len == 0) { + cnt = 1; + } else { + TreeMap p = dp.get(len - 1); + cnt = p.firstEntry().getValue() - (p.ceilingKey(num) != null ? p.get(p.ceilingKey(num)) : 0); + } + if (len == dp.size()) { + dp.add(new TreeMap()); + dp.get(len).put(num, cnt); + } else { + dp.get(len).put(num, dp.get(len).firstEntry().getValue() + cnt); + } + } + return dp.get(dp.size() - 1).firstEntry().getValue(); + } + + // 二分查找,返回>=num最左的位置 + public static int search(ArrayList> dp, int num) { + int l = 0, r = dp.size() - 1, m = 0; + int ans = dp.size(); + while (l <= r) { + m = (l + r) / 2; + if (dp.get(m).firstKey() >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + +} diff --git a/大厂刷题班/class35/Problem_0687_LongestUnivaluePath.java b/大厂刷题班/class35/Problem_0687_LongestUnivaluePath.java new file mode 100644 index 0000000..c4466ed --- /dev/null +++ b/大厂刷题班/class35/Problem_0687_LongestUnivaluePath.java @@ -0,0 +1,62 @@ +package class35; + +public class Problem_0687_LongestUnivaluePath { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int v) { + val = v; + } + } + + public static int longestUnivaluePath(TreeNode root) { + if (root == null) { + return 0; + } + return process(root).max - 1; + } + + // 建设以x节点为头的树,返回两个信息 + public static class Info { + // 在一条路径上:要求每个节点通过且只通过一遍 + public int len; // 路径必须从x出发且只能往下走的情况下,路径的最大距离 + public int max; // 路径不要求必须从x出发的情况下,整棵树的合法路径最大距离 + + public Info(int l, int m) { + len = l; + max = m; + } + } + + private static Info process(TreeNode x) { + if (x == null) { + return new Info(0, 0); + } + TreeNode l = x.left; + TreeNode r = x.right; + // 左树上,不要求从左孩子出发,最大路径 + // 左树上,必须从左孩子出发,往下的最大路径 + Info linfo = process(l); + // 右树上,不要求从右孩子出发,最大路径 + // 右树上,必须从右孩子出发,往下的最大路径 + Info rinfo = process(r); + // 必须从x出发的情况下,往下的最大路径 + int len = 1; + if (l != null && l.val == x.val) { + len = linfo.len + 1; + } + if (r != null && r.val == x.val) { + len = Math.max(len, rinfo.len + 1); + } + // 不要求从x出发,最大路径 + int max = Math.max(Math.max(linfo.max, rinfo.max), len); + if (l != null && r != null && l.val == x.val && r.val == x.val) { + max = Math.max(max, linfo.len + rinfo.len + 1); + } + return new Info(len, max); + } + +} diff --git a/大厂刷题班/class35/说明 b/大厂刷题班/class35/说明 new file mode 100644 index 0000000..0c4a832 --- /dev/null +++ b/大厂刷题班/class35/说明 @@ -0,0 +1,21 @@ +leetcode高频题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top Interview Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=wpwgkgt +即可看到leetcode高频题全列表 +本节课解决leetcode高频题列表中的如下题目 : +0347 : 大厂刷题班, 第35节, 本节 +0395 : 大厂刷题班, 第35节, 本节 +0412 : 大厂刷题班, 第35节, 本节 +0454 : 大厂刷题班, 第35节, 本节 +0673 : 大厂刷题班, 第35节, 本节 +0687 : 大厂刷题班, 第35节, 本节 +0772 : 大厂刷题班, 第8节第1题 +至此,Leetcode高频题系列完结 + +本节附加题 +Code01 : 2021年8月大厂真实笔试题 +Code02 : 2021年8月大厂真实笔试题 +Code03 : 2021年8月大厂真实笔试题 +Code04 : 2021年8月大厂真实笔试题 +Code05 : 2021年8月大厂真实笔试题 \ No newline at end of file diff --git a/大厂刷题班/class36/Code01_ReverseInvertString.java b/大厂刷题班/class36/Code01_ReverseInvertString.java new file mode 100644 index 0000000..395b64a --- /dev/null +++ b/大厂刷题班/class36/Code01_ReverseInvertString.java @@ -0,0 +1,108 @@ +package class36; + +// 来自网易 +// 规定:L[1]对应a,L[2]对应b,L[3]对应c,...,L[25]对应y +// S1 = a +// S(i) = S(i-1) + L[i] + reverse(invert(S(i-1))); +// 解释invert操作: +// S1 = a +// S2 = aby +// 假设invert(S(2)) = 甲乙丙 +// a + 甲 = 26, 那么 甲 = 26 - 1 = 25 -> y +// b + 乙 = 26, 那么 乙 = 26 - 2 = 24 -> x +// y + 丙 = 26, 那么 丙 = 26 - 25 = 1 -> a +// 如上就是每一位的计算方式,所以invert(S2) = yxa +// 所以S3 = S2 + L[3] + reverse(invert(S2)) = aby + c + axy = abycaxy +// invert(abycaxy) = yxawyba, 再reverse = abywaxy +// 所以S4 = abycaxy + d + abywaxy = abycaxydabywaxy +// 直到S25结束 +// 给定两个参数n和k,返回Sn的第k位是什么字符,n从1开始,k从1开始 +// 比如n=4,k=2,表示S4的第2个字符是什么,返回b字符 +public class Code01_ReverseInvertString { + + public static int[] lens = null; + + public static void fillLens() { + lens = new int[26]; + lens[1] = 1; + for (int i = 2; i <= 25; i++) { + lens[i] = (lens[i - 1] << 1) + 1; + } + } + + // 求sn中的第k个字符 + // O(n), s <= 25 O(1) + public static char kth(int n, int k) { + if (lens == null) { + fillLens(); + } + if (n == 1) { // 无视k + return 'a'; + } + // sn half + int half = lens[n - 1]; + if (k <= half) { + return kth(n - 1, k); + } else if (k == half + 1) { + return (char) ('a' + n - 1); + } else { + // sn + // 我需要右半区,从左往右的第a个 + // 需要找到,s(n-1)从右往左的第a个 + // 当拿到字符之后,invert一下,就可以返回了! + return invert(kth(n - 1, ((half + 1) << 1) - k)); + } + } + + public static char invert(char c) { + return (char) (('a' << 1) + 24 - c); + } + + // 为了测试 + public static String generateString(int n) { + String s = "a"; + for (int i = 2; i <= n; i++) { + s = s + (char) ('a' + i - 1) + reverseInvert(s); + } + return s; + } + + // 为了测试 + public static String reverseInvert(String s) { + char[] invert = invert(s).toCharArray(); + for (int l = 0, r = invert.length - 1; l < r; l++, r--) { + char tmp = invert[l]; + invert[l] = invert[r]; + invert[r] = tmp; + } + return String.valueOf(invert); + } + + // 为了测试 + public static String invert(String s) { + char[] str = s.toCharArray(); + for (int i = 0; i < str.length; i++) { + str[i] = invert(str[i]); + } + return String.valueOf(str); + } + + // 为了测试 + public static void main(String[] args) { + int n = 20; + String str = generateString(n); + int len = str.length(); + System.out.println("测试开始"); + for (int i = 1; i <= len; i++) { + if (str.charAt(i - 1) != kth(n, i)) { + System.out.println(i); + System.out.println(str.charAt(i - 1)); + System.out.println(kth(n, i)); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class36/Code02_Ratio01Split.java b/大厂刷题班/class36/Code02_Ratio01Split.java new file mode 100644 index 0000000..e06b928 --- /dev/null +++ b/大厂刷题班/class36/Code02_Ratio01Split.java @@ -0,0 +1,68 @@ +package class36; + +import java.util.HashMap; + +// 来自京东 +// 把一个01字符串切成多个部分,要求每一部分的0和1比例一样,同时要求尽可能多的划分 +// 比如 : 01010101 +// 01 01 01 01 这是一种切法,0和1比例为 1 : 1 +// 0101 0101 也是一种切法,0和1比例为 1 : 1 +// 两种切法都符合要求,但是那么尽可能多的划分为第一种切法,部分数为4 +// 比如 : 00001111 +// 只有一种切法就是00001111整体作为一块,那么尽可能多的划分,部分数为1 +// 给定一个01字符串str,假设长度为N,要求返回一个长度为N的数组ans +// 其中ans[i] = str[0...i]这个前缀串,要求每一部分的0和1比例一样,同时要求尽可能多的划分下,部分数是多少 +// 输入: str = "010100001" +// 输出: ans = [1, 1, 1, 2, 1, 2, 1, 1, 3] +public class Code02_Ratio01Split { + + // 001010010100... + public static int[] split(int[] arr) { + + // key : 分子 + // value : 属于key的分母表, 每一个分母,及其 分子/分母 这个比例,多少个前缀拥有 + HashMap> pre = new HashMap<>(); + int n = arr.length; + int[] ans = new int[n]; + int zero = 0; // 0出现的次数 + int one = 0; // 1出现的次数 + for (int i = 0; i < n; i++) { + if (arr[i] == 0) { + zero++; + } else { + one++; + } + if (zero == 0 || one == 0) { + ans[i] = i + 1; + } else { // 0和1,都有数量 -> 最简分数 + int gcd = gcd(zero, one); + int a = zero / gcd; + int b = one / gcd; + // a / b 比例,之前有多少前缀拥有? 3+1 4 5+1 6 + if (!pre.containsKey(a)) { + pre.put(a, new HashMap<>()); + } + if (!pre.get(a).containsKey(b)) { + pre.get(a).put(b, 1); + } else { + pre.get(a).put(b, pre.get(a).get(b) + 1); + } + ans[i] = pre.get(a).get(b); + } + } + return ans; + } + + public static int gcd(int m, int n) { + return n == 0 ? m : gcd(n, m % n); + } + + public static void main(String[] args) { + int[] arr = { 0, 1, 0, 1, 0, 1, 1, 0 }; + int[] ans = split(arr); + for (int i = 0; i < ans.length; i++) { + System.out.print(ans[i] + " "); + } + } + +} diff --git a/大厂刷题班/class36/Code03_MatchCount.java b/大厂刷题班/class36/Code03_MatchCount.java new file mode 100644 index 0000000..b7746c0 --- /dev/null +++ b/大厂刷题班/class36/Code03_MatchCount.java @@ -0,0 +1,66 @@ +package class36; + +// 来自美团 +// 给定两个字符串s1和s2 +// 返回在s1中有多少个子串等于s2 +public class Code03_MatchCount { + + public static int sa(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() < s2.length()) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + return count(str1, str2); + } + + // 改写kmp为这道题需要的功能 + public static int count(char[] str1, char[] str2) { + int x = 0; + int y = 0; + int count = 0; + int[] next = getNextArray(str2); + while (x < str1.length) { + if (str1[x] == str2[y]) { + x++; + y++; + if (y == str2.length) { + count++; + y = next[y]; + } + } else if (next[y] == -1) { + x++; + } else { + y = next[y]; + } + } + return count; + } + + // next数组多求一位 + // 比如:str2 = aaaa + // 那么,next = -1,0,1,2,3 + // 最后一个3表示,终止位置之前的字符串最长前缀和最长后缀的匹配长度 + // 也就是next数组补一位 + public static int[] getNextArray(char[] str2) { + if (str2.length == 1) { + return new int[] { -1, 0 }; + } + int[] next = new int[str2.length + 1]; + next[0] = -1; + next[1] = 0; + int i = 2; + int cn = 0; + while (i < next.length) { + if (str2[i - 1] == str2[cn]) { + next[i++] = ++cn; + } else if (cn > 0) { + cn = next[cn]; + } else { + next[i++] = 0; + } + } + return next; + } + +} diff --git a/大厂刷题班/class36/Code04_ComputeExpressionValue.java b/大厂刷题班/class36/Code04_ComputeExpressionValue.java new file mode 100644 index 0000000..5e8c7dd --- /dev/null +++ b/大厂刷题班/class36/Code04_ComputeExpressionValue.java @@ -0,0 +1,58 @@ +package class36; + +// 来自美团 +// () 分值为2 +// (()) 分值为3 +// ((())) 分值为4 +// 也就是说,每包裹一层,分数就是里面的分值+1 +// ()() 分值为2 * 2 +// (())() 分值为3 * 2 +// 也就是说,每连接一段,分数就是各部分相乘,以下是一个结合起来的例子 +// (()())()(()) -> (2 * 2 + 1) * 2 * 3 -> 30 +// 给定一个括号字符串str,已知str一定是正确的括号结合,不会有违规嵌套 +// 返回分数 +public class Code04_ComputeExpressionValue { + + public static int sores(String s) { + return compute(s.toCharArray(), 0)[0]; + } + + // s[i.....] 遇到 ')' 或者 终止位置 停! + // 返回值:int[] 长度就是2 + // 0 :分数是多少 + // 1 : 来到了什么位置停的! + public static int[] compute(char[] s, int i) { + if (s[i] == ')') { + return new int[] { 1, i }; + } + int ans = 1; + while (i < s.length && s[i] != ')') { + int[] info = compute(s, i + 1); + ans *= info[0] + 1; + i = info[1] + 1; + } + return new int[] { ans, i }; + } + + public static void main(String[] args) { + + String str1 = "(()())()(())"; + System.out.println(sores(str1)); + + // (()()) + (((()))) + ((())()) + // (()()) -> 2 * 2 + 1 -> 5 + // (((()))) -> 5 + // ((())()) -> ((2 + 1) * 2) + 1 -> 7 + // 所以下面的结果应该是175 + String str2 = "(()())(((())))((())())"; + System.out.println(sores(str2)); + + // (()()()) + (()(())) + // (()()()) -> 2 * 2 * 2 + 1 -> 9 + // (()(())) -> 2 * 3 + 1 -> 7 + // 所以下面的结果应该是63 + String str3 = "(()()())(()(()))"; + System.out.println(sores(str3)); + } + +} diff --git a/大厂刷题班/class36/Code05_Query3Problems.java b/大厂刷题班/class36/Code05_Query3Problems.java new file mode 100644 index 0000000..68c68f5 --- /dev/null +++ b/大厂刷题班/class36/Code05_Query3Problems.java @@ -0,0 +1,132 @@ +package class36; + +// 来自美团 +// 给定一个数组arr,长度为N,做出一个结构,可以高效的做如下的查询 +// 1) int querySum(L,R) : 查询arr[L...R]上的累加和 +// 2) int queryAim(L,R) : 查询arr[L...R]上的目标值,目标值定义如下: +// 假设arr[L...R]上的值为[a,b,c,d],a+b+c+d = s +// 目标值为 : (s-a)^2 + (s-b)^2 + (s-c)^2 + (s-d)^2 +// 3) int queryMax(L,R) : 查询arr[L...R]上的最大值 +// 要求: +// 1) 初始化该结构的时间复杂度不能超过O(N*logN) +// 2) 三个查询的时间复杂度不能超过O(logN) +// 3) 查询时,认为arr的下标从1开始,比如 : +// arr = [ 1, 1, 2, 3 ]; +// querySum(1, 3) -> 4 +// queryAim(2, 4) -> 50 +// queryMax(1, 4) -> 3 +public class Code05_Query3Problems { + + public static class SegmentTree { + private int[] max; + private int[] change; + private boolean[] update; + + public SegmentTree(int N) { + max = new int[N << 2]; + change = new int[N << 2]; + update = new boolean[N << 2]; + for (int i = 0; i < max.length; i++) { + max[i] = Integer.MIN_VALUE; + } + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + // ln表示左子树元素结点个数,rn表示右子树结点个数 + private void pushDown(int rt, int ln, int rn) { + if (update[rt]) { + update[rt << 1] = true; + update[rt << 1 | 1] = true; + change[rt << 1] = change[rt]; + change[rt << 1 | 1] = change[rt]; + max[rt << 1] = change[rt]; + max[rt << 1 | 1] = change[rt]; + update[rt] = false; + } + } + + public void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + update[rt] = true; + change[rt] = C; + max[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return max[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int left = 0; + int right = 0; + if (L <= mid) { + left = query(L, R, l, mid, rt << 1); + } + if (R > mid) { + right = query(L, R, mid + 1, r, rt << 1 | 1); + } + return Math.max(left, right); + } + + } + + public static class Query { + public int[] sum1; + public int[] sum2; + public SegmentTree st; + public int m; + + public Query(int[] arr) { + int n = arr.length; + m = arr.length + 1; + sum1 = new int[m]; + sum2 = new int[m]; + st = new SegmentTree(m); + for (int i = 0; i < n; i++) { + sum1[i + 1] = sum1[i] + arr[i]; + sum2[i + 1] = sum2[i] + arr[i] * arr[i]; + st.update(i + 1, i + 1, arr[i], 1, m, 1); + } + + } + + public int querySum(int L, int R) { + return sum1[R] - sum1[L - 1]; + } + + public int queryAim(int L, int R) { + int sumPower2 = querySum(L, R); + sumPower2 *= sumPower2; + return sum2[R] - sum2[L - 1] + (R - L - 1) * sumPower2; + } + + public int queryMax(int L, int R) { + return st.query(L, R, 1, m, 1); + } + + } + + public static void main(String[] args) { + int[] arr = { 1, 1, 2, 3 }; + Query q = new Query(arr); + System.out.println(q.querySum(1, 3)); + System.out.println(q.queryAim(2, 4)); + System.out.println(q.queryMax(1, 4)); + } + +} diff --git a/大厂刷题班/class36/Code06_NodeWeight.java b/大厂刷题班/class36/Code06_NodeWeight.java new file mode 100644 index 0000000..22c429e --- /dev/null +++ b/大厂刷题班/class36/Code06_NodeWeight.java @@ -0,0 +1,48 @@ +package class36; + +import java.util.HashMap; + +// 来自美团 +// 有一棵树,给定头节点h,和结构数组m,下标0弃而不用 +// 比如h = 1, m = [ [] , [2,3], [4], [5,6], [], [], []] +// 表示1的孩子是2、3; 2的孩子是4; 3的孩子是5、6; 4、5和6是叶节点,都不再有孩子 +// 每一个节点都有颜色,记录在c数组里,比如c[i] = 4, 表示节点i的颜色为4 +// 一开始只有叶节点是有权值的,记录在w数组里, +// 比如,如果一开始就有w[i] = 3, 表示节点i是叶节点、且权值是3 +// 现在规定非叶节点i的权值计算方式: +// 根据i的所有直接孩子来计算,假设i的所有直接孩子,颜色只有a,b,k +// w[i] = Max { +// (颜色为a的所有孩子个数 + 颜色为a的孩子权值之和), +// (颜色为b的所有孩子个数 + 颜色为b的孩子权值之和), +// (颜色为k的所有孩子个数 + 颜色k的孩子权值之和) +// } +// 请计算所有孩子的权值并返回 +public class Code06_NodeWeight { + + // 当前来到h节点, + // h的直接孩子,在哪呢?m[h] = {a,b,c,d,e} + // 每个节点的颜色在哪?比如i号节点,c[i]就是i号节点的颜色 + // 每个节点的权值在哪?比如i号节点,w[i]就是i号节点的权值 + // void : 把w数组填满就是这个函数的目标 + public static void w(int h, int[][] m, int[] w, int[] c) { + if (m[h].length == 0) { // 叶节点 + return; + } + // 有若干个直接孩子 + // 1 7个 + // 3 10个 + HashMap colors = new HashMap(); + // 1 20 + // 3 45 + HashMap weihts = new HashMap(); + for (int child : m[h]) { + w(child, m, w, c); + colors.put(c[child], colors.getOrDefault(c[child], 0) + 1); + weihts.put(c[child], weihts.getOrDefault(c[child], 0) + w[child]); + } + for (int color : colors.keySet()) { + w[h] = Math.max(w[h], colors.get(color) + weihts.get(color)); + } + } + +} diff --git a/大厂刷题班/class36/Code07_PickAddMax.java b/大厂刷题班/class36/Code07_PickAddMax.java new file mode 100644 index 0000000..e0c2b1f --- /dev/null +++ b/大厂刷题班/class36/Code07_PickAddMax.java @@ -0,0 +1,80 @@ +package class36; + +import java.util.Arrays; + +// 来自腾讯 +// 给定一个数组arr,当拿走某个数a的时候,其他所有的数都+a +// 请返回最终所有数都拿走的最大分数 +// 比如: [2,3,1] +// 当拿走3时,获得3分,数组变成[5,4] +// 当拿走5时,获得5分,数组变成[9] +// 当拿走9时,获得9分,数组变成[] +// 这是最大的拿取方式,返回总分17 +public class Code07_PickAddMax { + + // 最优解 + public static int pick(int[] arr) { + Arrays.sort(arr); + int ans = 0; + for (int i = arr.length - 1; i >= 0; i--) { + ans = (ans << 1) + arr[i]; + } + return ans; + } + + // 纯暴力方法,为了测试 + public static int test(int[] arr) { + if (arr.length == 1) { + return arr[0]; + } + int ans = 0; + for (int i = 0; i < arr.length; i++) { + int[] rest = removeAddOthers(arr, i); + ans = Math.max(ans, arr[i] + test(rest)); + } + return ans; + } + + // 为了测试 + public static int[] removeAddOthers(int[] arr, int i) { + int[] rest = new int[arr.length - 1]; + int ri = 0; + for (int j = 0; j < i; j++) { + rest[ri++] = arr[j] + arr[i]; + } + for (int j = i + 1; j < arr.length; j++) { + rest[ri++] = arr[j] + arr[i]; + } + return rest; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int N = 7; + int V = 10; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N) + 1; + int[] arr = randomArray(len, V); + int ans1 = pick(arr); + int ans2 = test(arr); + if (ans1 != ans2) { + System.out.println(ans1); + System.out.println(ans2); + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class36/Code08_MinBoatEvenNumbers.java b/大厂刷题班/class36/Code08_MinBoatEvenNumbers.java new file mode 100644 index 0000000..2267013 --- /dev/null +++ b/大厂刷题班/class36/Code08_MinBoatEvenNumbers.java @@ -0,0 +1,98 @@ +package class36; + +import java.util.Arrays; + +// 来自腾讯 +// 给定一个正数数组arr,代表每个人的体重。给定一个正数limit代表船的载重,所有船都是同样的载重量 +// 每个人的体重都一定不大于船的载重 +// 要求: +// 1, 可以1个人单独一搜船 +// 2, 一艘船如果坐2人,两个人的体重相加需要是偶数,且总体重不能超过船的载重 +// 3, 一艘船最多坐2人 +// 返回如果想所有人同时坐船,船的最小数量 +public class Code08_MinBoatEvenNumbers { + + public static int minBoat(int[] arr, int limit) { + Arrays.sort(arr); + int odd = 0; + int even = 0; + for (int num : arr) { + if ((num & 1) == 0) { + even++; + } else { + odd++; + } + } + int[] odds = new int[odd]; + int[] evens = new int[even]; + for (int i = arr.length - 1; i >= 0; i--) { + if ((arr[i] & 1) == 0) { + evens[--even] = arr[i]; + } else { + odds[--odd] = arr[i]; + } + } + return min(odds, limit) + min(evens, limit); + } + + public static int min(int[] arr, int limit) { + if (arr == null || arr.length == 0) { + return 0; + } + int N = arr.length; + if (arr[N - 1] > limit) { + return -1; + } + int lessR = -1; + for (int i = N - 1; i >= 0; i--) { + if (arr[i] <= (limit / 2)) { + lessR = i; + break; + } + } + if (lessR == -1) { + return N; + } + int L = lessR; + int R = lessR + 1; + int noUsed = 0; + while (L >= 0) { + int solved = 0; + while (R < N && arr[L] + arr[R] <= limit) { + R++; + solved++; + } + if (solved == 0) { + noUsed++; + L--; + } else { + L = Math.max(-1, L - solved); + } + } + int all = lessR + 1; + int used = all - noUsed; + int moreUnsolved = (N - all) - used; + return used + ((noUsed + 1) >> 1) + moreUnsolved; + } + + // 首尾双指针的解法 + public static int numRescueBoats2(int[] people, int limit) { + Arrays.sort(people); + int ans = 0; + int l = 0; + int r = people.length - 1; + int sum = 0; + while (l <= r) { + sum = l == r ? people[l] : people[l] + people[r]; + if (sum > limit) { + r--; + } else { + l++; + r--; + } + ans++; + } + return ans; + } + +} diff --git a/大厂刷题班/class36/Code09_MaxKLenSequence.java b/大厂刷题班/class36/Code09_MaxKLenSequence.java new file mode 100644 index 0000000..0ba90a1 --- /dev/null +++ b/大厂刷题班/class36/Code09_MaxKLenSequence.java @@ -0,0 +1,87 @@ +package class36; + +import java.util.TreeSet; + +// 来自腾讯 +// 给定一个字符串str,和一个正数k +// 返回长度为k的所有子序列中,字典序最大的子序列 +public class Code09_MaxKLenSequence { + + public static String maxString(String s, int k) { + if (k <= 0 || s.length() < k) { + return ""; + } + char[] str = s.toCharArray(); + int n = str.length; + char[] stack = new char[n]; + int size = 0; + for (int i = 0; i < n; i++) { + while (size > 0 && stack[size - 1] < str[i] && size + n - i > k) { + size--; + } + if (size + n - i == k) { + return String.valueOf(stack, 0, size) + s.substring(i); + } + stack[size++] = str[i]; + } + return String.valueOf(stack, 0, k); + } + + // 为了测试 + public static String test(String str, int k) { + if (k <= 0 || str.length() < k) { + return ""; + } + TreeSet ans = new TreeSet<>(); + process(0, 0, str.toCharArray(), new char[k], ans); + return ans.last(); + } + + // 为了测试 + public static void process(int si, int pi, char[] str, char[] path, TreeSet ans) { + if (si == str.length) { + if (pi == path.length) { + ans.add(String.valueOf(path)); + } + } else { + process(si + 1, pi, str, path, ans); + if (pi < path.length) { + path[pi] = str[si]; + process(si + 1, pi + 1, str, path, ans); + } + } + } + + // 为了测试 + public static String randomString(int len, int range) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * range) + 'a'); + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int n = 12; + int r = 5; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * (n + 1)); + String str = randomString(len, r); + int k = (int) (Math.random() * (str.length() + 1)); + String ans1 = maxString(str, k); + String ans2 = test(str, k); + if (!ans1.equals(ans2)) { + System.out.println("出错了!"); + System.out.println(str); + System.out.println(k); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class36/Code10_StoneGameIV.java b/大厂刷题班/class36/Code10_StoneGameIV.java new file mode 100644 index 0000000..5bb9521 --- /dev/null +++ b/大厂刷题班/class36/Code10_StoneGameIV.java @@ -0,0 +1,63 @@ +package class36; + +// 来自哈喽单车 +// 本题是leetcode原题 : https://leetcode.com/problems/stone-game-iv/ +public class Code10_StoneGameIV { + + // 当前的!先手,会不会赢 + // 打表,不能发现规律 + public static boolean winnerSquareGame1(int n) { + if (n == 0) { + return false; + } + // 当前的先手,会尝试所有的情况,1,4,9,16,25,36.... + for (int i = 1; i * i <= n; i++) { + // 当前的先手,决定拿走 i * i 这个平方数 + // 它的对手会不会赢? winnerSquareGame1(n - i * i) + if (!winnerSquareGame1(n - i * i)) { + return true; + } + } + return false; + } + + public static boolean winnerSquareGame2(int n) { + int[] dp = new int[n + 1]; + dp[0] = -1; + return process2(n, dp); + } + + public static boolean process2(int n, int[] dp) { + if (dp[n] != 0) { + return dp[n] == 1 ? true : false; + } + boolean ans = false; + for (int i = 1; i * i <= n; i++) { + if (!process2(n - i * i, dp)) { + ans = true; + break; + } + } + dp[n] = ans ? 1 : -1; + return ans; + } + + public static boolean winnerSquareGame3(int n) { + boolean[] dp = new boolean[n + 1]; + for (int i = 1; i <= n; i++) { + for (int j = 1; j * j <= i; j++) { + if (!dp[i - j * j]) { + dp[i] = true; + break; + } + } + } + return dp[n]; + } + + public static void main(String[] args) { + int n = 10000000; + System.out.println(winnerSquareGame3(n)); + } + +} diff --git a/大厂刷题班/class36/Code11_BusRoutes.java b/大厂刷题班/class36/Code11_BusRoutes.java new file mode 100644 index 0000000..2fb7af3 --- /dev/null +++ b/大厂刷题班/class36/Code11_BusRoutes.java @@ -0,0 +1,59 @@ +package class36; + +import java.util.ArrayList; +import java.util.HashMap; + +// 来自三七互娱 +// Leetcode原题 : https://leetcode.com/problems/bus-routes/ +public class Code11_BusRoutes { + + // 0 : [1,3,7,0] + // 1 : [7,9,6,2] + // .... + // 返回:返回换乘几次+1 -> 返回一共坐了多少条线的公交。 + public static int numBusesToDestination(int[][] routes, int source, int target) { + if (source == target) { + return 0; + } + int n = routes.length; + // key : 车站 + // value : list -> 该车站拥有哪些线路! + HashMap> map = new HashMap<>(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < routes[i].length; j++) { + if (!map.containsKey(routes[i][j])) { + map.put(routes[i][j], new ArrayList<>()); + } + map.get(routes[i][j]).add(i); + } + } + ArrayList queue = new ArrayList<>(); + boolean[] set = new boolean[n]; + for (int route : map.get(source)) { + queue.add(route); + set[route] = true; + } + int len = 1; + while (!queue.isEmpty()) { + ArrayList nextLevel = new ArrayList<>(); + for (int route : queue) { + int[] bus = routes[route]; + for (int station : bus) { + if (station == target) { + return len; + } + for (int nextRoute : map.get(station)) { + if (!set[nextRoute]) { + nextLevel.add(nextRoute); + set[nextRoute] = true; + } + } + } + } + queue = nextLevel; + len++; + } + return -1; + } + +} diff --git a/大厂刷题班/class36/说明 b/大厂刷题班/class36/说明 new file mode 100644 index 0000000..97dd816 --- /dev/null +++ b/大厂刷题班/class36/说明 @@ -0,0 +1,12 @@ +Code01 : 2021年8月大厂真实笔试题 +Code02 : 2021年8月大厂真实笔试题 +Code03 : 2021年8月大厂真实笔试题 +Code04 : 2021年8月大厂真实笔试题 +Code05 : 2021年8月大厂真实笔试题 +Code06 : 2021年8月大厂真实笔试题 +Code07 : 2021年8月大厂真实笔试题 +Code08 : 2021年8月大厂真实笔试题 +Code09 : 2021年8月大厂真实笔试题 +Code10 : 2021年8月大厂真实笔试题 +Code11 : 2021年8月大厂真实笔试题 +Code12 : 2021年8月大厂真实笔试题 \ No newline at end of file diff --git a/大厂刷题班/class37/Code01_ArrangeProject.java b/大厂刷题班/class37/Code01_ArrangeProject.java new file mode 100644 index 0000000..a448960 --- /dev/null +++ b/大厂刷题班/class37/Code01_ArrangeProject.java @@ -0,0 +1,50 @@ +package class37; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; + +// 来自网易 +// 刚入职网易互娱,新人mini项目便如火如荼的开展起来。为了更好的项目协作与管理, +// 小易决定将学到的甘特图知识用于mini项目时间预估。小易先把项目中每一项工作以任务的形式列举出来, +// 每项任务有一个预计花费时间与前置任务表,必须完成了该任务的前置任务才能着手去做该任务。 +// 作为经验PM,小易把任务划分得井井有条,保证没有前置任务或者前置任务全数完成的任务,都可以同时进行。 +// 小易给出了这样一个任务表,请作为程序的你计算需要至少多长时间才能完成所有任务。 +// 输入第一行为一个正整数T,表示数据组数。 +// 对于接下来每组数据,第一行为一个正整数N,表示一共有N项任务。 +// 接下来N行,每行先有两个整数Di和Ki,表示完成第i个任务的预计花费时间为Di天,该任务有Ki个前置任务。 +// 之后为Ki个整数Mj,表示第Mj个任务是第i个任务的前置任务。 +// 数据范围:对于所有数据,满足1<=T<=3, 1<=N, Mj<=100000, 0<=Di<=1000, 0<=sum(Ki)<=N*2。 +public class Code01_ArrangeProject { + + public static int dayCount(ArrayList[] nums, int[] days, int[] headCount) { + Queue head = countHead(headCount); + int maxDay = 0; + int[] countDay = new int[days.length]; + while (!head.isEmpty()) { + int cur = head.poll(); + countDay[cur] += days[cur]; + for (int j = 0; j < nums[cur].size(); j++) { + headCount[nums[cur].get(j)]--; + if (headCount[nums[cur].get(j)] == 0) { + head.offer(nums[cur].get(j)); + } + countDay[nums[cur].get(j)] = Math.max(countDay[nums[cur].get(j)], countDay[cur]); + } + } + for (int i = 0; i < countDay.length; i++) { + maxDay = Math.max(maxDay, countDay[i]); + } + return maxDay; + } + + private static Queue countHead(int[] headCount) { + Queue queue = new LinkedList<>(); + for (int i = 0; i < headCount.length; i++) { + if (headCount[i] == 0) + queue.offer(i); // 没有前驱任务 + } + return queue; + } + +} diff --git a/大厂刷题班/class37/Code02_GameForEveryStepWin.java b/大厂刷题班/class37/Code02_GameForEveryStepWin.java new file mode 100644 index 0000000..077707f --- /dev/null +++ b/大厂刷题班/class37/Code02_GameForEveryStepWin.java @@ -0,0 +1,111 @@ +package class37; + +// 来自字节 +// 扑克牌中的红桃J和梅花Q找不到了,为了利用剩下的牌做游戏,小明设计了新的游戏规则 +// 1) A,2,3,4....10,J,Q,K分别对应1到13这些数字,大小王对应0 +// 2) 游戏人数为2人,轮流从牌堆里摸牌,每次摸到的牌只有“保留”和“使用”两个选项,且当前轮必须做出选择 +// 3) 如果选择“保留”当前牌,那么当前牌的分数加到总分里,并且可以一直持续到游戏结束 +// 4) 如果选择“使用”当前牌,那么当前牌的分数*3,加到总分上去,但是只有当前轮,下一轮,下下轮生效,之后轮效果消失。 +// 5) 每一轮总分大的人获胜 +// 假设小明知道每一轮对手做出选择之后的总分,返回小明在每一轮都赢的情况下,最终的最大分是多少 +// 如果小明怎么都无法保证每一轮都赢,返回-1 +public class Code02_GameForEveryStepWin { + +// public static max(int[] cands, int[] sroces) { +// return f(cands, sroces, 0, 0, 0, 0); +// } + + // 当前来到index位置,牌是cands[index]值 + // 对手第i轮的得分,sroces[i] + // int hold : i之前保留的牌的总分 + // int cur : 当前轮得到的,之前的牌只算上使用的效果,加成是多少 + // int next : 之前的牌,对index的下一轮,使用效果加成是多少 + // 返回值:如果i...最后,不能全赢,返回-1 + // 如果i...最后,能全赢,返回最后一轮的最大值 + + // index -> 26种 + // hold -> (1+2+3+..13) -> 91 -> 91 * 4 - (11 + 12) -> 341 + // cur -> 26 + // next -> 13 + // 26 * 341 * 26 * 13 -> ? * (10 ^ 5) + public static int f(int[] cands, int[] sroces, int index, int hold, int cur, int next) { + if (index == 25) { // 最后一张 + int all = hold + cur + cands[index] * 3; + if (all <= sroces[index]) { + return -1; + } + return all; + } + // 不仅最后一张 + // 保留 + int all1 = hold + cur + cands[index]; + int p1 = -1; + if (all1 > sroces[index]) { + p1 = f(cands, sroces, index + 1, hold + cands[index], next, 0); + } + // 爆发 + int all2 = hold + cur + cands[index] * 3; + int p2 = -1; + if (all2 > sroces[index]) { + p2 = f(cands, sroces, index + 1, hold, next + cands[index] * 3, cands[index] * 3); + } + return Math.max(p1, p2); + } + + // 26 * 341 * 78 * 39 = 2 * (10 ^ 7) + public static int process(int[] cards, int[] scores, int index, int hold, int cur, int next) { + if (index == 25) { + int all = hold + cur + cards[index] * 3; + if (all > scores[index]) { + return all; + } else { + return -1; + } + } else { + int d1 = hold + cur + cards[index]; + int p1 = -1; + if (d1 > scores[index]) { + p1 = process(cards, scores, index + 1, hold + cards[index], next, 0); + } + int d2 = hold + cur + cards[index] * 3; + int p2 = -1; + if (d2 > scores[index]) { + p2 = process(cards, scores, index + 1, hold, next + cards[index] * 3, cards[index] * 3); + } + return Math.max(p1, p2); + } + } + + + + // cur -> 牌点数 -> * 3 之后是效果 + // next -> 牌点数 -> * 3之后是效果 + public static int p(int[] cands, int[] sroces, int index, int hold, int cur, int next) { + if (index == 25) { // 最后一张 + int all = hold + cur * 3 + cands[index] * 3; + if (all <= sroces[index]) { + return -1; + } + return all; + } + // 不仅最后一张 + // 保留 + int all1 = hold + cur * 3 + cands[index]; + int p1 = -1; + if (all1 > sroces[index]) { + p1 = f(cands, sroces, index + 1, hold + cands[index], next, 0); + } + // 爆发 + int all2 = hold + cur * 3 + cands[index] * 3; + int p2 = -1; + if (all2 > sroces[index]) { + p2 = f(cands, sroces, index + 1, hold, next + cands[index], cands[index]); + } + return Math.max(p1, p2); + } + + // 改出动态规划,记忆化搜索! + + + +} diff --git a/大厂刷题班/class37/Problem_0114_FlattenBinaryTreeToLinkedList.java b/大厂刷题班/class37/Problem_0114_FlattenBinaryTreeToLinkedList.java new file mode 100644 index 0000000..b6ba6ec --- /dev/null +++ b/大厂刷题班/class37/Problem_0114_FlattenBinaryTreeToLinkedList.java @@ -0,0 +1,93 @@ +package class37; + +// 注意,我们课上讲了一个别的题,并不是leetcode 114 +// 我们课上讲的是,把一棵搜索二叉树变成有序链表,怎么做 +// 而leetcode 114是,把一棵树先序遍历的结果串成链表 +// 所以我更新了代码,这个代码是leetcode 114的实现 +// 利用morris遍历 +public class Problem_0114_FlattenBinaryTreeToLinkedList { + + // 这个类不用提交 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int value) { + val = value; + } + } + + // 普通解 + public static void flatten1(TreeNode root) { + process(root); + } + + public static class Info { + public TreeNode head; + public TreeNode tail; + + public Info(TreeNode h, TreeNode t) { + head = h; + tail = t; + } + } + + public static Info process(TreeNode head) { + if (head == null) { + return null; + } + Info leftInfo = process(head.left); + Info rightInfo = process(head.right); + head.left = null; + head.right = leftInfo == null ? null : leftInfo.head; + TreeNode tail = leftInfo == null ? head : leftInfo.tail; + tail.right = rightInfo == null ? null : rightInfo.head; + tail = rightInfo == null ? tail : rightInfo.tail; + return new Info(head, tail); + } + + // Morris遍历的解 + public static void flatten2(TreeNode root) { + if (root == null) { + return; + } + TreeNode pre = null; + TreeNode cur = root; + TreeNode mostRight = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + if (pre != null) { + pre.left = cur; + } + pre = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } else { + if (pre != null) { + pre.left = cur; + } + pre = cur; + } + cur = cur.right; + } + cur = root; + TreeNode next = null; + while (cur != null) { + next = cur.left; + cur.left = null; + cur.right = next; + cur = next; + } + } + +} diff --git a/大厂刷题班/class37/Problem_0221_MaximalSquare.java b/大厂刷题班/class37/Problem_0221_MaximalSquare.java new file mode 100644 index 0000000..38bfe06 --- /dev/null +++ b/大厂刷题班/class37/Problem_0221_MaximalSquare.java @@ -0,0 +1,40 @@ +package class37; + +public class Problem_0221_MaximalSquare { + + public static int maximalSquare(char[][] m) { + if (m == null || m.length == 0 || m[0].length == 0) { + return 0; + } + int N = m.length; + int M = m[0].length; + int[][] dp = new int[N + 1][M + 1]; + int max = 0; + for (int i = 0; i < N; i++) { + if (m[i][0] == '1') { + dp[i][0] = 1; + max = 1; + } + } + for (int j = 1; j < M; j++) { + if (m[0][j] == '1') { + dp[0][j] = 1; + max = 1; + } + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + if (m[i][j] == '1') { + dp[i][j] = Math.min( + Math.min(dp[i - 1][j], + dp[i][j - 1]), + dp[i - 1][j - 1]) + + 1; + max = Math.max(max, dp[i][j]); + } + } + } + return max * max; + } + +} diff --git a/大厂刷题班/class37/Problem_0226_InvertBinaryTree.java b/大厂刷题班/class37/Problem_0226_InvertBinaryTree.java new file mode 100644 index 0000000..d48dd9d --- /dev/null +++ b/大厂刷题班/class37/Problem_0226_InvertBinaryTree.java @@ -0,0 +1,21 @@ +package class37; + +public class Problem_0226_InvertBinaryTree { + + public class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + public static TreeNode invertTree(TreeNode root) { + if (root == null) { + return null; + } + TreeNode left = root.left; + root.left = invertTree(root.right); + root.right = invertTree(left); + return root; + } + +} diff --git a/大厂刷题班/class37/Problem_0337_HouseRobberIII.java b/大厂刷题班/class37/Problem_0337_HouseRobberIII.java new file mode 100644 index 0000000..88bc718 --- /dev/null +++ b/大厂刷题班/class37/Problem_0337_HouseRobberIII.java @@ -0,0 +1,37 @@ +package class37; + +public class Problem_0337_HouseRobberIII { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + public static int rob(TreeNode root) { + Info info = process(root); + return Math.max(info.no, info.yes); + } + + public static class Info { + public int no; + public int yes; + + public Info(int n, int y) { + no = n; + yes = y; + } + } + + public static Info process(TreeNode x) { + if (x == null) { + return new Info(0, 0); + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int no = Math.max(leftInfo.no, leftInfo.yes) + Math.max(rightInfo.no, rightInfo.yes); + int yes = x.val + leftInfo.no + rightInfo.no; + return new Info(no, yes); + } + +} diff --git a/大厂刷题班/class37/Problem_0394_DecodeString.java b/大厂刷题班/class37/Problem_0394_DecodeString.java new file mode 100644 index 0000000..0387f14 --- /dev/null +++ b/大厂刷题班/class37/Problem_0394_DecodeString.java @@ -0,0 +1,50 @@ +package class37; + +public class Problem_0394_DecodeString { + + public static String decodeString(String s) { + char[] str = s.toCharArray(); + return process(str, 0).ans; + } + + public static class Info { + public String ans; + public int stop; + + public Info(String a, int e) { + ans = a; + stop = e; + } + } + + // s[i....] 何时停?遇到 ']' 或者遇到 s的终止位置,停止 + // 返回Info + // 0) 串 + // 1) 算到了哪 + public static Info process(char[] s, int i) { + StringBuilder ans = new StringBuilder(); + int count = 0; + while (i < s.length && s[i] != ']') { + if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z')) { + ans.append(s[i++]); + } else if (s[i] >= '0' && s[i] <= '9') { + count = count * 10 + s[i++] - '0'; + } else { // str[index] = '[' + Info next = process(s, i + 1); + ans.append(timesString(count, next.ans)); + count = 0; + i = next.stop + 1; + } + } + return new Info(ans.toString(), i); + } + + public static String timesString(int times, String str) { + StringBuilder ans = new StringBuilder(); + for (int i = 0; i < times; i++) { + ans.append(str); + } + return ans.toString(); + } + +} diff --git a/大厂刷题班/class37/Problem_0406_QueueReconstructionByHeight.java b/大厂刷题班/class37/Problem_0406_QueueReconstructionByHeight.java new file mode 100644 index 0000000..793a8cf --- /dev/null +++ b/大厂刷题班/class37/Problem_0406_QueueReconstructionByHeight.java @@ -0,0 +1,264 @@ +package class37; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; + +public class Problem_0406_QueueReconstructionByHeight { + + public static int[][] reconstructQueue1(int[][] people) { + int N = people.length; + Unit[] units = new Unit[N]; + for (int i = 0; i < N; i++) { + units[i] = new Unit(people[i][0], people[i][1]); + } + Arrays.sort(units, new UnitComparator()); + ArrayList arrList = new ArrayList<>(); + for (Unit unit : units) { + arrList.add(unit.k, unit); + } + int[][] ans = new int[N][2]; + int index = 0; + for (Unit unit : arrList) { + ans[index][0] = unit.h; + ans[index++][1] = unit.k; + } + return ans; + } + + public static int[][] reconstructQueue2(int[][] people) { + int N = people.length; + Unit[] units = new Unit[N]; + for (int i = 0; i < N; i++) { + units[i] = new Unit(people[i][0], people[i][1]); + } + Arrays.sort(units, new UnitComparator()); + SBTree tree = new SBTree(); + for (int i = 0; i < N; i++) { + tree.insert(units[i].k, i); + } + LinkedList allIndexes = tree.allIndexes(); + int[][] ans = new int[N][2]; + int index = 0; + for (Integer arri : allIndexes) { + ans[index][0] = units[arri].h; + ans[index++][1] = units[arri].k; + } + return ans; + } + + public static class Unit { + public int h; + public int k; + + public Unit(int height, int greater) { + h = height; + k = greater; + } + } + + public static class UnitComparator implements Comparator { + + @Override + public int compare(Unit o1, Unit o2) { + return o1.h != o2.h ? (o2.h - o1.h) : (o1.k - o2.k); + } + + } + + public static class SBTNode { + public int value; + public SBTNode l; + public SBTNode r; + public int size; + + public SBTNode(int arrIndex) { + value = arrIndex; + size = 1; + } + } + + public static class SBTree { + private SBTNode root; + + private SBTNode rightRotate(SBTNode cur) { + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + int leftSize = cur.l != null ? cur.l.size : 0; + int leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + int leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + int rightSize = cur.r != null ? cur.r.size : 0; + int rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + int rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + private SBTNode insert(SBTNode root, int index, SBTNode cur) { + if (root == null) { + return cur; + } + root.size++; + int leftAndHeadSize = (root.l != null ? root.l.size : 0) + 1; + if (index < leftAndHeadSize) { + root.l = insert(root.l, index, cur); + } else { + root.r = insert(root.r, index - leftAndHeadSize, cur); + } + root = maintain(root); + return root; + } + + private SBTNode get(SBTNode root, int index) { + int leftSize = root.l != null ? root.l.size : 0; + if (index < leftSize) { + return get(root.l, index); + } else if (index == leftSize) { + return root; + } else { + return get(root.r, index - leftSize - 1); + } + } + + private void process(SBTNode head, LinkedList indexes) { + if (head == null) { + return; + } + process(head.l, indexes); + indexes.addLast(head.value); + process(head.r, indexes); + } + + public void insert(int index, int value) { + SBTNode cur = new SBTNode(value); + if (root == null) { + root = cur; + } else { + if (index <= root.size) { + root = insert(root, index, cur); + } + } + } + + public int get(int index) { + SBTNode ans = get(root, index); + return ans.value; + } + + public LinkedList allIndexes() { + LinkedList indexes = new LinkedList<>(); + process(root, indexes); + return indexes; + } + + } + + // 通过以下这个测试, + // 可以很明显的看到LinkedList的插入和get效率不如SBTree + // LinkedList需要找到index所在的位置之后才能插入或者读取,时间复杂度O(N) + // SBTree是平衡搜索二叉树,所以插入或者读取时间复杂度都是O(logN) + public static void main(String[] args) { + // 功能测试 + int test = 10000; + int max = 1000000; + boolean pass = true; + LinkedList list = new LinkedList<>(); + SBTree sbtree = new SBTree(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + sbtree.insert(randomIndex, randomValue); + } + for (int i = 0; i < test; i++) { + if (list.get(i) != sbtree.get(i)) { + pass = false; + break; + } + } + System.out.println("功能测试是否通过 : " + pass); + + // 性能测试 + test = 50000; + list = new LinkedList<>(); + sbtree = new SBTree(); + long start = 0; + long end = 0; + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + list.add(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("LinkedList插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + list.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("LinkedList读取总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + int randomValue = (int) (Math.random() * (max + 1)); + sbtree.insert(randomIndex, randomValue); + } + end = System.currentTimeMillis(); + System.out.println("SBTree插入总时长(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + for (int i = 0; i < test; i++) { + int randomIndex = (int) (Math.random() * (i + 1)); + sbtree.get(randomIndex); + } + end = System.currentTimeMillis(); + System.out.println("SBTree读取总时长(毫秒) : " + (end - start)); + + } + +} diff --git a/大厂刷题班/class37/Problem_0437_PathSumIII.java b/大厂刷题班/class37/Problem_0437_PathSumIII.java new file mode 100644 index 0000000..253c27c --- /dev/null +++ b/大厂刷题班/class37/Problem_0437_PathSumIII.java @@ -0,0 +1,44 @@ +package class37; + +import java.util.HashMap; + +public class Problem_0437_PathSumIII { + + public class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + public static int pathSum(TreeNode root, int sum) { + HashMap preSumMap = new HashMap<>(); + preSumMap.put(0L, 1); + return process(root, sum, 0, preSumMap); + } + + // 返回方法数 + public static int process(TreeNode x, int sum, long preAll, HashMap preSumMap) { + if (x == null) { + return 0; + } + long all = preAll + x.val; + int ans = 0; + if (preSumMap.containsKey(all - sum)) { + ans = preSumMap.get(all - sum); + } + if (!preSumMap.containsKey(all)) { + preSumMap.put(all, 1); + } else { + preSumMap.put(all, preSumMap.get(all) + 1); + } + ans += process(x.left, sum, all, preSumMap); + ans += process(x.right, sum, all, preSumMap); + if (preSumMap.get(all) == 1) { + preSumMap.remove(all); + } else { + preSumMap.put(all, preSumMap.get(all) - 1); + } + return ans; + } + +} diff --git a/大厂刷题班/class37/说明 b/大厂刷题班/class37/说明 new file mode 100644 index 0000000..8ee8a8f --- /dev/null +++ b/大厂刷题班/class37/说明 @@ -0,0 +1,30 @@ +leetcode最受欢迎100题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top 100 Liked Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=79h8rn6 +即可看到leetcode最受欢迎100题全列表 + +大厂刷题班27节~35节已经讲完leetcode高频题系列 +leetcode高频题系列和leetcode最受欢迎100题系列题目有重合 +以下为leetcode最受欢迎100题不和leetcode高频题重合的题号,其他的都重复了,不再讲述 +0032 : 大厂刷题班, 第14节第1题 +0039 : 体系学习班, 硬币找零专题 : 第21节第2、3、4题, 第22节第2题, 第24节第4题。本题就是无限张找零问题,不再重复讲述 +0064 : 体系学习班, 第21节第1题 +0072 : 大厂刷题班, 第5节第3题 +0085 : 体系学习班, 第25节第4题 +0096 : 体系学习班, 第39节第4题, 卡特兰数 +0114 : 大厂刷题班, 第37节, 本节 +0142 : 体系学习班, 第10节第1题 +0221 : 大厂刷题班, 第37节, 本节 +0226 : 大厂刷题班, 第37节, 本节 +0337 : 体系学习班, 第13节, 第4题, 还是这道题的加强版(多叉树) +0338 : 和leetcode第191题重复, 大厂刷题班第30节讲过了 +0394 : 大厂刷题班, 第37节, 本节 +0406 : 大厂刷题班, 第37节, 本节 +0416 : 体系学习班, 第23节第1题, 还是这道题的加强版 +0437 : 大厂刷题班, 第37节, 本节 +剩余题目在下一节 + +本节附加题 +code01 : 2021年8月大厂真实笔试题 +code02 : 2021年8月大厂真实笔试题 \ No newline at end of file diff --git a/大厂刷题班/class38/Code01_FillGapMinStep.java b/大厂刷题班/class38/Code01_FillGapMinStep.java new file mode 100644 index 0000000..361a734 --- /dev/null +++ b/大厂刷题班/class38/Code01_FillGapMinStep.java @@ -0,0 +1,103 @@ +package class38; + +// 来自字节 +// 给定两个数a和b +// 第1轮,把1选择给a或者b +// 第2轮,把2选择给a或者b +// ... +// 第i轮,把i选择给a或者b +// 想让a和b的值一样大,请问至少需要多少轮? +public class Code01_FillGapMinStep { + + // 暴力方法 + // 不要让a、b过大! + public static int minStep0(int a, int b) { + if (a == b) { + return 0; + } + int limit = 15; + return process(a, b, 1, limit); + } + + public static int process(int a, int b, int i, int n) { + if (i > n) { + return Integer.MAX_VALUE; + } + if (a + i == b || a == b + i) { + return i; + } + return Math.min(process(a + i, b, i + 1, n), process(a, b + i, i + 1, n)); + } + + public static int minStep1(int a, int b) { + if (a == b) { + return 0; + } + int s = Math.abs(a - b); + int num = 1; + int sum = 0; + for (; !(sum >= s && (sum - s) % 2 == 0); num++) { + sum += num; + } + return num - 1; + } + + public static int minStep2(int a, int b) { + if (a == b) { + return 0; + } + int s = Math.abs(a - b); + // 找到sum >= s, 最小的i + int begin = best(s << 1); + for (; (begin * (begin + 1) / 2 - s) % 2 != 0;) { + begin++; + } + return begin; + } + + public static int best(int s2) { + int L = 0; + int R = 1; + for (; R * (R + 1) < s2;) { + L = R; + R *= 2; + } + int ans = 0; + while (L <= R) { + int M = (L + R) / 2; + if (M * (M + 1) >= s2) { + ans = M; + R = M - 1; + } else { + L = M + 1; + } + } + return ans; + } + + public static void main(String[] args) { + System.out.println("功能测试开始"); + for (int a = 1; a < 100; a++) { + for (int b = 1; b < 100; b++) { + int ans1 = minStep0(a, b); + int ans2 = minStep1(a, b); + int ans3 = minStep2(a, b); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + System.out.println(a + " , " + b); + break; + } + } + } + System.out.println("功能测试结束"); + + int a = 19019; + int b = 8439284; + int ans2 = minStep1(a, b); + int ans3 = minStep2(a, b); + System.out.println(ans2); + System.out.println(ans3); + + } + +} diff --git a/大厂刷题班/class38/Code02_GreatWall.java b/大厂刷题班/class38/Code02_GreatWall.java new file mode 100644 index 0000000..af9cf35 --- /dev/null +++ b/大厂刷题班/class38/Code02_GreatWall.java @@ -0,0 +1,183 @@ +package class38; + +// 360笔试题 +// 长城守卫军 +// 题目描述: +// 长城上有连成一排的n个烽火台,每个烽火台都有士兵驻守。 +// 第i个烽火台驻守着ai个士兵,相邻峰火台的距离为1。另外,有m位将军, +// 每位将军可以驻守一个峰火台,每个烽火台可以有多个将军驻守, +// 将军可以影响所有距离他驻守的峰火台小于等于x的烽火台。 +// 每个烽火台的基础战斗力为士兵数,另外,每个能影响此烽火台的将军都能使这个烽火台的战斗力提升k。 +// 长城的战斗力为所有烽火台的战斗力的最小值。 +// 请问长城的最大战斗力可以是多少? +// 输入描述 +// 第一行四个正整数n,m,x,k(1<=x<=n<=10^5,0<=m<=10^5,1<=k<=10^5) +// 第二行n个整数ai(0<=ai<=10^5) +// 输出描述 仅一行,一个整数,表示长城的最大战斗力 +// 样例输入 +// 5 2 1 2 +// 4 4 2 4 4 +// 样例输出 +// 6 +public class Code02_GreatWall { + + public static int maxForce(int[] wall, int m, int x, int k) { + long L = 0; + long R = 0; + for (int num : wall) { + R = Math.max(R, num); + } + R += m * k; + long ans = 0; + while (L <= R) { + long M = (L + R) / 2; + if (can(wall, m, x, k, M)) { + ans = M; + L = M + 1; + } else { + R = M - 1; + } + } + return (int) ans; + } + + public static boolean can(int[] wall, int m, int x, int k, long limit) { + int N = wall.length; + // 注意:下标从1开始 + SegmentTree st = new SegmentTree(wall); + st.build(1, N, 1); + int need = 0; + for (int i = 0; i < N; i++) { + // 注意:下标从1开始 + long cur = st.query(i + 1, i + 1, 1, N, 1); + if (cur < limit) { + int add = (int) ((limit - cur + k - 1) / k); + need += add; + if (need > m) { + return false; + } + st.add(i + 1, Math.min(i + x, N), add * k, 1, N, 1); + } + } + return true; + } + + public static class SegmentTree { + private int MAXN; + private int[] arr; + private int[] sum; + private int[] lazy; + private int[] change; + private boolean[] update; + + public SegmentTree(int[] origin) { + MAXN = origin.length + 1; + arr = new int[MAXN]; + for (int i = 1; i < MAXN; i++) { + arr[i] = origin[i - 1]; + } + sum = new int[MAXN << 2]; + lazy = new int[MAXN << 2]; + change = new int[MAXN << 2]; + update = new boolean[MAXN << 2]; + } + + private void pushUp(int rt) { + sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; + } + + private void pushDown(int rt, int ln, int rn) { + if (update[rt]) { + update[rt << 1] = true; + update[rt << 1 | 1] = true; + change[rt << 1] = change[rt]; + change[rt << 1 | 1] = change[rt]; + lazy[rt << 1] = 0; + lazy[rt << 1 | 1] = 0; + sum[rt << 1] = change[rt] * ln; + sum[rt << 1 | 1] = change[rt] * rn; + update[rt] = false; + } + if (lazy[rt] != 0) { + lazy[rt << 1] += lazy[rt]; + sum[rt << 1] += lazy[rt] * ln; + lazy[rt << 1 | 1] += lazy[rt]; + sum[rt << 1 | 1] += lazy[rt] * rn; + lazy[rt] = 0; + } + } + + public void build(int l, int r, int rt) { + if (l == r) { + sum[rt] = arr[l]; + return; + } + int mid = (l + r) >> 1; + build(l, mid, rt << 1); + build(mid + 1, r, rt << 1 | 1); + pushUp(rt); + } + + public void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + update[rt] = true; + change[rt] = C; + sum[rt] = C * (r - l + 1); + lazy[rt] = 0; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public void add(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + sum[rt] += C * (r - l + 1); + lazy[rt] += C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + add(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + add(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public long query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return sum[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + long ans = 0; + if (L <= mid) { + ans += query(L, R, l, mid, rt << 1); + } + if (R > mid) { + ans += query(L, R, mid + 1, r, rt << 1 | 1); + } + return ans; + } + + } + + public static void main(String[] args) { + int[] wall = { 3, 1, 1, 1, 3 }; + int m = 2; + int x = 3; + int k = 1; + System.out.println(maxForce(wall, m, x, k)); + } + +} diff --git a/大厂刷题班/class38/Problem_0438_FindAllAnagramsInAString.java b/大厂刷题班/class38/Problem_0438_FindAllAnagramsInAString.java new file mode 100644 index 0000000..3f68453 --- /dev/null +++ b/大厂刷题班/class38/Problem_0438_FindAllAnagramsInAString.java @@ -0,0 +1,58 @@ +package class38; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class Problem_0438_FindAllAnagramsInAString { + + public static List findAnagrams(String s, String p) { + List ans = new ArrayList<>(); + if (s == null || p == null || s.length() < p.length()) { + return ans; + } + char[] str = s.toCharArray(); + int N = str.length; + char[] pst = p.toCharArray(); + int M = pst.length; + HashMap map = new HashMap<>(); + for (char cha : pst) { + if (!map.containsKey(cha)) { + map.put(cha, 1); + } else { + map.put(cha, map.get(cha) + 1); + } + } + int all = M; + for (int end = 0; end < M - 1; end++) { + if (map.containsKey(str[end])) { + int count = map.get(str[end]); + if (count > 0) { + all--; + } + map.put(str[end], count - 1); + } + } + for (int end = M - 1, start = 0; end < N; end++, start++) { + if (map.containsKey(str[end])) { + int count = map.get(str[end]); + if (count > 0) { + all--; + } + map.put(str[end], count - 1); + } + if (all == 0) { + ans.add(start); + } + if (map.containsKey(str[start])) { + int count = map.get(str[start]); + if (count >= 0) { + all++; + } + map.put(str[start], count + 1); + } + } + return ans; + } + +} diff --git a/大厂刷题班/class38/Problem_0448_FindAllNumbersDisappearedInAnArray.java b/大厂刷题班/class38/Problem_0448_FindAllNumbersDisappearedInAnArray.java new file mode 100644 index 0000000..3dcf07e --- /dev/null +++ b/大厂刷题班/class38/Problem_0448_FindAllNumbersDisappearedInAnArray.java @@ -0,0 +1,42 @@ +package class38; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0448_FindAllNumbersDisappearedInAnArray { + + public static List findDisappearedNumbers(int[] nums) { + List ans = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return ans; + } + int N = nums.length; + for (int i = 0; i < N; i++) { + // 从i位置出发,去玩下标循环怼 + walk(nums, i); + } + for (int i = 0; i < N; i++) { + if (nums[i] != i + 1) { + ans.add(i + 1); + } + } + return ans; + } + + public static void walk(int[] nums, int i) { + while (nums[i] != i + 1) { // 不断从i发货 + int nexti = nums[i] - 1; + if (nums[nexti] == nexti + 1) { + break; + } + swap(nums, i, nexti); + } + } + + public static void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + +} diff --git a/大厂刷题班/class38/Problem_0617_MergeTwoBinaryTrees.java b/大厂刷题班/class38/Problem_0617_MergeTwoBinaryTrees.java new file mode 100644 index 0000000..942d269 --- /dev/null +++ b/大厂刷题班/class38/Problem_0617_MergeTwoBinaryTrees.java @@ -0,0 +1,31 @@ +package class38; + +public class Problem_0617_MergeTwoBinaryTrees { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int val) { + this.val = val; + } + } + + // 当前,一棵树的头是t1,另一颗树的头是t2 + // 请返回,整体merge之后的头 + public static TreeNode mergeTrees(TreeNode t1, TreeNode t2) { + if (t1 == null) { + return t2; + } + if (t2 == null) { + return t1; + } + // t1和t2都不是空 + TreeNode merge = new TreeNode(t1.val + t2.val); + merge.left = mergeTrees(t1.left, t2.left); + merge.right = mergeTrees(t1.right, t2.right); + return merge; + } + +} diff --git a/大厂刷题班/class38/Problem_0621_TaskScheduler.java b/大厂刷题班/class38/Problem_0621_TaskScheduler.java new file mode 100644 index 0000000..24b4b59 --- /dev/null +++ b/大厂刷题班/class38/Problem_0621_TaskScheduler.java @@ -0,0 +1,33 @@ +package class38; + +public class Problem_0621_TaskScheduler { + + // ['A', 'B', 'A'] + public static int leastInterval(char[] tasks, int free) { + int[] count = new int[256]; + // 出现最多次的任务,到底是出现了几次 + int maxCount = 0; + for (char task : tasks) { + count[task]++; + maxCount = Math.max(maxCount, count[task]); + } + // 有多少种任务,都出现最多次 + int maxKinds = 0; + for (int task = 0; task < 256; task++) { + if (count[task] == maxCount) { + maxKinds++; + } + } + // maxKinds : 有多少种任务,都出现最多次 + // maxCount : 最多次,是几次? + // 砍掉最后一组剩余的任务数 + int tasksExceptFinalTeam = tasks.length - maxKinds; + int spaces = (free + 1) * (maxCount - 1); + // 到底几个空格最终会留下! + int restSpaces = Math.max(0, spaces - tasksExceptFinalTeam); + return tasks.length + restSpaces; + // return Math.max(tasks.length, ((n + 1) * (maxCount - 1) + maxKinds)); + } + + +} diff --git a/大厂刷题班/class38/Problem_0647_PalindromicSubstrings.java b/大厂刷题班/class38/Problem_0647_PalindromicSubstrings.java new file mode 100644 index 0000000..e51180a --- /dev/null +++ b/大厂刷题班/class38/Problem_0647_PalindromicSubstrings.java @@ -0,0 +1,49 @@ +package class38; + +public class Problem_0647_PalindromicSubstrings { + + public static int countSubstrings(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int[] dp = getManacherDP(s); + int ans = 0; + for (int i = 0; i < dp.length; i++) { + ans += dp[i] >> 1; + } + return ans; + } + + public static int[] getManacherDP(String s) { + char[] str = manacherString(s); + int[] pArr = new int[str.length]; + int C = -1; + int R = -1; + for (int i = 0; i < str.length; i++) { + pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1; + while (i + pArr[i] < str.length && i - pArr[i] > -1) { + if (str[i + pArr[i]] == str[i - pArr[i]]) + pArr[i]++; + else { + break; + } + } + if (i + pArr[i] > R) { + R = i + pArr[i]; + C = i; + } + } + return pArr; + } + + public static char[] manacherString(String str) { + char[] charArr = str.toCharArray(); + char[] res = new char[str.length() * 2 + 1]; + int index = 0; + for (int i = 0; i != res.length; i++) { + res[i] = (i & 1) == 0 ? '#' : charArr[index++]; + } + return res; + } + +} diff --git a/大厂刷题班/class38/Problem_0739_DailyTemperatures.java b/大厂刷题班/class38/Problem_0739_DailyTemperatures.java new file mode 100644 index 0000000..92b0712 --- /dev/null +++ b/大厂刷题班/class38/Problem_0739_DailyTemperatures.java @@ -0,0 +1,34 @@ +package class38; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class Problem_0739_DailyTemperatures { + + public static int[] dailyTemperatures(int[] arr) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + int N = arr.length; + int[] ans = new int[N]; + Stack> stack = new Stack<>(); + for (int i = 0; i < N; i++) { + while (!stack.isEmpty() && arr[stack.peek().get(0)] < arr[i]) { + List popIs = stack.pop(); + for (Integer popi : popIs) { + ans[popi] = i - popi; + } + } + if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) { + stack.peek().add(Integer.valueOf(i)); + } else { + ArrayList list = new ArrayList<>(); + list.add(i); + stack.push(list); + } + } + return ans; + } + +} diff --git a/大厂刷题班/class38/Problem_0763_PartitionLabels.java b/大厂刷题班/class38/Problem_0763_PartitionLabels.java new file mode 100644 index 0000000..c72954a --- /dev/null +++ b/大厂刷题班/class38/Problem_0763_PartitionLabels.java @@ -0,0 +1,28 @@ +package class38; + +import java.util.ArrayList; +import java.util.List; + +public class Problem_0763_PartitionLabels { + + public static List partitionLabels(String S) { + char[] str = S.toCharArray(); + int[] far = new int[26]; + for (int i = 0; i < str.length; i++) { + far[str[i] - 'a'] = i; + } + List ans = new ArrayList<>(); + int left = 0; + int right = far[str[0] - 'a']; + for (int i = 1; i < str.length; i++) { + if (i > right) { + ans.add(right - left + 1); + left = i; + } + right = Math.max(right, far[str[i] - 'a']); + } + ans.add(right - left + 1); + return ans; + } + +} diff --git a/大厂刷题班/class38/说明 b/大厂刷题班/class38/说明 new file mode 100644 index 0000000..5fc1f1f --- /dev/null +++ b/大厂刷题班/class38/说明 @@ -0,0 +1,25 @@ +leetcode最受欢迎100题 +leetcode全题目列表 : https://leetcode.com/problemset/all/ +在全题目列表的右侧栏,点击Top 100 Liked Questions +或者直接进入右侧链接 : https://leetcode.com/problemset/all/?listId=79h8rn6 +即可看到leetcode最受欢迎100题全列表 + +大厂刷题班27节~35节已经讲完leetcode高频题系列 +leetcode高频题系列和leetcode最受欢迎100题系列题目有重合 +以下为leetcode最受欢迎100题不和leetcode高频题重合的题号,其他的都重复了,不再讲述 +0438 : 大厂刷题班, 第38节, 本节 +0448 : 大厂刷题班, 第38节, 本节 +0494 : 大厂刷题班, 第1节第7题 +0543 : 体系学习班, 第12节第6题 +0560 : 大厂刷题班, 第37节, Leetcode题目437与本题思路相同, 课上也讲了该题做法 +0581 : 大厂刷题班, 第1节第6题 +0617 : 大厂刷题班, 第38节, 本节 +0621 : 大厂刷题班, 第38节, 本节 +0647 : 大厂刷题班, 第38节, 本节 +0739 : 大厂刷题班, 第38节, 本节 +0763 : 大厂刷题班, 第38节, 本节 +至此,leetcode最受欢迎100题系列完结 + +本节附加题 +code01 : 2021年8月大厂真实笔试题 +code02 : 2021年8月大厂真实笔试题 \ No newline at end of file diff --git a/大厂刷题班/class39/Code01_01AddValue.java b/大厂刷题班/class39/Code01_01AddValue.java new file mode 100644 index 0000000..5609d29 --- /dev/null +++ b/大厂刷题班/class39/Code01_01AddValue.java @@ -0,0 +1,46 @@ +package class39; + +// 来自腾讯 +// 给定一个只由0和1组成的字符串S,假设下标从1开始,规定i位置的字符价值V[i]计算方式如下 : +// 1) i == 1时,V[i] = 1 +// 2) i > 1时,如果S[i] != S[i-1],V[i] = 1 +// 3) i > 1时,如果S[i] == S[i-1],V[i] = V[i-1] + 1 +// 你可以随意删除S中的字符,返回整个S的最大价值 +// 字符串长度<=5000 +public class Code01_01AddValue { + + public static int max1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int[] arr = new int[str.length]; + for (int i = 0; i < arr.length; i++) { + arr[i] = str[i] == '0' ? 0 : 1; + } + return process1(arr, 0, 0, 0); + } + + // 递归含义 : + // 目前在arr[index...]上做选择, str[index...]的左边,最近的数字是lastNum + // 并且lastNum所带的价值,已经拉高到baseValue + // 返回在str[index...]上做选择,最终获得的最大价值 + // index -> 0 ~ 4999 + // lastNum -> 0 or 1 + // baseValue -> 1 ~ 5000 + // 5000 * 2 * 5000 -> 5 * 10^7(过!) + public static int process1(int[] arr, int index, int lastNum, int baseValue) { + if (index == arr.length) { + return 0; + } + int curValue = lastNum == arr[index] ? (baseValue + 1) : 1; + // 当前index位置的字符保留 + int next1 = process1(arr, index + 1, arr[index], curValue); + // 当前index位置的字符不保留 + int next2 = process1(arr, index + 1, lastNum, baseValue); + return Math.max(curValue + next1, next2); + } + + // 请看体系学习班,动态规划章节,把上面的递归改成动态规划!看完必会 + +} diff --git a/大厂刷题班/class39/Code02_ValidSequence.java b/大厂刷题班/class39/Code02_ValidSequence.java new file mode 100644 index 0000000..37e499e --- /dev/null +++ b/大厂刷题班/class39/Code02_ValidSequence.java @@ -0,0 +1,113 @@ +package class39; + +// 来自腾讯 +// 给定一个长度为n的数组arr,求有多少个子数组满足 : +// 子数组两端的值,是这个子数组的最小值和次小值,最小值和次小值谁在最左和最右无所谓 +// n<=100000(10^5) n*logn O(N) +public class Code02_ValidSequence { + + + public static int nums(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int n = arr.length; + int[] values = new int[n]; + int[] times = new int[n]; + int size = 0; + int ans = 0; + for (int i = 0; i < arr.length; i++) { + while (size != 0 && values[size - 1] > arr[i]) { + size--; + ans += times[size] + cn2(times[size]); + } + if (size != 0 && values[size - 1] == arr[i]) { + times[size - 1]++; + } else { + values[size] = arr[i]; + times[size++] = 1; + } + } + while (size != 0) { + ans += cn2(times[--size]); + } + for (int i = arr.length - 1; i >= 0; i--) { + while (size != 0 && values[size - 1] > arr[i]) { + ans += times[--size]; + } + if (size != 0 && values[size - 1] == arr[i]) { + times[size - 1]++; + } else { + values[size] = arr[i]; + times[size++] = 1; + } + } + return ans; + } + + public static int cn2(int n) { + return (n * (n - 1)) >> 1; + } + + // 为了测试 + // 暴力方法 + public static int test(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int ans = 0; + for (int s = 0; s < arr.length; s++) { + for (int e = s + 1; e < arr.length; e++) { + int max = Math.max(arr[s], arr[e]); + boolean valid = true; + for (int i = s + 1; i < e; i++) { + if (arr[i] < max) { + valid = false; + break; + } + } + ans += valid ? 1 : 0; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int n = 30; + int v = 30; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int m = (int) (Math.random() * n); + int[] arr = randomArray(m, v); + int ans1 = nums(arr); + int ans2 = test(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + printArray(arr); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class39/Code03_SequenceKDifferentKinds.java b/大厂刷题班/class39/Code03_SequenceKDifferentKinds.java new file mode 100644 index 0000000..82803e7 --- /dev/null +++ b/大厂刷题班/class39/Code03_SequenceKDifferentKinds.java @@ -0,0 +1,63 @@ +package class39; + +// 来自百度 +// 给定一个字符串str,和一个正数k +// str子序列的字符种数必须是k种,返回有多少子序列满足这个条件 +// 已知str中都是小写字母 +// 原始是取mod +// 本节在尝试上,最难的 +// 搞出桶来,组合公式 +public class Code03_SequenceKDifferentKinds { + + // bu -> {6,7,0,0,6,3} + // 0 1 2 3 4 5 + // a b c d e f + // 在桶数组bu[index....] 一定要凑出rest种来!请问几种方法! + public static int f(int[] bu, int index, int rest) { + if (index == bu.length) { + return rest == 0 ? 1 : 0; + } + // 最后形成的子序列,一个index代表的字符也没有! + int p1 = f(bu, index + 1, rest); + // 最后形成的子序列,一定要包含index代表的字符,几个呢?(所有可能性都要算上!) + int p2 = 0; + if (rest > 0) { // 剩余的种数,没耗尽,可以包含当前桶的字符 + p2 = (1 << bu[index] - 1) * f(bu, index + 1, rest - 1); + } + return p1 + p2; + } + + public static int nums(String s, int k) { + char[] str = s.toCharArray(); + int[] counts = new int[26]; + for (char c : str) { + counts[c - 97]++; + } + return ways(counts, 0, k); + } + + public static int ways(int[] c, int i, int r) { + if (r == 0) { + return 1; + } + if (i == c.length) { + return 0; + } + // math(n) -> 2 ^ n -1 + return math(c[i]) * ways(c, i + 1, r - 1) + ways(c, i + 1, r); + } + + // n个不同的球 + // 挑出1个的方法数 + 挑出2个的方法数 + ... + 挑出n个的方法数为: + // C(n,1) + C(n,2) + ... + C(n,n) == (2 ^ n) -1 + public static int math(int n) { + return (1 << n) - 1; + } + + public static void main(String[] args) { + String str = "acbbca"; + int k = 3; + System.out.println(nums(str, k)); + } + +} diff --git a/大厂刷题班/class39/Code04_JumpGameOnMatrix.java b/大厂刷题班/class39/Code04_JumpGameOnMatrix.java new file mode 100644 index 0000000..3acbcc4 --- /dev/null +++ b/大厂刷题班/class39/Code04_JumpGameOnMatrix.java @@ -0,0 +1,239 @@ +package class39; + +// 来自京东 +// 给定一个二维数组matrix,matrix[i][j] = k代表: +// 从(i,j)位置可以随意往右跳<=k步,或者从(i,j)位置可以随意往下跳<=k步 +// 如果matrix[i][j] = 0,代表来到(i,j)位置必须停止 +// 返回从matrix左上角到右下角,至少要跳几次 +// 已知matrix中行数n <= 5000, 列数m <= 5000 +// matrix中的值,<= 5000 +// 最弟弟的技巧也过了。最优解 -> dp+枚举优化(线段树,体系学习班) +public class Code04_JumpGameOnMatrix { + + // 暴力方法,仅仅是做对数器 + // 如果无法到达会返回系统最大值 + public static int jump1(int[][] map) { + return process(map, 0, 0); + } + + // 当前来到的位置是(row,col) + // 目标:右下角 + // 当前最大能跳多远,map[row][col]值决定,只能向右、或者向下 + // 返回,到达右下角,最小跳几次? + // 5000 * 5000 = 25000000 -> 2 * (10 ^ 7) + public static int process(int[][] map, int row, int col) { + if (row == map.length - 1 && col == map[0].length - 1) { + return 0; + } + // 如果没到右下角 + if (map[row][col] == 0) { + return Integer.MAX_VALUE; + } + // 当前位置,可以去很多的位置,next含义: + // 在所有能去的位置里,哪个位置最后到达右下角,跳的次数最少,就是next + int next = Integer.MAX_VALUE; + // 往下能到达的位置,全试一遍 + for (int down = row + 1; down < map.length && (down - row) <= map[row][col]; down++) { + next = Math.min(next, process(map, down, col)); + } + // 往右能到达的位置,全试一遍 + for (int right = col + 1; right < map[0].length && (right - col) <= map[row][col]; right++) { + next = Math.min(next, process(map, row, right)); + } + // 如果所有下一步的位置,没有一个能到右下角,next = 系统最大! + // 返回系统最大! + // next != 系统最大 7 + 1 + return next != Integer.MAX_VALUE ? (next + 1) : next; + } + + // 优化方法, 利用线段树做枚举优化 + // 因为线段树,下标从1开始 + // 所以,该方法中所有的下标,请都从1开始,防止乱! + public static int jump2(int[][] arr) { + int n = arr.length; + int m = arr[0].length; + int[][] map = new int[n + 1][m + 1]; + for (int a = 0, b = 1; a < n; a++, b++) { + for (int c = 0, d = 1; c < m; c++, d++) { + map[b][d] = arr[a][c]; + } + } + SegmentTree[] rowTrees = new SegmentTree[n + 1]; + for (int i = 1; i <= n; i++) { + rowTrees[i] = new SegmentTree(m); + } + SegmentTree[] colTrees = new SegmentTree[m + 1]; + for (int i = 1; i <= m; i++) { + colTrees[i] = new SegmentTree(n); + } + rowTrees[n].update(m, m, 0, 1, m, 1); + colTrees[m].update(n, n, 0, 1, n, 1); + for (int col = m - 1; col >= 1; col--) { + if (map[n][col] != 0) { + int left = col + 1; + int right = Math.min(col + map[n][col], m); + int next = rowTrees[n].query(left, right, 1, m, 1); + if (next != Integer.MAX_VALUE) { + rowTrees[n].update(col, col, next + 1, 1, m, 1); + colTrees[col].update(n, n, next + 1, 1, n, 1); + } + } + } + for (int row = n - 1; row >= 1; row--) { + if (map[row][m] != 0) { + int up = row + 1; + int down = Math.min(row + map[row][m], n); + int next = colTrees[m].query(up, down, 1, n, 1); + if (next != Integer.MAX_VALUE) { + rowTrees[row].update(m, m, next + 1, 1, m, 1); + colTrees[m].update(row, row, next + 1, 1, n, 1); + } + } + } + for (int row = n - 1; row >= 1; row--) { + for (int col = m - 1; col >= 1; col--) { + if (map[row][col] != 0) { + // (row,col) 往右是什么范围呢?[left,right] + int left = col + 1; + int right = Math.min(col + map[row][col], m); + int next1 = rowTrees[row].query(left, right, 1, m, 1); + // (row,col) 往下是什么范围呢?[up,down] + int up = row + 1; + int down = Math.min(row + map[row][col], n); + int next2 = colTrees[col].query(up, down, 1, n, 1); + int next = Math.min(next1, next2); + if (next != Integer.MAX_VALUE) { + rowTrees[row].update(col, col, next + 1, 1, m, 1); + colTrees[col].update(row, row, next + 1, 1, n, 1); + } + } + } + } + return rowTrees[1].query(1, 1, 1, m, 1); + } + + // 区间查询最小值的线段树 + // 注意下标从1开始,不从0开始 + // 比如你传入size = 8 + // 则位置对应为1~8,而不是0~7 + public static class SegmentTree { + private int[] min; + private int[] change; + private boolean[] update; + + public SegmentTree(int size) { + int N = size + 1; + min = new int[N << 2]; + change = new int[N << 2]; + update = new boolean[N << 2]; + update(1, size, Integer.MAX_VALUE, 1, size, 1); + } + + private void pushUp(int rt) { + min[rt] = Math.min(min[rt << 1], min[rt << 1 | 1]); + } + + private void pushDown(int rt, int ln, int rn) { + if (update[rt]) { + update[rt << 1] = true; + update[rt << 1 | 1] = true; + change[rt << 1] = change[rt]; + change[rt << 1 | 1] = change[rt]; + min[rt << 1] = change[rt]; + min[rt << 1 | 1] = change[rt]; + update[rt] = false; + } + } + + // 最后三个参数是固定的, 每次传入相同的值即可: + // l = 1(固定) + // r = size(你设置的线段树大小) + // rt = 1(固定) + public void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + update[rt] = true; + change[rt] = C; + min[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + // 最后三个参数是固定的, 每次传入相同的值即可: + // l = 1(固定) + // r = size(你设置的线段树大小) + // rt = 1(固定) + public int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return min[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int left = Integer.MAX_VALUE; + int right = Integer.MAX_VALUE; + if (L <= mid) { + left = query(L, R, l, mid, rt << 1); + } + if (R > mid) { + right = query(L, R, mid + 1, r, rt << 1 | 1); + } + return Math.min(left, right); + } + + } + + // 为了测试 + public static int[][] randomMatrix(int n, int m, int v) { + int[][] ans = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + ans[i][j] = (int) (Math.random() * v); + } + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + // 先展示一下线段树的用法,假设N=100 + // 初始化时,1~100所有位置的值都是系统最大 + System.out.println("线段树展示开始"); + int N = 100; + SegmentTree st = new SegmentTree(N); + // 查询8~19范围上的最小值 + System.out.println(st.query(8, 19, 1, N, 1)); + // 把6~14范围上对应的值都修改成56 + st.update(6, 14, 56, 1, N, 1); + // 查询8~19范围上的最小值 + System.out.println(st.query(8, 19, 1, N, 1)); + // 以上是线段树的用法,你可以随意使用update和query方法 + // 线段树的详解请看体系学习班 + System.out.println("线段树展示结束"); + + // 以下为正式测试 + int len = 10; + int value = 8; + int testTimes = 10000; + System.out.println("对数器测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * len) + 1; + int m = (int) (Math.random() * len) + 1; + int[][] map = randomMatrix(n, m, value); + int ans1 = jump1(map); + int ans2 = jump2(map); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("对数器测试结束"); + } + +} diff --git a/大厂刷题班/class39/Code05_0123Disappear.java b/大厂刷题班/class39/Code05_0123Disappear.java new file mode 100644 index 0000000..c1b64aa --- /dev/null +++ b/大厂刷题班/class39/Code05_0123Disappear.java @@ -0,0 +1,82 @@ +package class39; + +// 真实笔试,忘了哪个公司,但是绝对大厂 +// 一个子序列的消除规则如下: +// 1) 在某一个子序列中,如果'1'的左边有'0',那么这两个字符->"01"可以消除 +// 2) 在某一个子序列中,如果'3'的左边有'2',那么这两个字符->"23"可以消除 +// 3) 当这个子序列的某个部分消除之后,认为其他字符会自动贴在一起,可以继续寻找消除的机会 +// 比如,某个子序列"0231",先消除掉"23",那么剩下的字符贴在一起变成"01",继续消除就没有字符了 +// 如果某个子序列通过最优良的方式,可以都消掉,那么这样的子序列叫做“全消子序列” +// 一个只由'0'、'1'、'2'、'3'四种字符组成的字符串str,可以生成很多子序列,返回“全消子序列”的最大长度 +// 字符串str长度 <= 200 +// 体系学习班,代码46节,第2题+第3题 +public class Code05_0123Disappear { + + // str[L...R]上,都能消掉的子序列,最长是多少? + public static int f(char[] str, int L, int R) { + if (L >= R) { + return 0; + } + if (L == R - 1) { + return (str[L] == '0' && str[R] == '1') || (str[L] == '2' && str[R] == '3') ? 2 : 0; + } + // L...R 有若干个字符 > 2 + // str[L...R]上,都能消掉的子序列,最长是多少? + // 可能性1,能消掉的子序列完全不考虑str[L],最长是多少? + int p1 = f(str, L + 1, R); + if (str[L] == '1' || str[L] == '3') { + return p1; + } + // str[L] =='0' 或者 '2' + // '0' 去找 '1' + // '2' 去找 '3' + char find = str[L] == '0' ? '1' : '3'; + int p2 = 0; + // L() ...... + for (int i = L + 1; i <= R; i++) { + // L(0) ..... i(1) i+1....R + if (str[i] == find) { + p2 = Math.max(p2, f(str, L + 1, i - 1) + 2 + f(str, i + 1, R)); + } + } + return Math.max(p1, p2); + } + + public static int maxDisappear(String str) { + if (str == null || str.length() == 0) { + return 0; + } + return disappear(str.toCharArray(), 0, str.length() - 1); + } + + // s[l..r]范围上,如题目所说的方式,最长的都能消掉的子序列长度 + public static int disappear(char[] s, int l, int r) { + if (l >= r) { + return 0; + } + if (l == r - 1) { + return (s[l] == '0' && s[r] == '1') || (s[l] == '2' && s[r] == '3') ? 2 : 0; + } + int p1 = disappear(s, l + 1, r); + if (s[l] == '1' || s[l] == '3') { + return p1; + } + int p2 = 0; + char find = s[l] == '0' ? '1' : '3'; + for (int i = l + 1; i <= r; i++) { + if (s[i] == find) { + p2 = Math.max(p2, disappear(s, l + 1, i - 1) + 2 + disappear(s, i + 1, r)); + } + } + return Math.max(p1, p2); + } + + public static void main(String[] args) { + String str1 = "010101"; + System.out.println(maxDisappear(str1)); + + String str2 = "021331"; + System.out.println(maxDisappear(str2)); + } + +} diff --git a/大厂刷题班/class40/Code01_SplitTo01.java b/大厂刷题班/class40/Code01_SplitTo01.java new file mode 100644 index 0000000..25713fc --- /dev/null +++ b/大厂刷题班/class40/Code01_SplitTo01.java @@ -0,0 +1,195 @@ +package class40; + +import java.util.ArrayList; +import java.util.HashMap; + +// 腾讯 +// 分裂问题 +// 一个数n,可以分裂成一个数组[n/2, n%2, n/2] +// 这个数组中哪个数不是1或者0,就继续分裂下去 +// 比如 n = 5,一开始分裂成[2, 1, 2] +// [2, 1, 2]这个数组中不是1或者0的数,会继续分裂下去,比如两个2就继续分裂 +// [2, 1, 2] -> [1, 0, 1, 1, 1, 0, 1] +// 那么我们说,5最后分裂成[1, 0, 1, 1, 1, 0, 1] +// 每一个数都可以这么分裂,在最终分裂的数组中,假设下标从1开始 +// 给定三个数n、l、r,返回n的最终分裂数组里[l,r]范围上有几个1 +// n <= 2 ^ 50,n是long类型 +// r - l <= 50000,l和r是int类型 +// 我们的课加个码: +// n是long类型随意多大都行 +// l和r也是long类型随意多大都行,但要保证l<=r +public class Code01_SplitTo01 { + +// public static long nums3(long n, long l, long r) { +// HashMap lenMap = new HashMap<>(); +// len(n, lenMap); +// HashMap onesMap = new HashMap<>(); +// ones(n, onesMap); +// } + + // n = 100 + // n = 100, 最终裂变的数组,长度多少? + // n = 50, 最终裂变的数组,长度多少? + // n = 25, 最终裂变的数组,长度多少? + // .. + // n = 1 ,.最终裂变的数组,长度多少? + // 请都填写到lenMap中去! + public static long len(long n, HashMap lenMap) { + if (n == 1 || n == 0) { + lenMap.put(n, 1L); + return 1; + } else { + // n > 1 + long half = len(n / 2, lenMap); + long all = half * 2 + 1; + lenMap.put(n, all); + return all; + } + } + + // n = 100 + // n = 100, 最终裂变的数组中,一共有几个1 + // n = 50, 最终裂变的数组,一共有几个1 + // n = 25, 最终裂变的数组,一共有几个1 + // .. + // n = 1 ,.最终裂变的数组,一共有几个1 + // 请都填写到onesMap中去! + public static long ones(long num, HashMap onesMap) { + if (num == 1 || num == 0) { + onesMap.put(num, num); + return num; + } + // n > 1 + long half = ones(num / 2, onesMap); + long mid = num % 2 == 1 ? 1 : 0; + long all = half * 2 + mid; + onesMap.put(num, all); + return all; + } + + // + + public static long nums1(long n, long l, long r) { + if (n == 1 || n == 0) { + return n == 1 ? 1 : 0; + } + long half = size(n / 2); + long left = l > half ? 0 : nums1(n / 2, l, Math.min(half, r)); + long mid = (l > half + 1 || r < half + 1) ? 0 : (n & 1); + long right = r > half + 1 ? nums1(n / 2, Math.max(l - half - 1, 1), r - half - 1) : 0; + return left + mid + right; + } + + public static long size(long n) { + if (n == 1 || n == 0) { + return 1; + } else { + long half = size(n / 2); + return (half << 1) + 1; + } + } + + public static long nums2(long n, long l, long r) { + HashMap allMap = new HashMap<>(); + return dp(n, l, r, allMap); + } + + public static long dp(long n, long l, long r, HashMap allMap) { + if (n == 1 || n == 0) { + return n == 1 ? 1 : 0; + } + long half = size(n / 2); + long all = (half << 1) + 1; + long mid = n & 1; + if (l == 1 && r >= all) { + if (allMap.containsKey(n)) { + return allMap.get(n); + } else { + long count = dp(n / 2, 1, half, allMap); + long ans = (count << 1) + mid; + allMap.put(n, ans); + return ans; + } + } else { + mid = (l > half + 1 || r < half + 1) ? 0 : mid; + long left = l > half ? 0 : dp(n / 2, l, Math.min(half, r), allMap); + long right = r > half + 1 ? dp(n / 2, Math.max(l - half - 1, 1), r - half - 1, allMap) : 0; + return left + mid + right; + } + } + + // 为了测试 + // 彻底生成n的最终分裂数组返回 + public static ArrayList test(long n) { + ArrayList arr = new ArrayList<>(); + process(n, arr); + return arr; + } + + public static void process(long n, ArrayList arr) { + if (n == 1 || n == 0) { + arr.add((int) n); + } else { + process(n / 2, arr); + arr.add((int) (n % 2)); + process(n / 2, arr); + } + } + + public static void main(String[] args) { + long num = 671; + ArrayList ans = test(num); + int testTime = 10000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int a = (int) (Math.random() * ans.size()) + 1; + int b = (int) (Math.random() * ans.size()) + 1; + int l = Math.min(a, b); + int r = Math.max(a, b); + int ans1 = 0; + for (int j = l - 1; j < r; j++) { + if (ans.get(j) == 1) { + ans1++; + } + } + long ans2 = nums1(num, l, r); + long ans3 = nums2(num, l, r); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + System.out.println("=============="); + + System.out.println("性能测试开始"); + num = (2L << 50) + 22517998136L; + long l = 30000L; + long r = 800000200L; + long start; + long end; + start = System.currentTimeMillis(); + System.out.println("nums1结果 : " + nums1(num, l, r)); + end = System.currentTimeMillis(); + System.out.println("nums1花费时间(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + System.out.println("nums2结果 : " + nums2(num, l, r)); + end = System.currentTimeMillis(); + System.out.println("nums2花费时间(毫秒) : " + (end - start)); + System.out.println("性能测试结束"); + System.out.println("=============="); + + System.out.println("单独展示nums2方法强悍程度测试开始"); + num = (2L << 55) + 22517998136L; + l = 30000L; + r = 6431000002000L; + start = System.currentTimeMillis(); + System.out.println("nums2结果 : " + nums2(num, l, r)); + end = System.currentTimeMillis(); + System.out.println("nums2花费时间(毫秒) : " + (end - start)); + System.out.println("单独展示nums2方法强悍程度测试结束"); + System.out.println("=============="); + + } + +} diff --git a/大厂刷题班/class40/Code02_Mod3Max.java b/大厂刷题班/class40/Code02_Mod3Max.java new file mode 100644 index 0000000..6bbc985 --- /dev/null +++ b/大厂刷题班/class40/Code02_Mod3Max.java @@ -0,0 +1,247 @@ +package class40; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.TreeSet; + +// 来自去哪儿网 +// 给定一个arr,里面的数字都是0~9 +// 你可以随意使用arr中的数字,哪怕打乱顺序也行 +// 请拼出一个能被3整除的,最大的数字,用str形式返回 +public class Code02_Mod3Max { + + public static String max1(int[] arr) { + Arrays.sort(arr); + for (int l = 0, r = arr.length - 1; l < r; l++, r--) { + int tmp = arr[l]; + arr[l] = arr[r]; + arr[r] = tmp; + } + StringBuilder builder = new StringBuilder(); + TreeSet set = new TreeSet<>((a, b) -> Integer.valueOf(b).compareTo(Integer.valueOf(a))); + process1(arr, 0, builder, set); + return set.isEmpty() ? "" : set.first(); + } + + public static void process1(int[] arr, int index, StringBuilder builder, TreeSet set) { + if (index == arr.length) { + if (builder.length() != 0 && Integer.valueOf(builder.toString()) % 3 == 0) { + set.add(builder.toString()); + } + } else { + process1(arr, index + 1, builder, set); + builder.append(arr[index]); + process1(arr, index + 1, builder, set); + builder.deleteCharAt(builder.length() - 1); + } + } + + public static String max2(int[] arr) { + if (arr == null || arr.length == 0) { + return ""; + } + Arrays.sort(arr); + for (int l = 0, r = arr.length - 1; l < r; l++, r--) { + int tmp = arr[l]; + arr[l] = arr[r]; + arr[r] = tmp; + } + if (arr[0] == 0) { + return "0"; + } + String ans = process2(arr, 0, 0); + String res = ans.replaceAll("^(0+)", ""); + if (!res.equals("")) { + return res; + } + return ans.equals("") ? ans : "0"; + } + + // arr中的数字一定是0~9 + // arr是经过排序的,并且是从大到小排序,比如[9,8,7,7,7,3,1]等 + // 这个递归函数的含义 : + // 在arr[index...一直到最后]上做选择,arr[0...index-1]就当不存在 + // 每个位置的字符可以要、也可以不要,但是!选出来的数字拼完之后的结果,在%3之后,余数一定要是mod! + // 返回在上面设定的情况下,最大的数是多少? + // 如果存在这样的数,返回字符串的形式 + // 如果不存在这样的数,返回特殊字符串,比如"$",代表不可能 + // 这个递归函数可以很轻易的改出动态规划 + public static String process2(int[] arr, int index, int mod) { + if (index == arr.length) { + return mod == 0 ? "" : "$"; + } + String p1 = "$"; + int nextMod = nextMod(mod, arr[index] % 3); + String next = process2(arr, index + 1, nextMod); + if (!next.equals("$")) { + p1 = String.valueOf(arr[index]) + next; + } + String p2 = process2(arr, index + 1, mod); + if (p1.equals("$") && p2.equals("$")) { + return "$"; + } + if (!p1.equals("$") && !p2.equals("$")) { + return smaller(p1, p2) ? p2 : p1; + } + return p1.equals("$") ? p2 : p1; + } + + public static int nextMod(int require, int current) { + if (require == 0) { + if (current == 0) { + return 0; + } else if (current == 1) { + return 2; + } else { + return 1; + } + } else if (require == 1) { + if (current == 0) { + return 1; + } else if (current == 1) { + return 0; + } else { + return 2; + } + } else { // require == 2 + if (current == 0) { + return 2; + } else if (current == 1) { + return 1; + } else { + return 0; + } + } + } + + public static boolean smaller(String p1, String p2) { + if (p1.length() != p2.length()) { + return p1.length() < p2.length(); + } + return p1.compareTo(p2) < 0; + } + + // 贪心的思路解法 : + // 先得到数组的累加和,记为sum + // 1) 如果sum%3==0,说明所有数从大到小拼起来就可以了 + // 2) 如果sum%3==1,说明多了一个余数1, + // 只需要删掉一个最小的数(该数是%3==1的数); + // 如果没有,只需要删掉两个最小的数(这两个数都是%3==2的数); + // 3) 如果sum%3==2,说明多了一个余数2, + // 只需要删掉一个最小的数(该数是%3==2的数); + // 如果没有,只需要删掉两个最小的数(这两个数都是%3==1的数); + // 如果上面都做不到,说明拼不成 + public static String max3(int[] A) { + if (A == null || A.length == 0) { + return ""; + } + int mod = 0; + ArrayList arr = new ArrayList<>(); + for (int num : A) { + arr.add(num); + mod += num; + mod %= 3; + } + if ((mod == 1 || mod == 2) && !remove(arr, mod, 3 - mod)) { + return ""; + } + if (arr.isEmpty()) { + return ""; + } + arr.sort((a, b) -> b - a); + if (arr.get(0) == 0) { + return "0"; + } + StringBuilder builder = new StringBuilder(); + for (int num : arr) { + builder.append(num); + } + return builder.toString(); + } + + // 在arr中,要么删掉最小的一个、且%3之后余数是first的数 + // 如果做不到,删掉最小的两个、且%3之后余数是second的数 + // 如果能做到返回true,不能做到返回false + public static boolean remove(ArrayList arr, int first, int second) { + if (arr.size() == 0) { + return false; + } + arr.sort((a, b) -> compare(a, b, first, second)); + int size = arr.size(); + if (arr.get(size - 1) % 3 == first) { + arr.remove(size - 1); + return true; + } else if (size > 1 && arr.get(size - 1) % 3 == second && arr.get(size - 2) % 3 == second) { + arr.remove(size - 1); + arr.remove(size - 2); + return true; + } else { + return false; + } + } + + // a和b比较: + // 如果余数一样,谁大谁放前面 + // 如果余数不一样,余数是0的放最前面、余数是s的放中间、余数是f的放最后 + public static int compare(int a, int b, int f, int s) { + int ma = a % 3; + int mb = b % 3; + if (ma == mb) { + return b - a; + } else { + if (ma == 0 || mb == 0) { + return ma == 0 ? -1 : 1; + } else { + return ma == s ? -1 : 1; + } + } + } + + // 为了测试 + public static int[] randomArray(int len) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * 10); + } + return arr; + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 10; + int testTimes = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int len = (int) (Math.random() * N); + int[] arr1 = randomArray(len); + int[] arr2 = copyArray(arr1); + int[] arr3 = copyArray(arr1); + String ans1 = max1(arr1); + String ans2 = max2(arr2); + String ans3 = max3(arr3); + if (!ans1.equals(ans2) || !ans1.equals(ans3)) { + System.out.println("出错了!"); + for (int num : arr3) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + break; + } + } + System.out.println("测试结束"); + + } + +} diff --git a/大厂刷题班/class40/Code03_MaxMeetingScore.java b/大厂刷题班/class40/Code03_MaxMeetingScore.java new file mode 100644 index 0000000..467b08d --- /dev/null +++ b/大厂刷题班/class40/Code03_MaxMeetingScore.java @@ -0,0 +1,109 @@ +package class40; + +import java.util.Arrays; +import java.util.PriorityQueue; + +// 给定int[][] meetings,比如 +// { +// {66, 70} 0号会议截止时间66,获得收益70 +// {25, 90} 1号会议截止时间25,获得收益90 +// {50, 30} 2号会议截止时间50,获得收益30 +// } +// 一开始的时间是0,任何会议都持续10的时间,但是一个会议一定要在该会议截止时间之前开始 +// 只有一个会议室,任何会议不能共用会议室,一旦一个会议被正确安排,将获得这个会议的收益 +// 请返回最大的收益 +public class Code03_MaxMeetingScore { + + public static int maxScore1(int[][] meetings) { + Arrays.sort(meetings, (a, b) -> a[0] - b[0]); + int[][] path = new int[meetings.length][]; + int size = 0; + return process1(meetings, 0, path, size); + } + + public static int process1(int[][] meetings, int index, int[][] path, int size) { + if (index == meetings.length) { + int time = 0; + int ans = 0; + for (int i = 0; i < size; i++) { + if (time + 10 <= path[i][0]) { + ans += path[i][1]; + time += 10; + } else { + return 0; + } + } + return ans; + } + int p1 = process1(meetings, index + 1, path, size); + path[size] = meetings[index]; + int p2 = process1(meetings, index + 1, path, size + 1); + // path[size] = null; + return Math.max(p1, p2); + } + + public static int maxScore2(int[][] meetings) { + Arrays.sort(meetings, (a, b) -> a[0] - b[0]); + PriorityQueue heap = new PriorityQueue<>(); + int time = 0; + // 已经把所有会议,按照截止时间,从小到大,排序了! + // 截止时间一样的,谁排前谁排后,无所谓 + for (int i = 0; i < meetings.length; i++) { + if (time + 10 <= meetings[i][0]) { + heap.add(meetings[i][1]); + time += 10; + } else { + if (!heap.isEmpty() && heap.peek() < meetings[i][1]) { + heap.poll(); + heap.add(meetings[i][1]); + } + } + } + int ans = 0; + while (!heap.isEmpty()) { + ans += heap.poll(); + } + return ans; + } + + public static int[][] randomMeetings(int n, int t, int s) { + int[][] ans = new int[n][2]; + for (int i = 0; i < n; i++) { + ans[i][0] = (int) (Math.random() * t) + 1; + ans[i][1] = (int) (Math.random() * s) + 1; + } + return ans; + } + + public static int[][] copyMeetings(int[][] meetings) { + int n = meetings.length; + int[][] ans = new int[n][2]; + for (int i = 0; i < n; i++) { + ans[i][0] = meetings[i][0]; + ans[i][1] = meetings[i][1]; + } + return ans; + } + + public static void main(String[] args) { + int n = 12; + int t = 100; + int s = 500; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * n) + 1; + int[][] meetings1 = randomMeetings(size, t, s); + int[][] meetings2 = copyMeetings(meetings1); + int ans1 = maxScore1(meetings1); + int ans2 = maxScore2(meetings2); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class40/Code04_LetASorted.java b/大厂刷题班/class40/Code04_LetASorted.java new file mode 100644 index 0000000..051bd9b --- /dev/null +++ b/大厂刷题班/class40/Code04_LetASorted.java @@ -0,0 +1,54 @@ +package class40; + +// 给定两个数组A和B,长度都是N +// A[i]不可以在A中和其他数交换,只可以选择和B[i]交换(0<=i= lastA && process1(A, B, i + 1, A[i])) { + return true; + } + // 第二种选择 : A[i]和B[i]交换 + if (B[i] >= lastA && process1(A, B, i + 1, B[i])) { + return true; + } + return false; + } + + public static boolean letASorted2(int[] A, int[] B) { + return process2(A, B, 0, true); + } + + // 当前推进到了i位置,对于A和B都是i位置 + // A[i]前一个数字是否来自A : + // 如果来自A,fromA = true;如果来自B,fromA = false; + // 能否通过题意中的操作,A[i] B[i] 让A整体有序 + // 好处:可变参数成了int + boolean,时间复杂度可以做到O(N) + public static boolean process2(int[] A, int[] B, int i, boolean fromA) { + if (i == A.length) { + return true; + } + if (i == 0 || (A[i] >= (fromA ? A[i - 1] : B[i - 1])) && process2(A, B, i + 1, true)) { + return true; + } + if (i == 0 || (B[i] >= (fromA ? A[i - 1] : B[i - 1])) && process2(A, B, i + 1, false)) { + return true; + } + return false; + } + + // 也可以彻底的贪心:就让A此时的值尽量小!也是可以的。时间复杂度O(N) + +} diff --git a/大厂刷题班/class40/Code05_AllSame.java b/大厂刷题班/class40/Code05_AllSame.java new file mode 100644 index 0000000..ae8359b --- /dev/null +++ b/大厂刷题班/class40/Code05_AllSame.java @@ -0,0 +1,135 @@ +package class40; + +// 来自腾讯 +// 比如arr = {3,1,2,4} +// 下标对应是:0 1 2 3 +// 你最开始选择一个下标进行操作,一旦最开始确定了是哪个下标,以后都只能在这个下标上进行操作 +// 比如你选定1下标,1下标上面的数字是1,你可以选择变化这个数字,比如你让这个数字变成2 +// 那么arr = {3,2,2,4} +// 下标对应是:0 1 2 3 +// 因为你最开始确定了1这个下标,所以你以后都只能对这个下标进行操作, +// 但是,和你此时下标上的数字一样的、且位置连成一片的数字,会跟着一起变 +// 比如你选择让此时下标1的数字2变成3, +// 那么arr = {3,3,3,4} 可以看到下标1和下标2的数字一起变成3,这是规则!一定会一起变 +// 下标对应是:0 1 2 3 +// 接下来,你还是只能对1下标进行操作,那么数字一样的、且位置连成一片的数字(arr[0~2]这个范围)都会一起变 +// 决定变成4 +// 那么arr = {4,4,4,4} +// 下标对应是:0 1 2 3 +// 至此,所有数都成一样的了,你在下标1上做了3个决定(第一次变成2,第二次变成3,第三次变成4), +// 因为联动规则,arr全刷成一种数字了 +// 给定一个数组arr,最开始选择哪个下标,你随意 +// 你的目的是一定要让arr都成为一种数字,注意联动效果会一直生效 +// 返回最小的变化数 +// arr长度 <= 200, arr中的值 <= 10^6 +public class Code05_AllSame { + + public static int allSame1(int[] arr) { + int ans = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + ans = Math.min(ans, process1(arr, i - 1, arr[i], i + 1)); + } + return ans; + } + + // 左边arr[0..left],如果left == -1,说明没有左边了 + // 右边arr[right...n-1],如果right == n,说明没有右边了 + // 中间的值是midV,中间的值代表中间一整个部分的值,中间部分有可能是一个数,也可能是一堆数,但是值都为midV + // 返回arr都刷成一样的,最小代价是多少 + // left 可能性 : N + // right 可能性 : N + // midV 可能性 : arr中的最大值! + public static int process1(int[] arr, int left, int midV, int right) { + for (; left >= 0 && arr[left] == midV;) { + left--; + } + for (; right < arr.length && arr[right] == midV;) { + right++; + } + if (left == -1 && right == arr.length) { + return 0; + } + int p1 = Integer.MAX_VALUE; + if (left >= 0) { + p1 = process1(arr, left - 1, arr[left], right) + 1; + } + int p2 = Integer.MAX_VALUE; + if (right < arr.length) { + p2 = process1(arr, left, arr[right], right + 1) + 1; + } + return Math.min(p1, p2); + } + + public static int allSame2(int[] arr) { + int ans = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + ans = Math.min(ans, process2(arr, i - 1, true, i + 1)); + } + return ans; + } + + // 左边arr[0..l],如果left == -1,说明没有左边了 + // 右边arr[r...n-1],如果right == n,说明没有右边了 + // 中间的值代表arr[l+1...r-1]这个部分的值已经刷成了一种 + // 中间的值,如果和arr[l+1]一样,isLeft就是true + // 中间的值,如果和arr[r-1]一样,isLeft就是false + // 返回arr都刷成一样的,最小代价是多少 + // left 可能性 : N + // right 可能性 : N + // isLeft 可能性 : true/false,两种 + public static int process2(int[] arr, int l, boolean isLeft, int r) { + int left = l; + for (; left >= 0 && arr[left] == (isLeft ? arr[l + 1] : arr[r - 1]);) { + left--; + } + int right = r; + for (; right < arr.length && arr[right] == (isLeft ? arr[l + 1] : arr[r - 1]);) { + right++; + } + if (left == -1 && right == arr.length) { + return 0; + } + int p1 = Integer.MAX_VALUE; + if (left >= 0) { + p1 = process2(arr, left - 1, true, right) + 1; + } + int p2 = Integer.MAX_VALUE; + if (right < arr.length) { + p2 = process2(arr, left, false, right + 1) + 1; + } + return Math.min(p1, p2); + } + // 如上的递归,请改动态规划,具体参考体系学习班,动态规划大章节! + + // 为了测试 + public static int[] randomArray(int maxSize, int maxNum) { + int size = 2 + (int) (Math.random() * maxSize); + int[] arr = new int[size]; + for (int i = 0; i < size; i++) { + arr[i] = 1 + (int) (Math.random() * maxSize); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + System.out.println("测试开始"); + for (int i = 0; i < 10000; i++) { + int[] arr = randomArray(20, 10); + int ans1 = allSame1(arr); + int ans2 = allSame2(arr); + if (ans1 != ans2) { + System.out.println("出错了!!!"); + for (int i1 : arr) { + System.out.print(i1 + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class41/Code01_MinSwapTimes.java b/大厂刷题班/class41/Code01_MinSwapTimes.java new file mode 100644 index 0000000..70ad912 --- /dev/null +++ b/大厂刷题班/class41/Code01_MinSwapTimes.java @@ -0,0 +1,88 @@ +package class41; + +// 来自小红书 +// 一个无序数组长度为n,所有数字都不一样,并且值都在[0...n-1]范围上 +// 返回让这个无序数组变成有序数组的最小交换次数 +public class Code01_MinSwapTimes { + + // 纯暴力,arr长度大一点都会超时 + // 但是绝对正确 + public static int minSwap1(int[] arr) { + return process1(arr, 0); + } + + // 让arr变有序,最少的交换次数是多少!返回 + // times, 之前已经做了多少次交换 + public static int process1(int[] arr, int times) { + boolean sorted = true; + for (int i = 1; i < arr.length; i++) { + if (arr[i - 1] > arr[i]) { + sorted = false; + break; + } + } + if (sorted) { + return times; + } + // 数组现在是无序的状态! + if (times >= arr.length - 1) { + return Integer.MAX_VALUE; + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + swap(arr, i, j); + ans = Math.min(ans, process1(arr, times + 1)); + swap(arr, i, j); + } + } + return ans; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 已知arr中,只有0~n-1这些值,并且都出现1次 + public static int minSwap2(int[] arr) { + int ans = 0; + for (int i = 0; i < arr.length; i++) { + while (i != arr[i]) { + swap(arr, i, arr[i]); + ans++; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = i; + } + for (int i = 0; i < len; i++) { + swap(arr, i, (int) (Math.random() * len)); + } + return arr; + } + + public static void main(String[] args) { + int n = 6; + int testTime = 2000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n) + 1; + int[] arr = randomArray(len); + int ans1 = minSwap1(arr); + int ans2 = minSwap2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class41/Code02_PoemProblem.java b/大厂刷题班/class41/Code02_PoemProblem.java new file mode 100644 index 0000000..617ca29 --- /dev/null +++ b/大厂刷题班/class41/Code02_PoemProblem.java @@ -0,0 +1,381 @@ +package class41; + +import java.util.Arrays; +import java.util.HashMap; + +// 来自小红书 +// 有四种诗的韵律分别为: AABB、ABAB、ABBA、AAAA +// 比如 : 1 1 3 3就属于AABB型的韵律、6 6 6 6就属于AAAA型的韵律等等 +// 一个数组arr,当然可以生成很多的子序列,如果某个子序列一直以韵律的方式连接起来,我们称这样的子序列是有效的 +// 比如, arr = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 } +// arr的一个子序列为{1, 1, 1, 1, 2, 3, 3, 2, 4, 3, 4, 3, 7, 7, 9, 9} +// 其中1, 1, 1, 1是AAAA、2, 3, 3, 2是ABBA、4, 3, 4, 3是ABAB、7, 7, 9, 9是AABB +// 可以看到,整个子序列一直以韵律的方式连接起来,所以这个子序列是有效的 +// 给定一个数组arr, 返回最长的有效子序列长度 +// 题目限制 : arr长度 <= 4000, arr中的值<= 10^9 +// 离散化之后,arr长度 <= 4000, arr中的值<= 4000 +public class Code02_PoemProblem { + + // arr[i.....]符合规则连接的最长子序列长度 +// public static int zuo(int[] arr, int i) { +// if (i + 4 > arr.length) { +// return 0; +// } +// // 最终的,符合规则连接的最长子序列长度,就是不要i位置的字符 +// int p0 = zuo(arr, i + 1); +// // p1使用for循环搞定的! +// int p1 = 找到arr[i..s]是最短的,且能搞出AABB来的(4个) + zuo(arr, s + 1); +// // p2使用for循环搞定的! +// int p2 = 找到arr[i..t]是最短的,且能搞出ABAB来的(4个) + zuo(arr, t + 1); +// // p3使用for循环搞定的! +// int p3 = 找到arr[i..k]是最短的,且能搞出ABBA来的(4个) + zuo(arr, k + 1); +// // p4没用 +// int p4 = 找到arr[i..f]是最短的,且能搞出AAAA来的(4个) + zuo(arr, f + 1); +// return p0~p4的最大值 +// } + + // AABB + // ABAB + // ABBA + // AAAA + public static int maxLen1(int[] arr) { + if (arr == null || arr.length < 4) { + return 0; + } + int[] path = new int[arr.length]; + return process1(arr, 0, path, 0); + } + + public static int process1(int[] arr, int index, int[] path, int size) { + if (index == arr.length) { + if (size % 4 != 0) { + return 0; + } else { + for (int i = 0; i < size; i += 4) { + if (!valid(path, i)) { + return 0; + } + } + return size; + } + } else { + int p1 = process1(arr, index + 1, path, size); + path[size] = arr[index]; + int p2 = process1(arr, index + 1, path, size + 1); + return Math.max(p1, p2); + } + } + + public static boolean valid(int[] p, int i) { + // AABB + // ABAB + // ABBA + // AAAA + return (p[i] == p[i + 1] && p[i + 2] == p[i + 3]) + || (p[i] == p[i + 2] && p[i + 1] == p[i + 3] && p[i] != p[i + 1]) + || (p[i] == p[i + 3] && p[i + 1] == p[i + 2] && p[i] != p[i + 1]); + } + + // 0 : [3,6,9] + // 1 : [2,7,13] + // 2 : [23] + // [ + // [3,6,9] + // ] + public static int maxLen2(int[] arr) { + if (arr == null || arr.length < 4) { + return 0; + } + int n = arr.length; + int[] sorted = Arrays.copyOf(arr, n); + Arrays.sort(sorted); + HashMap vmap = new HashMap<>(); + int index = 0; + vmap.put(sorted[0], index++); + for (int i = 1; i < n; i++) { + if (sorted[i] != sorted[i - 1]) { + vmap.put(sorted[i], index++); + } + } + int[] sizeArr = new int[index]; + for (int i = 0; i < n; i++) { + arr[i] = vmap.get(arr[i]); + sizeArr[arr[i]]++; + } + int[][] imap = new int[index][]; + for (int i = 0; i < index; i++) { + imap[i] = new int[sizeArr[i]]; + } + for (int i = n - 1; i >= 0; i--) { + imap[arr[i]][--sizeArr[arr[i]]] = i; + } + return process2(arr, imap, 0); + } + + // AABB + // ABAB + // ABBA + // AAAA + public static int process2(int[] varr, int[][] imap, int i) { + if (i + 4 > varr.length) { + return 0; + } + int p0 = process2(varr, imap, i + 1); + // AABB + int p1 = 0; + int rightClosedP1A2 = rightClosed(imap, varr[i], i); + if (rightClosedP1A2 != -1) { + for (int next = rightClosedP1A2 + 1; next < varr.length; next++) { + if (varr[i] != varr[next]) { + int rightClosedP1B2 = rightClosed(imap, varr[next], next); + if (rightClosedP1B2 != -1) { + p1 = Math.max(p1, 4 + process2(varr, imap, rightClosedP1B2 + 1)); + } + } + } + } + + // ABAB + int p2 = 0; + for (int p2B1 = i + 1; p2B1 < varr.length; p2B1++) { + if (varr[i] != varr[p2B1]) { + int rightClosedP2A2 = rightClosed(imap, varr[i], p2B1); + if (rightClosedP2A2 != -1) { + int rightClosedP2B2 = rightClosed(imap, varr[p2B1], rightClosedP2A2); + if (rightClosedP2B2 != -1) { + p2 = Math.max(p2, 4 + process2(varr, imap, rightClosedP2B2 + 1)); + } + } + } + } + + // ABBA + int p3 = 0; + for (int p3B1 = i + 1; p3B1 < varr.length; p3B1++) { + if (varr[i] != varr[p3B1]) { + int rightClosedP3B2 = rightClosed(imap, varr[p3B1], p3B1); + if (rightClosedP3B2 != -1) { + int rightClosedP3A2 = rightClosed(imap, varr[i], rightClosedP3B2); + if (rightClosedP3A2 != -1) { + p3 = Math.max(p3, 4 + process2(varr, imap, rightClosedP3A2 + 1)); + } + } + } + } + // AAAA + int p4 = 0; + int rightClosedP4A2 = rightClosed(imap, varr[i], i); + int rightClosedP4A3 = rightClosedP4A2 == -1 ? -1 : rightClosed(imap, varr[i], rightClosedP4A2); + int rightClosedP4A4 = rightClosedP4A3 == -1 ? -1 : rightClosed(imap, varr[i], rightClosedP4A3); + if (rightClosedP4A4 != -1) { + p4 = Math.max(p4, 4 + process2(varr, imap, rightClosedP4A4 + 1)); + } + return Math.max(p0, Math.max(Math.max(p1, p2), Math.max(p3, p4))); + } + + public static int rightClosed(int[][] imap, int v, int i) { + int left = 0; + int right = imap[v].length - 1; + int ans = -1; + while (left <= right) { + int mid = (left + right) / 2; + if (imap[v][mid] <= i) { + left = mid + 1; + } else { + ans = mid; + right = mid - 1; + } + } + return ans == -1 ? -1 : imap[v][ans]; + } + + public static int maxLen3(int[] arr) { + if (arr == null || arr.length < 4) { + return 0; + } + int n = arr.length; + int[] sorted = Arrays.copyOf(arr, n); + Arrays.sort(sorted); + HashMap vmap = new HashMap<>(); + int index = 0; + vmap.put(sorted[0], index++); + for (int i = 1; i < n; i++) { + if (sorted[i] != sorted[i - 1]) { + vmap.put(sorted[i], index++); + } + } + int[] sizeArr = new int[index]; + for (int i = 0; i < n; i++) { + arr[i] = vmap.get(arr[i]); + sizeArr[arr[i]]++; + } + int[][] imap = new int[index][]; + for (int i = 0; i < index; i++) { + imap[i] = new int[sizeArr[i]]; + } + for (int i = n - 1; i >= 0; i--) { + imap[arr[i]][--sizeArr[arr[i]]] = i; + } + int[] dp = new int[n + 1]; + for (int i = n - 4; i >= 0; i--) { + int p0 = dp[i + 1]; + // AABB + int p1 = 0; + int rightClosedP1A2 = rightClosed(imap, arr[i], i); + if (rightClosedP1A2 != -1) { + for (int next = rightClosedP1A2 + 1; next < arr.length; next++) { + if (arr[i] != arr[next]) { + int rightClosedP1B2 = rightClosed(imap, arr[next], next); + if (rightClosedP1B2 != -1) { + p1 = Math.max(p1, 4 + dp[rightClosedP1B2 + 1]); + } + } + } + } + + // ABAB + int p2 = 0; + for (int p2B1 = i + 1; p2B1 < arr.length; p2B1++) { + if (arr[i] != arr[p2B1]) { + int rightClosedP2A2 = rightClosed(imap, arr[i], p2B1); + if (rightClosedP2A2 != -1) { + int rightClosedP2B2 = rightClosed(imap, arr[p2B1], rightClosedP2A2); + if (rightClosedP2B2 != -1) { + p2 = Math.max(p2, 4 + dp[rightClosedP2B2 + 1]); + } + } + } + } + + // ABBA + int p3 = 0; + for (int p3B1 = i + 1; p3B1 < arr.length; p3B1++) { + if (arr[i] != arr[p3B1]) { + int rightClosedP3B2 = rightClosed(imap, arr[p3B1], p3B1); + if (rightClosedP3B2 != -1) { + int rightClosedP3A2 = rightClosed(imap, arr[i], rightClosedP3B2); + if (rightClosedP3A2 != -1) { + p3 = Math.max(p3, 4 + dp[rightClosedP3A2 + 1]); + } + } + } + } + // AAAA + int p4 = 0; + int rightClosedP4A2 = rightClosed(imap, arr[i], i); + int rightClosedP4A3 = rightClosedP4A2 == -1 ? -1 : rightClosed(imap, arr[i], rightClosedP4A2); + int rightClosedP4A4 = rightClosedP4A3 == -1 ? -1 : rightClosed(imap, arr[i], rightClosedP4A3); + if (rightClosedP4A4 != -1) { + p4 = Math.max(p4, 4 + dp[rightClosedP4A4 + 1]); + } + dp[i] = Math.max(p0, Math.max(Math.max(p1, p2), Math.max(p3, p4))); + } + return dp[0]; + } + + // 课堂有同学提出了贪心策略(这题还真是有贪心策略),是正确的 + // AABB + // ABAB + // ABBA + // AAAA + // 先看前三个规则:AABB、ABAB、ABBA + // 首先A、A、B、B的全排列为: + // AABB -> AABB + // ABAB -> ABAB + // ABBA -> ABBA + // BBAA -> 等同于AABB,因为A和B谁在前、谁在后都算是 : AABB的范式 + // BABA -> 等同于ABAB,因为A和B谁在前、谁在后都算是 : ABAB的范式 + // BAAB -> 等同于ABBA,因为A和B谁在前、谁在后都算是 : ABBA的范式 + // 也就是说,AABB、ABAB、ABBA这三个规则,可以这么用: + // 只要有两个不同的数,都出现2次,那么这一共4个数就一定符合韵律规则。 + // 所以: + // 1) 当来到arr中的一个数字num的时候, + // 如果num已经出现了2次了, 只要之前还有一个和num不同的数, + // 也出现了两次,则一定符合了某个规则, 长度直接+4,然后清空所有的统计 + // 2) 当来到arr中的一个数字num的时候, + // 如果num已经出现了4次了(规则四), 长度直接+4,然后清空所有的统计 + // 但是如果我去掉某个规则,该贪心直接报废,比如韵律规则变成: + // AABB、ABAB、AAAA + // 因为少了ABBA, 所以上面的化简不成立了, 得重新分析新规则下的贪心策略 + // 而尝试的方法就更通用(也就是maxLen3),只是减少一个分支而已 + // 这个贪心费了很多心思,值得点赞! + public static int maxLen4(int[] arr) { + // 统计某个数(key),出现的次数(value) + HashMap map = new HashMap<>(); + // tow代表目前有多少数出现了2次 + int two = 0; + // ans代表目前符合韵律链接的子序列增长到了多长 + int ans = 0; + // 当前的num出现了几次 + int numTimes = 0; + for (int num : arr) { + // 对当前的num,做次数统计 + map.put(num, map.getOrDefault(num, 0) + 1); + // 把num出现的次数拿出来 + numTimes = map.get(num); + // 如果num刚刚出现了2次, 那么目前出现了2次的数,的数量,需要增加1个 + two += numTimes == 2 ? 1 : 0; + // 下面的if代表 : + // 如果目前有2个数出现2次了,可以连接了 + // 如果目前有1个数出现4次了,可以连接了 + if (two == 2 || numTimes == 4) { + ans += 4; + map.clear(); + two = 0; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + + // 1111 2332 4343 7799 + int[] test = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 }; + System.out.println(maxLen1(test)); + System.out.println(maxLen2(test)); + System.out.println(maxLen3(test)); + System.out.println(maxLen4(test)); + System.out.println("==========="); + + int len = 16; + int value = 10; + int[] arr = randomArray(len, value); + int[] arr1 = Arrays.copyOf(arr, arr.length); + int[] arr2 = Arrays.copyOf(arr, arr.length); + int[] arr3 = Arrays.copyOf(arr, arr.length); + int[] arr4 = Arrays.copyOf(arr, arr.length); + System.out.println(maxLen1(arr1)); + System.out.println(maxLen2(arr2)); + System.out.println(maxLen3(arr3)); + System.out.println(maxLen4(arr4)); + + System.out.println("==========="); + + long start; + long end; + int[] longArr = randomArray(4000, 20); + start = System.currentTimeMillis(); + System.out.println(maxLen3(longArr)); + end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒) : " + (end - start)); + System.out.println("==========="); + + start = System.currentTimeMillis(); + System.out.println(maxLen4(longArr)); + end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒) : " + (end - start)); + System.out.println("==========="); + + } + +} diff --git a/大厂刷题班/class41/Code03_MagicGoToAim.java b/大厂刷题班/class41/Code03_MagicGoToAim.java new file mode 100644 index 0000000..cf6c447 --- /dev/null +++ b/大厂刷题班/class41/Code03_MagicGoToAim.java @@ -0,0 +1,71 @@ +package class41; + +import java.util.PriorityQueue; + +// 来自网易互娱 +// N个结点之间,表世界存在双向通行的道路,里世界存在双向通行的传送门. +// 若走表世界的道路,花费一分钟. +// 若走里世界的传送门,不花费时间,但是接下来一分钟不能走传送门. +// 输入: T为测试用例的组数,对于每组数据: +// 第一行:N M1 M2 N代表结点的个数1到N +// 接下来M1行 每行两个数,u和v,表示表世界u和v之间存在道路 +// 接下来M2行 每行两个数,u和v,表示里世界u和v之间存在传送门 +// 现在处于1号结点,最终要到达N号结点,求最小的到达时间 保证所有输入均有效,不存在环等情况 +public class Code03_MagicGoToAim { + + // 城市编号从0开始,编号对应0~n-1 + // roads[i]是一个数组,表示i能走路达到的城市有哪些,每条路都花费1分钟 + // gates[i]是一个数组,表示i能传送达到的城市有哪些 + // 返回从0到n-1的最少用时 + public static int fast(int n, int[][] roads, int[][] gates) { + int[][] distance = new int[2][n]; + // 因为从0开始走,所以distance[0][0] = 0, distance[1][0] = 0 + // distance[0][i] -> 0 : 前一个城市到达i,是走路的方式, 最小代价,distance[0][i] + // distance[1][i] -> 1 : 前一个城市到达i,是传送的方式, 最小代价,distance[1][i] + for (int i = 1; i < n; i++) { + distance[0][i] = Integer.MAX_VALUE; + distance[1][i] = Integer.MAX_VALUE; + } + // 小根堆,根据距离排序,距离小的点,在上! + PriorityQueue heap = new PriorityQueue<>((a, b) -> a.cost - b.cost); + heap.add(new Node(0, 0, 0)); + boolean[][] visited = new boolean[2][n]; + while (!heap.isEmpty()) { + Node cur = heap.poll(); + if (visited[cur.preTransfer][cur.city]) { + continue; + } + visited[cur.preTransfer][cur.city] = true; + // 走路的方式 + for (int next : roads[cur.city]) { + if (distance[0][next] > cur.cost + 1) { + distance[0][next] = cur.cost + 1; + heap.add(new Node(0, next, distance[0][next])); + } + } + // 传送的方式 + if (cur.preTransfer == 0) { + for (int next : gates[cur.city]) { + if (distance[1][next] > cur.cost) { + distance[1][next] = cur.cost; + heap.add(new Node(1, next, distance[1][next])); + } + } + } + } + return Math.min(distance[0][n - 1], distance[1][n - 1]); + } + + public static class Node { + public int preTransfer; + public int city; + public int cost; + + public Node(int a, int b, int c) { + preTransfer = a; + city = b; + cost = c; + } + } + +} \ No newline at end of file diff --git a/大厂刷题班/class41/Problem_0031_NextPermutation.java b/大厂刷题班/class41/Problem_0031_NextPermutation.java new file mode 100644 index 0000000..5848f43 --- /dev/null +++ b/大厂刷题班/class41/Problem_0031_NextPermutation.java @@ -0,0 +1,44 @@ +package class41; + +public class Problem_0031_NextPermutation { + + public static void nextPermutation(int[] nums) { + int N = nums.length; + // 从右往左第一次降序的位置 + int firstLess = -1; + for (int i = N - 2; i >= 0; i--) { + if (nums[i] < nums[i + 1]) { + firstLess = i; + break; + } + } + if (firstLess < 0) { + reverse(nums, 0, N - 1); + } else { + int rightClosestMore = -1; + // 找最靠右的、同时比nums[firstLess]大的数,位置在哪 + // 这里其实也可以用二分优化,但是这种优化无关紧要了 + for (int i = N - 1; i > firstLess; i--) { + if (nums[i] > nums[firstLess]) { + rightClosestMore = i; + break; + } + } + swap(nums, firstLess, rightClosestMore); + reverse(nums, firstLess + 1, N - 1); + } + } + + public static void reverse(int[] nums, int L, int R) { + while (L < R) { + swap(nums, L++, R--); + } + } + + public static void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + +} diff --git a/大厂刷题班/class42/Problem_0265_PaintHouseII.java b/大厂刷题班/class42/Problem_0265_PaintHouseII.java new file mode 100644 index 0000000..a89f7d9 --- /dev/null +++ b/大厂刷题班/class42/Problem_0265_PaintHouseII.java @@ -0,0 +1,56 @@ +package class42; + +public class Problem_0265_PaintHouseII { + + // costs[i][k] i号房子用k颜色刷的花费 + // 要让0...N-1的房子相邻不同色 + // 返回最小花费 + public static int minCostII(int[][] costs) { + int N = costs.length; + if (N == 0) { + return 0; + } + int K = costs[0].length; + // 之前取得的最小代价、取得最小代价时的颜色 + int preMin1 = 0; + int preEnd1 = -1; + // 之前取得的次小代价、取得次小代价时的颜色 + int preMin2 = 0; + int preEnd2 = -1; + for (int i = 0; i < N; i++) { // i房子 + int curMin1 = Integer.MAX_VALUE; + int curEnd1 = -1; + int curMin2 = Integer.MAX_VALUE; + int curEnd2 = -1; + for (int j = 0; j < K; j++) { // j颜色! + if (j != preEnd1) { + if (preMin1 + costs[i][j] < curMin1) { + curMin2 = curMin1; + curEnd2 = curEnd1; + curMin1 = preMin1 + costs[i][j]; + curEnd1 = j; + } else if (preMin1 + costs[i][j] < curMin2) { + curMin2 = preMin1 + costs[i][j]; + curEnd2 = j; + } + } else if (j != preEnd2) { + if (preMin2 + costs[i][j] < curMin1) { + curMin2 = curMin1; + curEnd2 = curEnd1; + curMin1 = preMin2 + costs[i][j]; + curEnd1 = j; + } else if (preMin2 + costs[i][j] < curMin2) { + curMin2 = preMin2 + costs[i][j]; + curEnd2 = j; + } + } + } + preMin1 = curMin1; + preEnd1 = curEnd1; + preMin2 = curMin2; + preEnd2 = curEnd2; + } + return preMin1; + } + +} diff --git a/大厂刷题班/class42/Problem_0272_ClosestBinarySearchTreeValueII.java b/大厂刷题班/class42/Problem_0272_ClosestBinarySearchTreeValueII.java new file mode 100644 index 0000000..62d98f2 --- /dev/null +++ b/大厂刷题班/class42/Problem_0272_ClosestBinarySearchTreeValueII.java @@ -0,0 +1,109 @@ +package class42; + +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +public class Problem_0272_ClosestBinarySearchTreeValueII { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int val) { + this.val = val; + } + } + + // 这个解法来自讨论区的回答,最优解实现的很易懂且漂亮 + public static List closestKValues(TreeNode root, double target, int k) { + List ret = new LinkedList<>(); + // >=8,最近的节点,而且需要快速找后继的这么一种结构 + Stack moreTops = new Stack<>(); + // <=8,最近的节点,而且需要快速找前驱的这么一种结构 + Stack lessTops = new Stack<>(); + getMoreTops(root, target, moreTops); + getLessTops(root, target, lessTops); + if (!moreTops.isEmpty() && !lessTops.isEmpty() && moreTops.peek().val == lessTops.peek().val) { + getPredecessor(lessTops); + } + while (k-- > 0) { + if (moreTops.isEmpty()) { + ret.add(getPredecessor(lessTops)); + } else if (lessTops.isEmpty()) { + ret.add(getSuccessor(moreTops)); + } else { + double diffs = Math.abs((double) moreTops.peek().val - target); + double diffp = Math.abs((double) lessTops.peek().val - target); + if (diffs < diffp) { + ret.add(getSuccessor(moreTops)); + } else { + ret.add(getPredecessor(lessTops)); + } + } + } + return ret; + } + + // 在root为头的树上 + // 找到>=target,且最接近target的节点 + // 并且找的过程中,只要某个节点x往左走了,就把x放入moreTops里 + public static void getMoreTops(TreeNode root, double target, Stack moreTops) { + while (root != null) { + if (root.val == target) { + moreTops.push(root); + break; + } else if (root.val > target) { + moreTops.push(root); + root = root.left; + } else { + root = root.right; + } + } + } + + // 在root为头的树上 + // 找到<=target,且最接近target的节点 + // 并且找的过程中,只要某个节点x往右走了,就把x放入lessTops里 + public static void getLessTops(TreeNode root, double target, Stack lessTops) { + while (root != null) { + if (root.val == target) { + lessTops.push(root); + break; + } else if (root.val < target) { + lessTops.push(root); + root = root.right; + } else { + root = root.left; + } + } + } + + // 返回moreTops的头部的值 + // 并且调整moreTops : 为了以后能很快的找到返回节点的后继节点 + public static int getSuccessor(Stack moreTops) { + TreeNode cur = moreTops.pop(); + int ret = cur.val; + cur = cur.right; + while (cur != null) { + moreTops.push(cur); + cur = cur.left; + } + return ret; + } + + // 返回lessTops的头部的值 + // 并且调整lessTops : 为了以后能很快的找到返回节点的前驱节点 + public static int getPredecessor(Stack lessTops) { + TreeNode cur = lessTops.pop(); + int ret = cur.val; + cur = cur.left; + while (cur != null) { + lessTops.push(cur); + cur = cur.right; + } + return ret; + } + +} diff --git a/大厂刷题班/class42/Problem_0273_IntegerToEnglishWords.java b/大厂刷题班/class42/Problem_0273_IntegerToEnglishWords.java new file mode 100644 index 0000000..480cab2 --- /dev/null +++ b/大厂刷题班/class42/Problem_0273_IntegerToEnglishWords.java @@ -0,0 +1,78 @@ +package class42; + +public class Problem_0273_IntegerToEnglishWords { + + public static String num1To19(int num) { + if (num < 1 || num > 19) { + return ""; + } + String[] names = { "One ", "Two ", "Three ", "Four ", "Five ", "Six ", "Seven ", "Eight ", "Nine ", "Ten ", + "Eleven ", "Twelve ", "Thirteen ", "Fourteen ", "Fifteen ", "Sixteen ", "Seventeen", "Eighteen ", + "Nineteen " }; + return names[num - 1]; + } + + public static String num1To99(int num) { + if (num < 1 || num > 99) { + return ""; + } + if (num < 20) { + return num1To19(num); + } + int high = num / 10; + String[] tyNames = { "Twenty ", "Thirty ", "Forty ", "Fifty ", "Sixty ", "Seventy ", "Eighty ", "Ninety " }; + return tyNames[high - 2] + num1To19(num % 10); + } + + public static String num1To999(int num) { + if (num < 1 || num > 999) { + return ""; + } + if (num < 100) { + return num1To99(num); + } + int high = num / 100; + return num1To19(high) + "Hundred " + num1To99(num % 100); + } + + public static String numberToWords(int num) { + if (num == 0) { + return "Zero"; + } + String res = ""; + if (num < 0) { + res = "Negative "; + } + if (num == Integer.MIN_VALUE) { + res += "Two Billion "; + num %= -2000000000; + } + num = Math.abs(num); + int high = 1000000000; + int highIndex = 0; + String[] names = { "Billion ", "Million ", "Thousand ", "" }; + while (num != 0) { + int cur = num / high; + num %= high; + if (cur != 0) { + res += num1To999(cur); + res += names[highIndex]; + } + high /= 1000; + highIndex++; + } + return res.trim(); + } + + public static void main(String[] args) { + int test = Integer.MIN_VALUE; + System.out.println(test); + + test = -test; + System.out.println(test); + + int num = -10001; + System.out.println(numberToWords(num)); + } + +} diff --git a/大厂刷题班/class42/Problem_0296_BestMeetingPoint.java b/大厂刷题班/class42/Problem_0296_BestMeetingPoint.java new file mode 100644 index 0000000..8987edf --- /dev/null +++ b/大厂刷题班/class42/Problem_0296_BestMeetingPoint.java @@ -0,0 +1,48 @@ +package class42; + +public class Problem_0296_BestMeetingPoint { + + public static int minTotalDistance(int[][] grid) { + int N = grid.length; + int M = grid[0].length; + int[] iOnes = new int[N]; + int[] jOnes = new int[M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (grid[i][j] == 1) { + iOnes[i]++; + jOnes[j]++; + } + } + } + int total = 0; + int i = 0; + int j = N - 1; + int iRest = 0; + int jRest = 0; + while (i < j) { + if (iOnes[i] + iRest <= iOnes[j] + jRest) { + total += iOnes[i] + iRest; + iRest += iOnes[i++]; + } else { + total += iOnes[j] + jRest; + jRest += iOnes[j--]; + } + } + i = 0; + j = M - 1; + iRest = 0; + jRest = 0; + while (i < j) { + if (jOnes[i] + iRest <= jOnes[j] + jRest) { + total += jOnes[i] + iRest; + iRest += jOnes[i++]; + } else { + total += jOnes[j] + jRest; + jRest += jOnes[j--]; + } + } + return total; + } + +} diff --git a/大厂刷题班/class42/Problem_0335_SelfCrossing.java b/大厂刷题班/class42/Problem_0335_SelfCrossing.java new file mode 100644 index 0000000..cd3d8d8 --- /dev/null +++ b/大厂刷题班/class42/Problem_0335_SelfCrossing.java @@ -0,0 +1,28 @@ +package class42; + +public class Problem_0335_SelfCrossing { + + public static boolean isSelfCrossing(int[] x) { + if (x == null || x.length < 4) { + return false; + } + if ((x.length > 3 && x[2] <= x[0] && x[3] >= x[1]) + || (x.length > 4 + && ((x[3] <= x[1] && x[4] >= x[2]) || (x[3] == x[1] && x[0] + x[4] >= x[2])))) { + return true; + } + for (int i = 5; i < x.length; i++) { + if (x[i - 1] <= x[i - 3] && ((x[i] >= x[i - 2]) + || (x[i - 2] >= x[i - 4] && x[i - 5] + x[i - 1] >= x[i - 3] && x[i - 4] + x[i] >= x[i - 2]))) { + return true; + } + } + return false; + } + + public static void main(String[] args) { + int[] arr = { 2, 2, 3, 2, 2 }; + System.out.println(isSelfCrossing(arr)); + } + +} diff --git a/大厂刷题班/class43/Code01_SumNoPositiveMinCost.java b/大厂刷题班/class43/Code01_SumNoPositiveMinCost.java new file mode 100644 index 0000000..fb039a6 --- /dev/null +++ b/大厂刷题班/class43/Code01_SumNoPositiveMinCost.java @@ -0,0 +1,197 @@ +package class43; + +import java.util.Arrays; + +// 来自微软面试 +// 给定一个正数数组arr长度为n、正数x、正数y +// 你的目标是让arr整体的累加和<=0 +// 你可以对数组中的数num执行以下三种操作中的一种,且每个数最多能执行一次操作 : +// 1)不变 +// 2)可以选择让num变成0,承担x的代价 +// 3)可以选择让num变成-num,承担y的代价 +// 返回你达到目标的最小代价 +// 数据规模 : 面试时面试官没有说数据规模 +public class Code01_SumNoPositiveMinCost { + + // 动态规划 + public static int minOpStep1(int[] arr, int x, int y) { + int sum = 0; + for (int num : arr) { + sum += num; + } + return process1(arr, x, y, 0, sum); + } + + // arr[i...]自由选择,每个位置的数可以执行三种操作中的一种! + // 执行变0的操作,x操作,代价 -> x + // 执行变相反数的操作,y操作,代价 -> y + // 还剩下sum这么多累加和,需要去搞定! + // 返回搞定了sum,最低代价是多少? + public static int process1(int[] arr, int x, int y, int i, int sum) { + if (sum <= 0) { + return 0; + } + // sum > 0 没搞定 + if (i == arr.length) { + return Integer.MAX_VALUE; + } + // 第一选择,什么也不干! + int p1 = process1(arr, x, y, i + 1, sum); + // 第二选择,执行x的操作,变0 x + 后续 + int p2 = Integer.MAX_VALUE; + int next2 = process1(arr, x, y, i + 1, sum - arr[i]); + if (next2 != Integer.MAX_VALUE) { + p2 = x + next2; + } + // 第三选择,执行y的操作,变相反数 x + 后续 7 -7 -14 + int p3 = Integer.MAX_VALUE; + int next3 = process1(arr, x, y, i + 1, sum - (arr[i] << 1)); + if (next3 != Integer.MAX_VALUE) { + p3 = y + next3; + } + return Math.min(p1, Math.min(p2, p3)); + } + + // 贪心(最优解) + public static int minOpStep2(int[] arr, int x, int y) { + Arrays.sort(arr); // 小 -> 大 + int n = arr.length; + for (int l = 0, r = n - 1; l <= r; l++, r--) { + int tmp = arr[l]; + arr[l] = arr[r]; + arr[r] = tmp; + } + // arr 大 -> 小 + if (x >= y) { // 没有任何必要执行x操作 + int sum = 0; + for (int num : arr) { + sum += num; + } + int cost = 0; + for (int i = 0; i < n && sum > 0; i++) { + sum -= arr[i] << 1; + cost += y; + } + return cost; + } else { + for (int i = n - 2; i >= 0; i--) { + arr[i] += arr[i + 1]; + } + int benefit = 0; + // 注意,可以不二分,用不回退的方式! + // 执行Y操作的数,有0个的时候 + int left = mostLeft(arr, 0, benefit); + int cost = left * x; + for (int i = 0; i < n - 1; i++) { + // 0..i 这些数,都执行Y + benefit += arr[i] - arr[i + 1]; + left = mostLeft(arr, i + 1, benefit); + cost = Math.min(cost, (i + 1) * y + (left - i - 1) * x); + } + return cost; + } + } + + // arr是后缀和数组, arr[l...]中找到值<=v的最左位置 + public static int mostLeft(int[] arr, int l, int v) { + int r = arr.length - 1; + int m = 0; + int ans = arr.length; + while (l <= r) { + m = (l + r) / 2; + if (arr[m] <= v) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + // 不回退 + public static int minOpStep3(int[] arr, int x, int y) { + // 系统排序,小 -> 大 + Arrays.sort(arr); + int n = arr.length; + // 如何变成 大 -> 小 + for (int l = 0, r = n - 1; l <= r; l++, r--) { + int tmp = arr[l]; + arr[l] = arr[r]; + arr[r] = tmp; + } + if (x >= y) { + int sum = 0; + for (int num : arr) { + sum += num; + } + int cost = 0; + for (int i = 0; i < n && sum > 0; i++) { + sum -= arr[i] << 1; + cost += y; + } + return cost; + } else { + // 0个数执行Y + int benefit = 0; + // 全部的数都需要执行x,才能让累加和<=0 + int cost = arr.length * x; + int holdSum = 0; + for (int yRight = 0, holdLeft = n; yRight < holdLeft - 1; yRight++) { + benefit += arr[yRight]; + while (holdLeft - 1 > yRight && holdSum + arr[holdLeft - 1] <= benefit) { + holdSum += arr[holdLeft - 1]; + holdLeft--; + } + // 0...yRight x holdLeft.... + cost = Math.min(cost, (yRight + 1) * y + (holdLeft - yRight - 1) * x); + } + return cost; + } + } + + // 为了测试 + public static int[] randomArray(int len, int v) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int n = 12; + int v = 20; + int c = 10; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n); + int[] arr = randomArray(len, v); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int[] arr3 = copyArray(arr); + int x = (int) (Math.random() * c); + int y = (int) (Math.random() * c); + int ans1 = minOpStep1(arr1, x, y); + int ans2 = minOpStep2(arr2, x, y); + int ans3 = minOpStep3(arr3, x, y); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + + } + +} diff --git a/大厂刷题班/class43/Code02_MinCostToYeahArray.java b/大厂刷题班/class43/Code02_MinCostToYeahArray.java new file mode 100644 index 0000000..de9ccdc --- /dev/null +++ b/大厂刷题班/class43/Code02_MinCostToYeahArray.java @@ -0,0 +1,304 @@ +package class43; + +import java.util.Arrays; + +// 来自360笔试 +// 给定一个正数数组arr,长度为n,下标0~n-1 +// arr中的0、n-1位置不需要达标,它们分别是最左、最右的位置 +// 中间位置i需要达标,达标的条件是 : arr[i-1] > arr[i] 或者 arr[i+1] > arr[i]哪个都可以 +// 你每一步可以进行如下操作:对任何位置的数让其-1 +// 你的目的是让arr[1~n-2]都达标,这时arr称之为yeah!数组 +// 返回至少要多少步可以让arr变成yeah!数组 +// 数据规模 : 数组长度 <= 10000,数组中的值<=500 +public class Code02_MinCostToYeahArray { + + public static final int INVALID = Integer.MAX_VALUE; + + // 纯暴力方法,只是为了结果对 + // 时间复杂度极差 + public static int minCost0(int[] arr) { + if (arr == null || arr.length < 3) { + return 0; + } + int n = arr.length; + int min = INVALID; + for (int num : arr) { + min = Math.min(min, num); + } + int base = min - n; + return process0(arr, base, 0); + } + + public static int process0(int[] arr, int base, int index) { + if (index == arr.length) { + for (int i = 1; i < arr.length - 1; i++) { + if (arr[i - 1] <= arr[i] && arr[i] >= arr[i + 1]) { + return INVALID; + } + } + return 0; + } else { + int ans = INVALID; + int tmp = arr[index]; + for (int cost = 0; arr[index] >= base; cost++, arr[index]--) { + int next = process0(arr, base, index + 1); + if (next != INVALID) { + ans = Math.min(ans, cost + next); + } + } + arr[index] = tmp; + return ans; + } + } + + // 递归方法,已经把尝试写出 + public static int minCost1(int[] arr) { + if (arr == null || arr.length < 3) { + return 0; + } + int min = INVALID; + for (int num : arr) { + min = Math.min(min, num); + } + for (int i = 0; i < arr.length; i++) { + arr[i] += arr.length - min; + } + return process1(arr, 1, arr[0], true); + } + + // 当前来到index位置,值arr[index] + // pre : 前一个位置的值,可能减掉了一些,所以不能用arr[index-1] + // preOk : 前一个位置的值,是否被它左边的数变有效了 + // 返回 : 让arr都变有效,最小代价是什么? + public static int process1(int[] arr, int index, int pre, boolean preOk) { + if (index == arr.length - 1) { // 已经来到最后一个数了 + return preOk || pre < arr[index] ? 0 : INVALID; + } + // 当前index,不是最后一个数! + int ans = INVALID; + if (preOk) { + for (int cur = arr[index]; cur >= 0; cur--) { + int next = process1(arr, index + 1, cur, cur < pre); + if (next != INVALID) { + ans = Math.min(ans, arr[index] - cur + next); + } + } + } else { + for (int cur = arr[index]; cur > pre; cur--) { + int next = process1(arr, index + 1, cur, false); + if (next != INVALID) { + ans = Math.min(ans, arr[index] - cur + next); + } + } + } + return ans; + } + + // 初改动态规划方法,就是参考minCost1,改出来的版本 + public static int minCost2(int[] arr) { + if (arr == null || arr.length < 3) { + return 0; + } + int min = INVALID; + for (int num : arr) { + min = Math.min(min, num); + } + int n = arr.length; + for (int i = 0; i < n; i++) { + arr[i] += n - min; + } + int[][][] dp = new int[n][2][]; + for (int i = 1; i < n; i++) { + dp[i][0] = new int[arr[i - 1] + 1]; + dp[i][1] = new int[arr[i - 1] + 1]; + Arrays.fill(dp[i][0], INVALID); + Arrays.fill(dp[i][1], INVALID); + } + for (int pre = 0; pre <= arr[n - 2]; pre++) { + dp[n - 1][0][pre] = pre < arr[n - 1] ? 0 : INVALID; + dp[n - 1][1][pre] = 0; + } + for (int index = n - 2; index >= 1; index--) { + for (int pre = 0; pre <= arr[index - 1]; pre++) { + for (int cur = arr[index]; cur > pre; cur--) { + int next = dp[index + 1][0][cur]; + if (next != INVALID) { + dp[index][0][pre] = Math.min(dp[index][0][pre], arr[index] - cur + next); + } + } + for (int cur = arr[index]; cur >= 0; cur--) { + int next = dp[index + 1][cur < pre ? 1 : 0][cur]; + if (next != INVALID) { + dp[index][1][pre] = Math.min(dp[index][1][pre], arr[index] - cur + next); + } + } + } + } + return dp[1][1][arr[0]]; + } + + // minCost2动态规划 + 枚举优化 + // 改出的这个版本,需要一些技巧,但很可惜不是最优解 + // 虽然不是最优解,也足以通过100%的case了, + // 这种技法的练习,非常有意义 + public static int minCost3(int[] arr) { + if (arr == null || arr.length < 3) { + return 0; + } + int min = INVALID; + for (int num : arr) { + min = Math.min(min, num); + } + int n = arr.length; + for (int i = 0; i < n; i++) { + arr[i] += n - min; + } + int[][][] dp = new int[n][2][]; + for (int i = 1; i < n; i++) { + dp[i][0] = new int[arr[i - 1] + 1]; + dp[i][1] = new int[arr[i - 1] + 1]; + } + for (int p = 0; p <= arr[n - 2]; p++) { + dp[n - 1][0][p] = p < arr[n - 1] ? 0 : INVALID; + } + int[][] best = best(dp, n - 1, arr[n - 2]); + for (int i = n - 2; i >= 1; i--) { + for (int p = 0; p <= arr[i - 1]; p++) { + if (arr[i] < p) { + dp[i][1][p] = best[1][arr[i]]; + } else { + dp[i][1][p] = Math.min(best[0][p], p > 0 ? best[1][p - 1] : INVALID); + } + dp[i][0][p] = arr[i] <= p ? INVALID : best[0][p + 1]; + } + best = best(dp, i, arr[i - 1]); + } + return dp[1][1][arr[0]]; + } + + public static int[][] best(int[][][] dp, int i, int v) { + int[][] best = new int[2][v + 1]; + best[0][v] = dp[i][0][v]; + for (int p = v - 1; p >= 0; p--) { + best[0][p] = dp[i][0][p] == INVALID ? INVALID : v - p + dp[i][0][p]; + best[0][p] = Math.min(best[0][p], best[0][p + 1]); + } + best[1][0] = dp[i][1][0] == INVALID ? INVALID : v + dp[i][1][0]; + for (int p = 1; p <= v; p++) { + best[1][p] = dp[i][1][p] == INVALID ? INVALID : v - p + dp[i][1][p]; + best[1][p] = Math.min(best[1][p], best[1][p - 1]); + } + return best; + } + + // 最终的最优解,贪心 + // 时间复杂度O(N) + // 请注意,重点看上面的方法 + // 这个最优解容易理解,但让你学到的东西不是很多 + public static int yeah(int[] arr) { + if (arr == null || arr.length < 3) { + return 0; + } + int n = arr.length; + int[] nums = new int[n + 2]; + nums[0] = Integer.MAX_VALUE; + nums[n + 1] = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + nums[i + 1] = arr[i]; + } + int[] leftCost = new int[n + 2]; + int pre = nums[0]; + int change = 0; + for (int i = 1; i <= n; i++) { + change = Math.min(pre - 1, nums[i]); + leftCost[i] = nums[i] - change + leftCost[i - 1]; + pre = change; + } + int[] rightCost = new int[n + 2]; + pre = nums[n + 1]; + for (int i = n; i >= 1; i--) { + change = Math.min(pre - 1, nums[i]); + rightCost[i] = nums[i] - change + rightCost[i + 1]; + pre = change; + } + int ans = Integer.MAX_VALUE; + for (int i = 1; i <= n; i++) { + ans = Math.min(ans, leftCost[i] + rightCost[i + 1]); + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int v) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int len = 7; + int v = 10; + int testTime = 100; + System.out.println("=========="); + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, v); + int[] arr0 = copyArray(arr); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int[] arr3 = copyArray(arr); + int[] arr4 = copyArray(arr); + int ans0 = minCost0(arr0); + int ans1 = minCost1(arr1); + int ans2 = minCost2(arr2); + int ans3 = minCost3(arr3); + int ans4 = yeah(arr4); + if (ans0 != ans1 || ans0 != ans2 || ans0 != ans3 || ans0 != ans4) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + System.out.println("=========="); + + System.out.println("性能测试开始"); + + len = 10000; + v = 500; + System.out.println("生成随机数组长度:" + len); + System.out.println("生成随机数组值的范围:[1, " + v + "]"); + int[] arr = randomArray(len, v); + int[] arr3 = copyArray(arr); + int[] arrYeah = copyArray(arr); + long start; + long end; + start = System.currentTimeMillis(); + int ans3 = minCost3(arr3); + end = System.currentTimeMillis(); + System.out.println("minCost3方法:"); + System.out.println("运行结果: " + ans3 + ", 时间(毫秒) : " + (end - start)); + + start = System.currentTimeMillis(); + int ansYeah = yeah(arrYeah); + end = System.currentTimeMillis(); + System.out.println("yeah方法:"); + System.out.println("运行结果: " + ansYeah + ", 时间(毫秒) : " + (end - start)); + + System.out.println("性能测试结束"); + System.out.println("=========="); + + } + +} diff --git a/大厂刷题班/class44/Problem_0248_StrobogrammaticNumberIII.java b/大厂刷题班/class44/Problem_0248_StrobogrammaticNumberIII.java new file mode 100644 index 0000000..c5684db --- /dev/null +++ b/大厂刷题班/class44/Problem_0248_StrobogrammaticNumberIII.java @@ -0,0 +1,216 @@ +package class44; + +public class Problem_0248_StrobogrammaticNumberIII { + + public static int strobogrammaticInRange(String l, String h) { + char[] low = l.toCharArray(); + char[] high = h.toCharArray(); + if (!equalMore(low, high)) { + return 0; + } + int lowLen = low.length; + int highLen = high.length; + if (lowLen == highLen) { + int up1 = up(low, 0, false, 1); + int up2 = up(high, 0, false, 1); + return up1 - up2 + (valid(high) ? 1 : 0); + } + int ans = 0; + // lowLen = 3 hightLen = 7 + // 4 5 6 + for (int i = lowLen + 1; i < highLen; i++) { + ans += all(i); + } + ans += up(low, 0, false, 1); + ans += down(high, 0, false, 1); + return ans; + } + + public static boolean equalMore(char[] low, char[] cur) { + if (low.length != cur.length) { + return low.length < cur.length; + } + for (int i = 0; i < low.length; i++) { + if (low[i] != cur[i]) { + return low[i] < cur[i]; + } + } + return true; + } + + public static boolean valid(char[] str) { + int L = 0; + int R = str.length - 1; + while (L <= R) { + boolean t = L != R; + if (convert(str[L++], t) != str[R--]) { + return false; + } + } + return true; + } + + // left想得到cha字符,right配合应该做什么决定, + // 如果left怎么也得不到cha字符,返回-1;如果能得到,返回right配合应做什么决定 + // 比如,left!=right,即不是同一个位置 + // left想得到0,那么就right就需要是0 + // left想得到1,那么就right就需要是1 + // left想得到6,那么就right就需要是9 + // left想得到8,那么就right就需要是8 + // left想得到9,那么就right就需要是6 + // 除此了这些之外,left不能得到别的了。 + // 比如,left==right,即是同一个位置 + // left想得到0,那么就right就需要是0 + // left想得到1,那么就right就需要是1 + // left想得到8,那么就right就需要是8 + // 除此了这些之外,left不能得到别的了,比如: + // left想得到6,那么就right就需要是9,而left和right是一个位置啊,怎么可能即6又9,返回-1 + // left想得到9,那么就right就需要是6,而left和right是一个位置啊,怎么可能即9又6,返回-1 + public static int convert(char cha, boolean diff) { + switch (cha) { + case '0': + return '0'; + case '1': + return '1'; + case '6': + return diff ? '9' : -1; + case '8': + return '8'; + case '9': + return diff ? '6' : -1; + default: + return -1; + } + } + + // low [左边已经做完决定了 left.....right 右边已经做完决定了] + // 左边已经做完决定的部分,如果大于low的原始,leftMore = true; + // 左边已经做完决定的部分,如果不大于low的原始,那一定是相等,不可能小于,leftMore = false; + // 右边已经做完决定的部分,如果小于low的原始,rightLessEqualMore = 0; + // 右边已经做完决定的部分,如果等于low的原始,rightLessEqualMore = 1; + // 右边已经做完决定的部分,如果大于low的原始,rightLessEqualMore = 2; + // rightLessEqualMore < = > + // 0 1 2 + // 返回 :没做决定的部分,随意变,几个有效的情况?返回! + public static int up(char[] low, int left, boolean leftMore, int rightLessEqualMore) { + int N = low.length; + int right = N - 1 - left; + if (left > right) { // 都做完决定了! + // 如果左边做完决定的部分大于原始 或者 如果左边做完决定的部分等于原始&左边做完决定的部分不小于原始 + // 有效! + // 否则,无效! + return leftMore || (!leftMore && rightLessEqualMore != 0) ? 1 : 0; + } + // 如果上面没有return,说明决定没做完,就继续做 + if (leftMore) { // 如果左边做完决定的部分大于原始 + return num(N - (left << 1)); + } else { // 如果左边做完决定的部分等于原始 + int ways = 0; + // 当前left做的决定,大于原始的left + for (char cha = (char) (low[left] + 1); cha <= '9'; cha++) { + if (convert(cha, left != right) != -1) { + ways += up(low, left + 1, true, rightLessEqualMore); + } + } + // 当前left做的决定,等于原始的left + int convert = convert(low[left], left != right); + if (convert != -1) { + if (convert < low[right]) { + ways += up(low, left + 1, false, 0); + } else if (convert == low[right]) { + ways += up(low, left + 1, false, rightLessEqualMore); + } else { + ways += up(low, left + 1, false, 2); + } + } + return ways; + } + } + + // ll < = + // rs < = > + public static int down(char[] high, int left, boolean ll, int rs) { + int N = high.length; + int right = N - 1 - left; + if (left > right) { + return ll || (!ll && rs != 2) ? 1 : 0; + } + if (ll) { + return num(N - (left << 1)); + } else { + int ways = 0; + for (char cha = (N != 1 && left == 0) ? '1' : '0'; cha < high[left]; cha++) { + if (convert(cha, left != right) != -1) { + ways += down(high, left + 1, true, rs); + } + } + int convert = convert(high[left], left != right); + if (convert != -1) { + if (convert < high[right]) { + ways += down(high, left + 1, false, 0); + } else if (convert == high[right]) { + ways += down(high, left + 1, false, rs); + } else { + ways += down(high, left + 1, false, 2); + } + } + return ways; + } + } + + public static int num(int bits) { + if (bits == 1) { + return 3; + } + if (bits == 2) { + return 5; + } + int p2 = 3; + int p1 = 5; + int ans = 0; + for (int i = 3; i <= bits; i++) { + ans = 5 * p2; + p2 = p1; + p1 = ans; + } + return ans; + } + + // 如果是最开始 : + // Y X X X Y + // -> 1 X X X 1 + // -> 8 X X X 8 + // -> 9 X X X 6 + // -> 6 X X X 9 + // 如果不是最开始 : + // Y X X X Y + // -> 0 X X X 0 + // -> 1 X X X 1 + // -> 8 X X X 8 + // -> 9 X X X 6 + // -> 6 X X X 9 + // 所有的len位数,有几个有效的? + public static int all(int len) { + int ans = (len & 1) == 0 ? 1 : 3; + for (int i = (len & 1) == 0 ? 2 : 3; i < len; i += 2) { + ans *= 5; + } + return ans << 2; + } + + // 我们课上讲的 + public static int all(int len, boolean init) { + if (len == 0) { // init == true,不可能调用all(0) + return 1; + } + if (len == 1) { + return 3; + } + if (init) { + return all(len - 2, false) << 2; + } else { + return all(len - 2, false) * 5; + } + } + +} diff --git a/大厂刷题班/class44/Problem_0317_ShortestDistanceFromAllBuildings.java b/大厂刷题班/class44/Problem_0317_ShortestDistanceFromAllBuildings.java new file mode 100644 index 0000000..6a6648b --- /dev/null +++ b/大厂刷题班/class44/Problem_0317_ShortestDistanceFromAllBuildings.java @@ -0,0 +1,237 @@ +package class44; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; + +public class Problem_0317_ShortestDistanceFromAllBuildings { + + // 如果grid中0比较少,用这个方法比较好 + public static int shortestDistance1(int[][] grid) { + int ans = Integer.MAX_VALUE; + int N = grid.length; + int M = grid[0].length; + int buildings = 0; + Position[][] positions = new Position[N][M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (grid[i][j] == 1) { + buildings++; + } + positions[i][j] = new Position(i, j, grid[i][j]); + } + } + if (buildings == 0) { + return 0; + } + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + ans = Math.min(ans, bfs(positions, buildings, i, j)); + } + } + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + public static int bfs(Position[][] positions, int buildings, int i, int j) { + if (positions[i][j].v != 0) { + return Integer.MAX_VALUE; + } + HashMap levels = new HashMap<>(); + Queue queue = new LinkedList<>(); + Position from = positions[i][j]; + levels.put(from, 0); + queue.add(from); + int ans = 0; + int solved = 0; + while (!queue.isEmpty() && solved != buildings) { + Position cur = queue.poll(); + int level = levels.get(cur); + if (cur.v == 1) { + ans += level; + solved++; + } else { + add(queue, levels, positions, cur.r - 1, cur.c, level + 1); + add(queue, levels, positions, cur.r + 1, cur.c, level + 1); + add(queue, levels, positions, cur.r, cur.c - 1, level + 1); + add(queue, levels, positions, cur.r, cur.c + 1, level + 1); + } + } + return solved == buildings ? ans : Integer.MAX_VALUE; + } + + public static class Position { + public int r; + public int c; + public int v; + + public Position(int row, int col, int value) { + r = row; + c = col; + v = value; + } + } + + public static void add(Queue q, HashMap l, Position[][] p, int i, int j, int level) { + if (i >= 0 && i < p.length && j >= 0 && j < p[0].length && p[i][j].v != 2 && !l.containsKey(p[i][j])) { + l.put(p[i][j], level); + q.add(p[i][j]); + } + } + + // 如果grid中1比较少,用这个方法比较好 + public static int shortestDistance2(int[][] grid) { + int N = grid.length; + int M = grid[0].length; + int ones = 0; + int zeros = 0; + Info[][] infos = new Info[N][M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (grid[i][j] == 1) { + infos[i][j] = new Info(i, j, 1, ones++); + } else if (grid[i][j] == 0) { + infos[i][j] = new Info(i, j, 0, zeros++); + } else { + infos[i][j] = new Info(i, j, 2, Integer.MAX_VALUE); + } + } + } + if (ones == 0) { + return 0; + } + int[][] distance = new int[ones][zeros]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (infos[i][j].v == 1) { + bfs(infos, i, j, distance); + } + } + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < zeros; i++) { + int sum = 0; + for (int j = 0; j < ones; j++) { + if (distance[j][i] == 0) { + sum = Integer.MAX_VALUE; + break; + } else { + sum += distance[j][i]; + } + } + ans = Math.min(ans, sum); + } + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + public static class Info { + public int r; + public int c; + public int v; + public int t; + + public Info(int row, int col, int value, int th) { + r = row; + c = col; + v = value; + t = th; + } + } + + public static void bfs(Info[][] infos, int i, int j, int[][] distance) { + HashMap levels = new HashMap<>(); + Queue queue = new LinkedList<>(); + Info from = infos[i][j]; + add(queue, levels, infos, from.r - 1, from.c, 1); + add(queue, levels, infos, from.r + 1, from.c, 1); + add(queue, levels, infos, from.r, from.c - 1, 1); + add(queue, levels, infos, from.r, from.c + 1, 1); + while (!queue.isEmpty()) { + Info cur = queue.poll(); + int level = levels.get(cur); + distance[from.t][cur.t] = level; + add(queue, levels, infos, cur.r - 1, cur.c, level + 1); + add(queue, levels, infos, cur.r + 1, cur.c, level + 1); + add(queue, levels, infos, cur.r, cur.c - 1, level + 1); + add(queue, levels, infos, cur.r, cur.c + 1, level + 1); + } + } + + public static void add(Queue q, HashMap l, Info[][] infos, int i, int j, int level) { + if (i >= 0 && i < infos.length && j >= 0 && j < infos[0].length && infos[i][j].v == 0 + && !l.containsKey(infos[i][j])) { + l.put(infos[i][j], level); + q.add(infos[i][j]); + } + } + + // 方法三的大流程和方法二完全一样,从每一个1出发,而不从0出发 + // 运行时间快主要是因为常数优化,以下是优化点: + // 1) 宽度优先遍历时,一次解决一层,不是一个一个遍历: + // int size = que.size(); + // level++; + // for (int k = 0; k < size; k++) { ... } + // 2) pass的值每次减1何用?只有之前所有的1都到达的0,才有必要继续尝试的意思 + // 也就是说,如果某个1,自我封闭,之前的1根本到不了现在这个1附近的0,就没必要继续尝试了 + // if (nextr >= 0 && nextr < grid.length + // && nextc >= 0 && nextc < grid[0].length + // && grid[nextr][nextc] == pass) + // 3) int[] trans = { 0, 1, 0, -1, 0 }; 的作用是迅速算出上、下、左、右 + // 4) 如果某个1在计算时,它周围已经没有pass值了,可以提前宣告1之间是不连通的 + // step = bfs(grid, dist, i, j, pass--, trans); + // if (step == Integer.MAX_VALUE) { + // return -1; + // } + // 5) 最要的优化,每个1到某个0的距离是逐渐叠加的,每个1给所有的0叠一次(宽度优先遍历) + // dist[nextr][nextc] += level; + public static int shortestDistance3(int[][] grid) { + int[][] dist = new int[grid.length][grid[0].length]; + int pass = 0; + int step = Integer.MAX_VALUE; + int[] trans = { 0, 1, 0, -1, 0 }; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == 1) { + step = bfs(grid, dist, i, j, pass--, trans); + if (step == Integer.MAX_VALUE) { + return -1; + } + } + } + } + return step == Integer.MAX_VALUE ? -1 : step; + } + + // 原始矩阵是grid,但是所有的路(0),被改了 + // 改成了啥?改成认为,pass才是路!原始矩阵中的1和2呢?不变! + // dist,距离压缩表,之前的bfs,也就是之前每个1,走到某个0,总距离和都在dist里 + // row,col 宽度优先遍历的,出发点! + // trans -> 炫技的,上下左右 + // 返回值代表,进行完这一遍bfs,压缩距离表中(dist),最小值是谁? + // 如果突然发现,无法联通!返回系统最大! + public static int bfs(int[][] grid, int[][] dist, int row, int col, int pass, int[] trans) { + Queue que = new LinkedList(); + que.offer(new int[] { row, col }); + int level = 0; + int ans = Integer.MAX_VALUE; + while (!que.isEmpty()) { + int size = que.size(); + level++; + for (int k = 0; k < size; k++) { + int[] node = que.poll(); + for (int i = 1; i < trans.length; i++) { // 上下左右 + int nextr = node[0] + trans[i - 1]; + int nextc = node[1] + trans[i]; + if (nextr >= 0 && nextr < grid.length && nextc >= 0 && nextc < grid[0].length + && grid[nextr][nextc] == pass) { + que.offer(new int[] { nextr, nextc }); + dist[nextr][nextc] += level; + ans = Math.min(ans, dist[nextr][nextc]); + grid[nextr][nextc]--; + } + } + } + } + return ans; + } + +} diff --git a/大厂刷题班/class44/Problem_0992_SubarraysWithKDifferentIntegers.java b/大厂刷题班/class44/Problem_0992_SubarraysWithKDifferentIntegers.java new file mode 100644 index 0000000..97c111c --- /dev/null +++ b/大厂刷题班/class44/Problem_0992_SubarraysWithKDifferentIntegers.java @@ -0,0 +1,71 @@ +package class44; + +import java.util.HashMap; + +public class Problem_0992_SubarraysWithKDifferentIntegers { + + // nums 数组,题目规定,nums中的数字,不会超过nums的长度 + // [ ]长度为5,0~5 + public static int subarraysWithKDistinct1(int[] nums, int k) { + int n = nums.length; + // k-1种数的窗口词频统计 + int[] lessCounts = new int[n + 1]; + // k种数的窗口词频统计 + int[] equalCounts = new int[n + 1]; + int lessLeft = 0; + int equalLeft = 0; + int lessKinds = 0; + int equalKinds = 0; + int ans = 0; + for (int r = 0; r < n; r++) { + // 当前刚来到r位置! + if (lessCounts[nums[r]] == 0) { + lessKinds++; + } + if (equalCounts[nums[r]] == 0) { + equalKinds++; + } + lessCounts[nums[r]]++; + equalCounts[nums[r]]++; + while (lessKinds == k) { + if (lessCounts[nums[lessLeft]] == 1) { + lessKinds--; + } + lessCounts[nums[lessLeft++]]--; + } + while (equalKinds > k) { + if (equalCounts[nums[equalLeft]] == 1) { + equalKinds--; + } + equalCounts[nums[equalLeft++]]--; + } + ans += lessLeft - equalLeft; + } + return ans; + } + + public static int subarraysWithKDistinct2(int[] arr, int k) { + return numsMostK(arr, k) - numsMostK(arr, k - 1); + } + + public static int numsMostK(int[] arr, int k) { + int i = 0, res = 0; + HashMap count = new HashMap<>(); + for (int j = 0; j < arr.length; ++j) { + if (count.getOrDefault(arr[j], 0) == 0) { + k--; + } + count.put(arr[j], count.getOrDefault(arr[j], 0) + 1); + while (k < 0) { + count.put(arr[i], count.get(arr[i]) - 1); + if (count.get(arr[i]) == 0) { + k++; + } + i++; + } + res += j - i + 1; + } + return res; + } + +} diff --git a/大厂刷题班/class45/Code01_SplitBuildingBlock.java b/大厂刷题班/class45/Code01_SplitBuildingBlock.java new file mode 100644 index 0000000..b63b36c --- /dev/null +++ b/大厂刷题班/class45/Code01_SplitBuildingBlock.java @@ -0,0 +1,87 @@ +package class45; + +import java.util.Arrays; + +// 来自京东笔试 +// 小明手中有n块积木,并且小明知道每块积木的重量。现在小明希望将这些积木堆起来 +// 要求是任意一块积木如果想堆在另一块积木上面,那么要求: +// 1) 上面的积木重量不能小于下面的积木重量 +// 2) 上面积木的重量减去下面积木的重量不能超过x +// 3) 每堆中最下面的积木没有重量要求 +// 现在小明有一个机会,除了这n块积木,还可以获得k块任意重量的积木。 +// 小明希望将积木堆在一起,同时希望积木堆的数量越少越好,你能帮他找到最好的方案么? +// 输入描述: +// 第一行三个整数n,k,x,1<=n<=200000,0<=x,k<=1000000000 +// 第二行n个整数,表示积木的重量,任意整数范围都在[1,1000000000] +// 样例输出: +// 13 1 38 +// 20 20 80 70 70 70 420 5 1 5 1 60 90 +// 1 1 5 5 20 20 60 70 70 70 80 90 420 -> 只有1块魔法积木,x = 38 +// 输出:2 +// 解释: +// 两堆分别是 +// 1 1 5 5 20 20 (50) 60 70 70 70 80 90 +// 420 +// 其中x是一个任意重量的积木,夹在20和60之间可以让积木继续往上搭 +public class Code01_SplitBuildingBlock { + + // 这是启发解 + // arr是从小到大排序的,x是限制,固定参数 + // 当前来到i位置,积木重量arr[i] + // 潜台词 : 当前i位置的积木在一个堆里,堆的开头在哪?之前已经决定了 + // i i+1 该在一起 or 该用魔法积木弥合 or 该分家 + // 返回值:arr[i....]最少能分几个堆? + public static int zuo(int[] arr, int x, int i, int r) { + if (i == arr.length - 1) { + return 1; + } + // i没到最后一个数 + if (arr[i + 1] - arr[i] <= x) { // 一定贴在一起 + return zuo(arr, x, i + 1, r); + } else { // 弥合!分家 + // 分家 + int p1 = 1 + zuo(arr, x, i + 1, r); + // 弥合 + int p2 = Integer.MAX_VALUE; + int need = (arr[i + 1] - arr[i] - 1) / x; + if (r >= need) { + p2 = zuo(arr, x, i + 1, r - need); + } + return Math.min(p1, p2); + } + } + + // 这是最优解 + // arr里装着所有积木的重量 + // k是魔法积木的数量,每一块魔法积木都能变成任何重量 + // x差值,后 - 前 <= x + public static int minSplit(int[] arr, int k, int x) { + Arrays.sort(arr); + int n = arr.length; + int[] needs = new int[n]; + int size = 0; + int splits = 1; + for (int i = 1; i < n; i++) { + if (arr[i] - arr[i - 1] > x) { + needs[size++] = arr[i] - arr[i - 1]; + splits++; + } + } + if (splits == 1 || x == 0 || k == 0) { + return splits; + } + // 试图去利用魔法积木,弥合堆! + Arrays.sort(needs, 0, size); + for (int i = 0; i < size; i++) { + int need = (needs[i] - 1) / x; + if (k >= need) { + splits--; + k -= need; + } else { + break; + } + } + return splits; + } + +} diff --git a/大厂刷题班/class45/Problem_0291_WordPatternII.java b/大厂刷题班/class45/Problem_0291_WordPatternII.java new file mode 100644 index 0000000..a961a6d --- /dev/null +++ b/大厂刷题班/class45/Problem_0291_WordPatternII.java @@ -0,0 +1,62 @@ +package class45; + +import java.util.HashSet; + +public class Problem_0291_WordPatternII { + + public static boolean wordPatternMatch(String pattern, String str) { + return match(str, pattern, 0, 0, new String[26], new HashSet<>()); + } + + // 题目有限制,str和pattern其中的字符,一定是a~z小写 + // p[a] -> "abc" + // p[b] -> "fbf" + // 需要指代的表最多26长度 + // String[] map -> new String[26] + // p[a] -> "abc" map[0] -> "abc" + // p[b] -> "fbf" map[1] -> "fbf"; + // p[z] -> "kfk" map[25] -> "kfk" + // HashSet set -> map中指代了哪些字符串 + // str[si.......] 是不是符合 p[pi......]?符合返回true,不符合返回false + // 之前的决定!由map和set,告诉我!不能冲突! + public static boolean match(String s, String p, int si, int pi, String[] map, HashSet set) { + if (pi == p.length() && si == s.length()) { + return true; + } + // str和pattern,并没有都结束! + if (pi == p.length() || si == s.length()) { + return false; + } + // str和pattern,都没结束! + + char ch = p.charAt(pi); + String cur = map[ch - 'a']; + if (cur != null) { // 当前p[pi]已经指定过了! + return si + cur.length() <= s.length() // 不能越界! + && cur.equals(s.substring(si, si + cur.length())) + && match(s, p, si + cur.length(), pi + 1, map, set); + } + // p[pi]没指定! + int end = s.length(); + // 剪枝!重要的剪枝! + for (int i = p.length() - 1; i > pi; i--) { + end -= map[p.charAt(i) - 'a'] == null ? 1 : map[p.charAt(i) - 'a'].length(); + } + for (int i = si; i < end; i++) { + // 从si出发的所有前缀串,全试 + cur = s.substring(si, i + 1); + // 但是,只有这个前缀串,之前没占过别的坑!才能去尝试 + if (!set.contains(cur)) { + set.add(cur); + map[ch - 'a'] = cur; + if (match(s, p, i + 1, pi + 1, map, set)) { + return true; + } + map[ch - 'a'] = null; + set.remove(cur); + } + } + return false; + } + +} diff --git a/大厂刷题班/class45/Problem_0403_FrogJump.java b/大厂刷题班/class45/Problem_0403_FrogJump.java new file mode 100644 index 0000000..08540e5 --- /dev/null +++ b/大厂刷题班/class45/Problem_0403_FrogJump.java @@ -0,0 +1,40 @@ +package class45; + +import java.util.HashMap; +import java.util.HashSet; + +public class Problem_0403_FrogJump { + + public static boolean canCross(int[] stones) { + HashSet set = new HashSet<>(); + for (int num : stones) { + set.add(num); + } + HashMap> dp = new HashMap<>(); + return jump(1, 1, stones[stones.length - 1], set, dp); + } + + public static boolean jump(int cur, int pre, int end, HashSet set, + HashMap> dp) { + if (cur == end) { + return true; + } + if (!set.contains(cur)) { + return false; + } + if (dp.containsKey(cur) && dp.get(cur).containsKey(pre)) { + return dp.get(cur).get(pre); + } + boolean ans = (pre > 1 && jump(cur + pre - 1, pre - 1, end, set, dp)) + || jump(cur + pre, pre, end, set, dp) + || jump(cur + pre + 1, pre + 1, end, set, dp); + if (!dp.containsKey(cur)) { + dp.put(cur, new HashMap<>()); + } + if (!dp.get(cur).containsKey(pre)) { + dp.get(cur).put(pre, ans); + } + return ans; + } + +} diff --git a/大厂刷题班/class45/Problem_2035_PartitionArrayIntoTwoArraysToMinimizeSumDifference.java b/大厂刷题班/class45/Problem_2035_PartitionArrayIntoTwoArraysToMinimizeSumDifference.java new file mode 100644 index 0000000..3592e53 --- /dev/null +++ b/大厂刷题班/class45/Problem_2035_PartitionArrayIntoTwoArraysToMinimizeSumDifference.java @@ -0,0 +1,58 @@ +package class45; + +import java.util.HashMap; +import java.util.TreeSet; + +public class Problem_2035_PartitionArrayIntoTwoArraysToMinimizeSumDifference { + + public static int minimumDifference(int[] arr) { + int size = arr.length; + int half = size >> 1; + HashMap> lmap = new HashMap<>(); + process(arr, 0, half, 0, 0, lmap); + HashMap> rmap = new HashMap<>(); + process(arr, half, size, 0, 0, rmap); + int sum = 0; + for (int num : arr) { + sum += num; + } + int ans = Integer.MAX_VALUE; + for (int leftNum : lmap.keySet()) { + for (int leftSum : lmap.get(leftNum)) { + Integer rightSum = rmap.get(half - leftNum).floor((sum >> 1) - leftSum); + if (rightSum != null) { + int pickSum = leftSum + rightSum; + int restSum = sum - pickSum; + ans = Math.min(ans, restSum - pickSum); + } + } + } + return ans; + } + + + // arr -> 8 0 1 2 3 4 5 6 7 + // process(arr, 0, 4) [0,4) + // process(arr, 4, 8) [4,8) + // arr[index....end-1]这个范围上,去做选择 + // pick挑了几个数! + // sum挑的这些数,累加和是多少! + // map记录结果 + // HashMap> map + // key -> 挑了几个数,比如挑了3个数,但是形成累加和可能多个! + // value -> 有序表,都记下来! + // 整个过程,纯暴力!2^15 -> 3万多,纯暴力跑完,依然很快! + public static void process(int[] arr, int index, int end, int pick, int sum, + HashMap> map) { + if (index == end) { + if (!map.containsKey(pick)) { + map.put(pick, new TreeSet<>()); + } + map.get(pick).add(sum); + } else { + process(arr, index + 1, end, pick, sum, map); + process(arr, index + 1, end, pick + 1, sum + arr[index], map); + } + } + +} diff --git a/大厂刷题班/class46/Problem_0363_MaxSumOfRectangleNoLargerThanK.java b/大厂刷题班/class46/Problem_0363_MaxSumOfRectangleNoLargerThanK.java new file mode 100644 index 0000000..2694c13 --- /dev/null +++ b/大厂刷题班/class46/Problem_0363_MaxSumOfRectangleNoLargerThanK.java @@ -0,0 +1,77 @@ +package class46; + +import java.util.TreeSet; + +public class Problem_0363_MaxSumOfRectangleNoLargerThanK { + + public static int nearK(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return Integer.MIN_VALUE; + } + TreeSet set = new TreeSet<>(); + set.add(0); + int ans = Integer.MIN_VALUE; + int sum = 0; + for (int i = 0; i < arr.length; i++) { + // 讨论子数组必须以i位置结尾,最接近k的累加和是多少? + sum += arr[i]; + // 找之前哪个前缀和 >= sum - k 且最接近 + // 有序表中,ceiling(x) 返回>=x且最接近的! + // 有序表中,floor(x) 返回<=x且最接近的! + Integer find = set.ceiling(sum - k); + if(find != null) { + int curAns = sum - find; + ans = Math.max(ans, curAns); + } + set.add(sum); + } + return ans; + } + + public static int maxSumSubmatrix(int[][] matrix, int k) { + if (matrix == null || matrix[0] == null) { + return 0; + } + if (matrix.length > matrix[0].length) { + matrix = rotate(matrix); + } + int row = matrix.length; + int col = matrix[0].length; + int res = Integer.MIN_VALUE; + TreeSet sumSet = new TreeSet<>(); + for (int s = 0; s < row; s++) { + int[] colSum = new int[col]; + for (int e = s; e < row; e++) { + // s ~ e 这些行 选的子矩阵必须包含、且只包含s行~e行的数据 + // 0 ~ 0 0 ~ 1 0 ~ 2 。。。 + // 1 ~ 2 1 ~ 2 1 ~ 3 。。。 + sumSet.add(0); + int rowSum = 0; + for (int c = 0; c < col; c++) { + colSum[c] += matrix[e][c]; + rowSum += colSum[c]; + Integer it = sumSet.ceiling(rowSum - k); + if (it != null) { + res = Math.max(res, rowSum - it); + } + sumSet.add(rowSum); + } + sumSet.clear(); + } + } + return res; + } + + public static int[][] rotate(int[][] matrix) { + int N = matrix.length; + int M = matrix[0].length; + int[][] r = new int[M][N]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + r[j][i] = matrix[i][j]; + } + } + return r; + } + +} diff --git a/大厂刷题班/class46/Problem_0391_PerfectRectangle.java b/大厂刷题班/class46/Problem_0391_PerfectRectangle.java new file mode 100644 index 0000000..b041a47 --- /dev/null +++ b/大厂刷题班/class46/Problem_0391_PerfectRectangle.java @@ -0,0 +1,59 @@ +package class46; + +import java.util.HashMap; + +public class Problem_0391_PerfectRectangle { + + public static boolean isRectangleCover(int[][] matrix) { + if (matrix.length == 0 || matrix[0].length == 0) { + return false; + } + int l = Integer.MAX_VALUE; + int r = Integer.MIN_VALUE; + int d = Integer.MAX_VALUE; + int u = Integer.MIN_VALUE; + HashMap> map = new HashMap<>(); + int area = 0; + for (int[] rect : matrix) { + add(map, rect[0], rect[1]); + add(map, rect[0], rect[3]); + add(map, rect[2], rect[1]); + add(map, rect[2], rect[3]); + area += (rect[2] - rect[0]) * (rect[3] - rect[1]); + l = Math.min(rect[0], l); + d = Math.min(rect[1], d); + r = Math.max(rect[2], r); + u = Math.max(rect[3], u); + } + return checkPoints(map, l, d, r, u) && area == (r - l) * (u - d); + } + + public static void add(HashMap> map, int row, int col) { + if (!map.containsKey(row)) { + map.put(row, new HashMap<>()); + } + map.get(row).put(col, map.get(row).getOrDefault(col, 0) + 1); + } + + public static boolean checkPoints(HashMap> map, int l, int d, int r, int u) { + if (map.get(l).getOrDefault(d, 0) != 1 + || map.get(l).getOrDefault(u, 0) != 1 + || map.get(r).getOrDefault(d, 0) != 1 + || map.get(r).getOrDefault(u, 0) != 1) { + return false; + } + map.get(l).remove(d); + map.get(l).remove(u); + map.get(r).remove(d); + map.get(r).remove(u); + for (int key : map.keySet()) { + for (int value : map.get(key).values()) { + if ((value & 1) != 0) { + return false; + } + } + } + return true; + } + +} diff --git a/大厂刷题班/class46/Problem_0411_MinimumUniqueWordAbbreviation.java b/大厂刷题班/class46/Problem_0411_MinimumUniqueWordAbbreviation.java new file mode 100644 index 0000000..29d840f --- /dev/null +++ b/大厂刷题班/class46/Problem_0411_MinimumUniqueWordAbbreviation.java @@ -0,0 +1,174 @@ +package class46; + +public class Problem_0411_MinimumUniqueWordAbbreviation { + + // 区分出来之后,缩写的长度,最短是多少? + public static int min = Integer.MAX_VALUE; + + // 取得缩写的长度最短的时候,决定是什么(fix) + public static int best = 0; + + public static int abbrLen(int fix, int len) { + int ans = 0; + int cnt = 0; + for (int i = 0; i < len; i++) { + if ((fix & (1 << i)) != 0) { + ans++; + if (cnt != 0) { + ans += (cnt > 9 ? 2 : 1) - cnt; + } + cnt = 0; + } else { + cnt++; + } + } + if (cnt != 0) { + ans += (cnt > 9 ? 2 : 1) - cnt; + } + return ans; + } + + // 原始的字典,被改了 + // target : abc 字典中的词 : bbb -> 101 -> int -> + // fix -> int -> 根本不用值,用状态 -> 每一位保留还是不保留的决定 + public static boolean canFix(int[] words, int fix) { + for (int word : words) { + if ((fix & word) == 0) { + return false; + } + } + return true; + } + + // 利用位运算加速 + public static String minAbbreviation1(String target, String[] dictionary) { + min = Integer.MAX_VALUE; + best = 0; + char[] t = target.toCharArray(); + int len = t.length; + int siz = 0; + for (String word : dictionary) { + if (word.length() == len) { + siz++; + } + } + int[] words = new int[siz]; + int index = 0; + for (String word : dictionary) { + if (word.length() == len) { + char[] w = word.toCharArray(); + int status = 0; + for (int j = 0; j < len; j++) { + if (t[j] != w[j]) { + status |= 1 << j; + } + } + words[index++] = status; + } + } + dfs1(words, len, 0, 0); + StringBuilder builder = new StringBuilder(); + int count = 0; + for (int i = 0; i < len; i++) { + if ((best & (1 << i)) != 0) { + if (count > 0) { + builder.append(count); + } + builder.append(t[i]); + count = 0; + } else { + count++; + } + } + if (count > 0) { + builder.append(count); + } + return builder.toString(); + } + + // 所有字典中的单词现在都变成了int,放在words里 + // 0....len-1 位去决定保留还是不保留!当前来到index位 + // 之前做出的决定! + public static void dfs1(int[] words, int len, int fix, int index) { + if (!canFix(words, fix)) { + if (index < len) { + dfs1(words, len, fix, index + 1); + dfs1(words, len, fix | (1 << index), index + 1); + } + } else { + // 决定是fix,一共的长度是len,求出缩写是多长? + int ans = abbrLen(fix, len); + if (ans < min) { + min = ans; + best = fix; + } + } + } + + // 进一步设计剪枝,注意diff的用法 + public static String minAbbreviation2(String target, String[] dictionary) { + min = Integer.MAX_VALUE; + best = 0; + char[] t = target.toCharArray(); + int len = t.length; + int siz = 0; + for (String word : dictionary) { + if (word.length() == len) { + siz++; + } + } + int[] words = new int[siz]; + int index = 0; + // 用来剪枝 + int diff = 0; + for (String word : dictionary) { + if (word.length() == len) { + char[] w = word.toCharArray(); + int status = 0; + for (int j = 0; j < len; j++) { + if (t[j] != w[j]) { + status |= 1 << j; + } + } + words[index++] = status; + diff |= status; + } + } + dfs2(words, len, diff, 0, 0); + StringBuilder builder = new StringBuilder(); + int count = 0; + for (int i = 0; i < len; i++) { + if ((best & (1 << i)) != 0) { + if (count > 0) { + builder.append(count); + } + builder.append(t[i]); + count = 0; + } else { + count++; + } + } + if (count > 0) { + builder.append(count); + } + return builder.toString(); + } + + public static void dfs2(int[] words, int len, int diff, int fix, int index) { + if (!canFix(words, fix)) { + if (index < len) { + dfs2(words, len, diff, fix, index + 1); + if ((diff & (1 << index)) != 0) { + dfs2(words, len, diff, fix | (1 << index), index + 1); + } + } + } else { + int ans = abbrLen(fix, len); + if (ans < min) { + min = ans; + best = fix; + } + } + } + +} diff --git a/大厂刷题班/class46/Problem_0425_WordSquares.java b/大厂刷题班/class46/Problem_0425_WordSquares.java new file mode 100644 index 0000000..24f2418 --- /dev/null +++ b/大厂刷题班/class46/Problem_0425_WordSquares.java @@ -0,0 +1,87 @@ +package class46; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +// 注意!课上介绍题目设定的时候,有一点点小错 +// 题目描述如下: +// 给定n个字符串,并且每个字符串长度一定是n,请组成单词方阵,比如: +// 给定4个字符串,长度都是4,["ball","area","lead","lady"] +// 可以组成如下的方阵: +// b a l l +// a r e a +// l e a d +// l a d y +// 什么叫单词方阵?如上的方阵可以看到, +// 第1行和第1列都是"ball",第2行和第2列都是"area",第3行和第3列都是"lead",第4行和第4列都是"lady" +// 所以如果有N个单词,单词方阵是指: +// 一个N*N的二维矩阵,并且i行和i列都是某个单词,不要求全部N个单词都在这个方阵里。 +// 请返回所有可能的单词方阵。 +// 示例: +// 输入: words = ["abat","baba","atan","atal"] +// 输出: [["baba","abat","baba","atal"],["baba","abat","baba","atan"]] +// 解释: +// 可以看到输出里,有两个链表,代表两个单词方阵 +// 第一个如下: +// b a b a +// a b a t +// b a b a +// a t a l +// 这个方阵里没有atan,因为不要求全部单词都在方阵里 +// 第二个如下: +// b a b a +// a b a t +// b a b a +// a t a n +// 这个方阵里没有atal,因为不要求全部单词都在方阵里 +// 课上说的是:一个N*N的二维矩阵,并且i行和i列都是某个单词,要求全部N个单词都在这个方阵里 +// 原题说的是:一个N*N的二维矩阵,并且i行和i列都是某个单词,不要求全部N个单词都在这个方阵里 +// 讲的过程没错,但是介绍题意的时候,这里失误了 +public class Problem_0425_WordSquares { + + public static List> wordSquares(String[] words) { + int n = words[0].length(); + // 所有单词,所有前缀字符串,都会成为key! + HashMap> map = new HashMap<>(); + for (String word : words) { + for (int end = 0; end <= n; end++) { + String prefix = word.substring(0, end); + if (!map.containsKey(prefix)) { + map.put(prefix, new ArrayList<>()); + } + map.get(prefix).add(word); + } + } + List> ans = new ArrayList<>(); + process(0, n, map, new LinkedList<>(), ans); + return ans; + } + + // i, 当前填到第i号单词,从0开始,填到n-1 + // map, 前缀所拥有的单词 + // path, 之前填过的单词, 0...i-1填过的 + // ans, 收集答案 + public static void process(int i, int n, HashMap> map, LinkedList path, + List> ans) { + if (i == n) { + ans.add(new ArrayList<>(path)); + } else { + // 把限制求出来,前缀的限制! + StringBuilder builder = new StringBuilder(); + for (String pre : path) { + builder.append(pre.charAt(i)); + } + String prefix = builder.toString(); + if (map.containsKey(prefix)) { + for (String next : map.get(prefix)) { + path.addLast(next); + process(i + 1, n, map, path, ans); + path.pollLast(); + } + } + } + } + +} diff --git a/大厂刷题班/class47/Code01_DynamicSegmentTree.java b/大厂刷题班/class47/Code01_DynamicSegmentTree.java new file mode 100644 index 0000000..6a75a39 --- /dev/null +++ b/大厂刷题班/class47/Code01_DynamicSegmentTree.java @@ -0,0 +1,135 @@ +package class47; + +// 只支持单点增加 + 范围查询的动态开点线段树(累加和) +public class Code01_DynamicSegmentTree { + + public static class Node { + public int sum; + public Node left; + public Node right; + } + + // arr[0] -> 1 + // 线段树,从1开始下标! + public static class DynamicSegmentTree { + public Node root; + public int size; + + public DynamicSegmentTree(int max) { + root = new Node(); + size = max; + } + + // 下标i这个位置的数,增加v + public void add(int i, int v) { + add(root, 1, size, i, v); + } + + // c-> cur 当前节点!表达的范围 l~r + // i位置的数,增加v + // 潜台词!i一定在l~r范围上! + private void add(Node c, int l, int r, int i, int v) { + if (l == r) { + c.sum += v; + } else { // l~r 还可以划分 + int mid = (l + r) / 2; + if (i <= mid) { // l ~ mid + if (c.left == null) { + c.left = new Node(); + } + add(c.left, l, mid, i, v); + } else { // mid + 1 ~ r + if (c.right == null) { + c.right = new Node(); + } + add(c.right, mid + 1, r, i, v); + } + c.sum = (c.left != null ? c.left.sum : 0) + (c.right != null ? c.right.sum : 0); + } + } + + // s~e范围的累加和,告诉我! + public int query(int s, int e) { + return query(root, 1, size, s, e); + } + + // 当前节点c,表达的范围l~r + // 收到了一个任务,s~e这个任务! + // s~e这个任务,影响了多少l~r范围的数,把答案返回! + private int query(Node c, int l, int r, int s, int e) { + if (c == null) { + return 0; + } + if (s <= l && r <= e) { // 3~6 1~100任务 + return c.sum; + } + // 有影响,但又不是全影响 + // l ~ r + // l~mid mid+1~r + int mid = (l + r) / 2; + // 1~100 + // 1~50 51 ~ 100 + // 任务 s~e 53~76 + if (e <= mid) { + return query(c.left, l, mid, s, e); + } else if (s > mid) { + return query(c.right, mid + 1, r, s, e); + } else { + return query(c.left, l, mid, s, e) + query(c.right, mid + 1, r, s, e); + } + } + + } + + public static class Right { + public int[] arr; + + public Right(int size) { + arr = new int[size + 1]; + } + + public void add(int i, int v) { + arr[i] += v; + } + + public int query(int s, int e) { + int sum = 0; + for (int i = s; i <= e; i++) { + sum += arr[i]; + } + return sum; + } + + } + + public static void main(String[] args) { + int size = 10000; + int testTime = 50000; + int value = 500; + DynamicSegmentTree dst = new DynamicSegmentTree(size); + Right right = new Right(size); + System.out.println("测试开始"); + for (int k = 0; k < testTime; k++) { + if (Math.random() < 0.5) { + int i = (int) (Math.random() * size) + 1; + int v = (int) (Math.random() * value); + dst.add(i, v); + right.add(i, v); + } else { + int a = (int) (Math.random() * size) + 1; + int b = (int) (Math.random() * size) + 1; + int s = Math.min(a, b); + int e = Math.max(a, b); + int ans1 = dst.query(s, e); + int ans2 = right.query(s, e); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(ans1); + System.out.println(ans2); + } + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class47/Code02_DynamicSegmentTree.java b/大厂刷题班/class47/Code02_DynamicSegmentTree.java new file mode 100644 index 0000000..1dca034 --- /dev/null +++ b/大厂刷题班/class47/Code02_DynamicSegmentTree.java @@ -0,0 +1,198 @@ +package class47; + +// 同时支持范围增加 + 范围修改 + 范围查询的动态开点线段树(累加和) +// 真的用到!才去建立 +// 懒更新,及其所有的东西,和普通线段树,没有任何区别! +public class Code02_DynamicSegmentTree { + + public static class Node { + public int sum; + public int lazy; + public int change; + public boolean update; + public Node left; + public Node right; + } + + public static class DynamicSegmentTree { + public Node root; + public int size; + + public DynamicSegmentTree(int max) { + root = new Node(); + size = max; + } + + private void pushUp(Node c) { + c.sum = c.left.sum + c.right.sum; + } + + private void pushDown(Node p, int ln, int rn) { + if (p.left == null) { + p.left = new Node(); + } + if (p.right == null) { + p.right = new Node(); + } + if (p.update) { + p.left.update = true; + p.right.update = true; + p.left.change = p.change; + p.right.change = p.change; + p.left.lazy = 0; + p.right.lazy = 0; + p.left.sum = p.change * ln; + p.right.sum = p.change * rn; + p.update = false; + } + if (p.lazy != 0) { + p.left.lazy += p.lazy; + p.right.lazy += p.lazy; + p.left.sum += p.lazy * ln; + p.right.sum += p.lazy * rn; + p.lazy = 0; + } + } + + public void update(int s, int e, int v) { + update(root, 1, size, s, e, v); + } + + private void update(Node c, int l, int r, int s, int e, int v) { + if (s <= l && r <= e) { + c.update = true; + c.change = v; + c.sum = v * (r - l + 1); + c.lazy = 0; + } else { + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + if (s <= mid) { + update(c.left, l, mid, s, e, v); + } + if (e > mid) { + update(c.right, mid + 1, r, s, e, v); + } + pushUp(c); + } + } + + public void add(int s, int e, int v) { + add(root, 1, size, s, e, v); + } + + private void add(Node c, int l, int r, int s, int e, int v) { + if (s <= l && r <= e) { + c.sum += v * (r - l + 1); + c.lazy += v; + } else { + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + if (s <= mid) { + add(c.left, l, mid, s, e, v); + } + if (e > mid) { + add(c.right, mid + 1, r, s, e, v); + } + pushUp(c); + } + } + + public int query(int s, int e) { + return query(root, 1, size, s, e); + } + + private int query(Node c, int l, int r, int s, int e) { + if (s <= l && r <= e) { + return c.sum; + } + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + int ans = 0; + if (s <= mid) { + ans += query(c.left, l, mid, s, e); + } + if (e > mid) { + ans += query(c.right, mid + 1, r, s, e); + } + return ans; + } + + } + + public static class Right { + public int[] arr; + + public Right(int size) { + arr = new int[size + 1]; + } + + public void add(int s, int e, int v) { + for (int i = s; i <= e; i++) { + arr[i] += v; + } + } + + public void update(int s, int e, int v) { + for (int i = s; i <= e; i++) { + arr[i] = v; + } + } + + public int query(int s, int e) { + int sum = 0; + for (int i = s; i <= e; i++) { + sum += arr[i]; + } + return sum; + } + + } + + public static void main(String[] args) { + int n = 1000; + int value = 50; + int createTimes = 5000; + int operateTimes = 5000; + System.out.println("测试开始"); + for (int i = 0; i < createTimes; i++) { + int size = (int) (Math.random() * n) + 1; + DynamicSegmentTree dst = new DynamicSegmentTree(size); + Right right = new Right(size); + for (int k = 0; k < operateTimes; k++) { + double choose = Math.random(); + if (choose < 0.333) { + int a = (int) (Math.random() * size) + 1; + int b = (int) (Math.random() * size) + 1; + int s = Math.min(a, b); + int e = Math.max(a, b); + int v = (int) (Math.random() * value); + dst.update(s, e, v); + right.update(s, e, v); + } else if (choose < 0.666) { + int a = (int) (Math.random() * size) + 1; + int b = (int) (Math.random() * size) + 1; + int s = Math.min(a, b); + int e = Math.max(a, b); + int v = (int) (Math.random() * value); + dst.add(s, e, v); + right.add(s, e, v); + } else { + int a = (int) (Math.random() * size) + 1; + int b = (int) (Math.random() * size) + 1; + int s = Math.min(a, b); + int e = Math.max(a, b); + int ans1 = dst.query(s, e); + int ans2 = right.query(s, e); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(ans1); + System.out.println(ans2); + } + } + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class47/Problem_0315_CountOfSmallerNumbersAfterSelf.java b/大厂刷题班/class47/Problem_0315_CountOfSmallerNumbersAfterSelf.java new file mode 100644 index 0000000..7f01d20 --- /dev/null +++ b/大厂刷题班/class47/Problem_0315_CountOfSmallerNumbersAfterSelf.java @@ -0,0 +1,94 @@ +package class47; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +// 利用只支持单点增加 + 范围查询的动态开点线段树(累加和),解决leetcode 315 +public class Problem_0315_CountOfSmallerNumbersAfterSelf { + + public static class Node { + public int sum; + public Node left; + public Node right; + } + + public static class DynamicSegmentTree { + public Node root; + public int size; + + public DynamicSegmentTree(int max) { + root = new Node(); + size = max; + } + + public void add(int i, int v) { + add(root, 1, size, i, v); + } + + private void add(Node c, int l, int r, int i, int v) { + if (l == r) { + c.sum += v; + } else { + int mid = (l + r) / 2; + if (i <= mid) { + if (c.left == null) { + c.left = new Node(); + } + add(c.left, l, mid, i, v); + } else { + if (c.right == null) { + c.right = new Node(); + } + add(c.right, mid + 1, r, i, v); + } + c.sum = (c.left != null ? c.left.sum : 0) + (c.right != null ? c.right.sum : 0); + } + } + + public int query(int s, int e) { + return query(root, 1, size, s, e); + } + + private int query(Node c, int l, int r, int s, int e) { + if (c == null) { + return 0; + } + if (s <= l && r <= e) { + return c.sum; + } + int mid = (l + r) / 2; + if (e <= mid) { + return query(c.left, l, mid, s, e); + } else if (s > mid) { + return query(c.right, mid + 1, r, s, e); + } else { + return query(c.left, l, mid, s, e) + query(c.right, mid + 1, r, s, e); + } + } + + } + + public static List countSmaller(int[] nums) { + List ans = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return ans; + } + int n = nums.length; + for (int i = 0; i < n; i++) { + ans.add(0); + } + int[][] arr = new int[n][]; + for (int i = 0; i < n; i++) { + arr[i] = new int[] { nums[i], i }; + } + Arrays.sort(arr, (a, b) -> (a[0] - b[0])); + DynamicSegmentTree dst = new DynamicSegmentTree(n); + for (int[] num : arr) { + ans.set(num[1], dst.query(num[1] + 1, n)); + dst.add(num[1] + 1, 1); + } + return ans; + } + +} diff --git a/大厂刷题班/class47/Problem_0358_RearrangeStringKDistanceApart.java b/大厂刷题班/class47/Problem_0358_RearrangeStringKDistanceApart.java new file mode 100644 index 0000000..60319c7 --- /dev/null +++ b/大厂刷题班/class47/Problem_0358_RearrangeStringKDistanceApart.java @@ -0,0 +1,64 @@ +package class47; + +import java.util.ArrayList; +import java.util.Arrays; + +// 本题的解法思路与leetcode 621题 TaskScheduler 问题是一样的 +public class Problem_0358_RearrangeStringKDistanceApart { + + public String rearrangeString(String s, int k) { + if (s == null || s.length() < k) { + return s; + } + char[] str = s.toCharArray(); + int[][] cnts = new int[256][2]; + for (int i = 0; i < 256; i++) { + cnts[i] = new int[] { i, 0 }; + } + int maxCount = 0; + for (char task : str) { + cnts[task][1]++; + maxCount = Math.max(maxCount, cnts[task][1]); + } + int maxKinds = 0; + for (int task = 0; task < 256; task++) { + if (cnts[task][1] == maxCount) { + maxKinds++; + } + } + int N = str.length; + if (!isValid(N, k, maxCount, maxKinds)) { + return ""; + } + ArrayList ans = new ArrayList<>(); + for (int i = 0; i < maxCount; i++) { + ans.add(new StringBuilder()); + } + Arrays.sort(cnts, (a, b) -> (b[1] - a[1])); + int i = 0; + for (; i < 256 && cnts[i][1] == maxCount; i++) { + for (int j = 0; j < maxCount; j++) { + ans.get(j).append((char) cnts[i][0]); + } + } + int out = 0; + for (; i < 256; i++) { + for (int j = 0; j < cnts[i][1]; j++) { + ans.get(out).append((char) cnts[i][0]); + out = out == ans.size() - 2 ? 0 : out + 1; + } + } + StringBuilder builder = new StringBuilder(); + for (StringBuilder b : ans) { + builder.append(b.toString()); + } + return builder.toString(); + } + + public static boolean isValid(int N, int k, int maxCount, int maxKinds) { + int restTasks = N - maxKinds; + int spaces = k * (maxCount - 1); + return spaces - restTasks <= 0; + } + +} diff --git a/大厂刷题班/class47/Problem_0428_SerializeAndDeserializeNaryTree.java b/大厂刷题班/class47/Problem_0428_SerializeAndDeserializeNaryTree.java new file mode 100644 index 0000000..6b9c1df --- /dev/null +++ b/大厂刷题班/class47/Problem_0428_SerializeAndDeserializeNaryTree.java @@ -0,0 +1,103 @@ +package class47; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class Problem_0428_SerializeAndDeserializeNaryTree { + + // 不要提交这个类 + public static class Node { + public int val; + public List children; + + public Node() { + children = new ArrayList<>(); + } + + public Node(int _val) { + val = _val; + children = new ArrayList<>(); + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + }; + + // 提交下面这个类 + public static class Codec { + + public static String serialize(Node root) { + if (root == null) { // 空树!直接返回# + return "#"; + } + StringBuilder builder = new StringBuilder(); + serial(builder, root); + return builder.toString(); + } + + // 当前来到head + private static void serial(StringBuilder builder, Node head) { + builder.append(head.val + ","); + if (!head.children.isEmpty()) { + builder.append("[,"); + for (Node next : head.children) { + serial(builder, next); + } + builder.append("],"); + } + } + + public static Node deserialize(String data) { + if (data.equals("#")) { + return null; + } + String[] splits = data.split(","); + Queue queue = new LinkedList<>(); + for (String str : splits) { + queue.offer(str); + } + return deserial(queue); + } + + private static Node deserial(Queue queue) { + Node cur = new Node(Integer.valueOf(queue.poll())); + cur.children = new ArrayList<>(); + if (!queue.isEmpty() && queue.peek().equals("[")) { + queue.poll(); + while (!queue.peek().equals("]")) { + Node child = deserial(queue); + cur.children.add(child); + } + queue.poll(); + } + return cur; + } + + } + + public static void main(String[] args) { + // 如果想跑以下的code,请把Codec类描述和内部所有方法改成static的 + Node a = new Node(1); + Node b = new Node(2); + Node c = new Node(3); + Node d = new Node(4); + Node e = new Node(5); + Node f = new Node(6); + Node g = new Node(7); + a.children.add(b); + a.children.add(c); + a.children.add(d); + b.children.add(e); + b.children.add(f); + e.children.add(g); + String content = Codec.serialize(a); + System.out.println(content); + Node head = Codec.deserialize(content); + System.out.println(content.equals(Codec.serialize(head))); + } + +} diff --git a/大厂刷题班/class47/Problem_0465_OptimalAccountBalancing.java b/大厂刷题班/class47/Problem_0465_OptimalAccountBalancing.java new file mode 100644 index 0000000..d1fe945 --- /dev/null +++ b/大厂刷题班/class47/Problem_0465_OptimalAccountBalancing.java @@ -0,0 +1,149 @@ +package class47; + +import java.util.Arrays; +import java.util.HashMap; + +// 需要证明: +// 一个集合中,假设整体的累加和为K, +// 不管该集合用了什么样的0集合划分方案,当一个新的数到来时: +// 1) 如果该数是-K,那么任何0集合的划分方案中,因为新数字的加入,0集合的数量都会+1 +// 2) 如果该数不是-K,那么任何0集合的划分方案中,0集合的数量都会不变 +public class Problem_0465_OptimalAccountBalancing { + + // 用位信息替代集合结构的暴力尝试 + public static int minTransfers1(int[][] transactions) { + // 不管转账有几笔,最终每个人收到的钱,如果是0,不进入debt数组 + // 只有最终收到的钱,不等于0的人,进入debt数组 + // 收到的钱,4,说明该给出去! + // 收到的钱,-4,说明该要回来! + // debt数组的累加和,必为0! + int[] debt = debts(transactions); + int N = debt.length; + return N - process1(debt, (1 << N) - 1, 0, N); + } + + // set -> int -> 不使用值 -> 只使用状态! + // 001110 0号人,不在集合里;1、2、3号人在集合里,4、5号人不在集合里! + // sum -> set这个集合累加和是多少?sum被set决定的! + // debt数组,收到的钱的数组(固定) + // N, debt的长度(固定) + // 返回值含义 : set这个集合中,最多能划分出多少个小集合累加和是0,返回累加和是0的小集合最多的数量 + public static int process1(int[] debt, int set, int sum, int N) { + // set中只有一个人的时候! + // debt中,没有0的,所以每个人一定都需要转账! + if ((set & (set - 1)) == 0) { + return 0; + } + int value = 0; + int max = 0; + // 尝试,每一个人都最后考虑 + // 0,如果最后考虑 + // 1,如果最后考虑 + // 2,如果最后考虑 + // .... + // n-1,最后考虑 + // 011001 + // 0(在) + // 1(不能考虑!因为不在集合里!) + for (int i = 0; i < N; i++) { + value = debt[i]; + if ((set & (1 << i)) != 0) { // i号人真的在集合里,才能考虑! + // 011001 + // 3号人在 + // 3号人之前,010001(考虑0号人和4号人剩下的情况) + // process ( set ^ (1 << i) , sum - value ) + max = Math.max(max, process1(debt, set ^ (1 << i), sum - value, N)); + } + } + return sum == 0 ? max + 1 : max; + } + + // 上面的尝试过程 + 记忆化搜索 + // 最优解 + public static int minTransfers2(int[][] transactions) { + int[] debt = debts(transactions); + int N = debt.length; + int sum = 0; + for (int num : debt) { + sum += num; + } + int[] dp = new int[1 << N]; + Arrays.fill(dp, -1); + return N - process2(debt, (1 << N) - 1, sum, N, dp); + } + + public static int process2(int[] debt, int set, int sum, int N, int[] dp) { + if (dp[set] != -1) { + return dp[set]; + } + int ans = 0; + if ((set & (set - 1)) != 0) { + int value = 0; + int max = 0; + for (int i = 0; i < N; i++) { + value = debt[i]; + if ((set & (1 << i)) != 0) { + max = Math.max(max, process2(debt, set ^ (1 << i), sum - value, N, dp)); + } + } + ans = sum == 0 ? max + 1 : max; + } + dp[set] = ans; + return ans; + } + + public static int[] debts(int[][] transactions) { + HashMap map = new HashMap<>(); + for (int[] tran : transactions) { + map.put(tran[0], map.getOrDefault(tran[0], 0) + tran[2]); + map.put(tran[1], map.getOrDefault(tran[1], 0) - tran[2]); + } + int N = 0; + for (int value : map.values()) { + if (value != 0) { + N++; + } + } + int[] debt = new int[N]; + int index = 0; + for (int value : map.values()) { + if (value != 0) { + debt[index++] = value; + } + } + return debt; + } + + // 为了测试 + public static int[][] randomTrans(int s, int n, int m) { + int[][] trans = new int[s][3]; + for (int i = 0; i < s; i++) { + trans[i][0] = (int) (Math.random() * n); + trans[i][1] = (int) (Math.random() * n); + trans[i][2] = (int) (Math.random() * m) + 1; + } + return trans; + } + + // 为了测试 + public static void main(String[] args) { + int s = 8; + int n = 8; + int m = 10; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[][] trans = randomTrans(s, n, m); + int ans1 = minTransfers1(trans); + int ans2 = minTransfers2(trans); + if (ans1 != ans2) { + System.out.println("Oops!"); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/大厂刷题班/class47/Problem_0475_Heaters.java b/大厂刷题班/class47/Problem_0475_Heaters.java new file mode 100644 index 0000000..cd0f7b0 --- /dev/null +++ b/大厂刷题班/class47/Problem_0475_Heaters.java @@ -0,0 +1,113 @@ +package class47; + +import java.util.Arrays; + +public class Problem_0475_Heaters { + + // 比如地点是7, 9, 14 + // 供暖点的位置: 1 3 4 5 13 15 17 + // 先看地点7 + // 由1供暖,半径是6 + // 由3供暖,半径是4 + // 由4供暖,半径是3 + // 由5供暖,半径是2 + // 由13供暖,半径是6 + // 由此可知,地点7应该由供暖点5来供暖,半径是2 + // 再看地点9 + // 供暖点不回退 + // 由5供暖,半径是4 + // 由13供暖,半径是4 + // 由15供暖,半径是6 + // 由此可知,地点9应该由供暖点13来供暖,半径是4 + // 为什么是13而不是5?因为接下来的地点都会更靠右,所以半径一样的时候,就应该选更右的供暖点 + // 再看地点14 + // 供暖点不回退 + // 由13供暖,半径是1 + // 由15供暖,半径是1 + // 由17供暖,半径是3 + // 由此可知,地点14应该由供暖点15来供暖,半径是1 + // 以此类推 + public static int findRadius(int[] houses, int[] heaters) { + Arrays.sort(houses); + Arrays.sort(heaters); + int ans = 0; + // 时间复杂度O(N) + // i是地点,j是供暖点 + for (int i = 0, j = 0; i < houses.length; i++) { + while (!best(houses, heaters, i, j)) { + j++; + } + ans = Math.max(ans, Math.abs(heaters[j] - houses[i])); + } + return ans; + } + + // 这个函数含义: + // 当前的地点houses[i]由heaters[j]来供暖是最优的吗? + // 当前的地点houses[i]由heaters[j]来供暖,产生的半径是a + // 当前的地点houses[i]由heaters[j + 1]来供暖,产生的半径是b + // 如果a < b, 说明是最优,供暖不应该跳下一个位置 + // 如果a >= b, 说明不是最优,应该跳下一个位置 + public static boolean best(int[] houses, int[] heaters, int i, int j) { + return j == heaters.length - 1 + || Math.abs(heaters[j] - houses[i]) < Math.abs(heaters[j + 1] - houses[i]); + } + + // 下面这个方法不对,你能找出原因嘛?^_^ + public static int findRadius2(int[] houses, int[] heaters) { + Arrays.sort(houses); + Arrays.sort(heaters); + int ans = 0; + // 时间复杂度O(N) + // i是地点,j是供暖点 + for (int i = 0, j = 0; i < houses.length; i++) { + while (!best2(houses, heaters, i, j)) { + j++; + } + ans = Math.max(ans, Math.abs(heaters[j] - houses[i])); + } + return ans; + } + + public static boolean best2(int[] houses, int[] heaters, int i, int j) { + return j == heaters.length - 1 || Math.abs(heaters[j] - houses[i]) <= Math.abs(heaters[j + 1] - houses[i]); + } + + // 为了测试 + public static int[] randomArray(int len, int v) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 5; + int v = 10; + int testTime = 10000; + for (int i = 0; i < testTime; i++) { + int[] a = randomArray(len, v); + int[] b = randomArray(len, v); + int ans1 = findRadius(a, b); + int ans2 = findRadius2(a, b); + if (ans1 != ans2) { + System.out.println("A : "); + for (int num : a) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("B : "); + for (int num : b) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + } + +} diff --git a/大厂刷题班/class48/Code01_MinKthPairMinusABS.java b/大厂刷题班/class48/Code01_MinKthPairMinusABS.java new file mode 100644 index 0000000..14868c8 --- /dev/null +++ b/大厂刷题班/class48/Code01_MinKthPairMinusABS.java @@ -0,0 +1,122 @@ +package class48; + +import java.util.Arrays; + +// 来自学员问题 +// 比如{ 5, 3, 1, 4 } +// 全部数字对是:(5,3)、(5,1)、(5,4)、(3,1)、(3,4)、(1,4) +// 数字对的差值绝对值: 2、4、1、2、1、3 +// 差值绝对值排序后:1、1、2、2、3、4 +// 给定一个数组arr,和一个正数k +// 返回arr中所有数字对差值的绝对值,第k小的是多少 +// arr = { 5, 3, 1, 4 }, k = 4 +// 返回2 +public class Code01_MinKthPairMinusABS { + + // 暴力解,生成所有数字对差值绝对值,排序,拿出第k个,k从1开始 + public static int kthABS1(int[] arr, int k) { + int n = arr.length; + int m = ((n - 1) * n) >> 1; + if (m == 0 || k < 1 || k > m) { + return -1; + } + int[] abs = new int[m]; + int size = 0; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + abs[size++] = Math.abs(arr[i] - arr[j]); + } + } + Arrays.sort(abs); + return abs[k - 1]; + } + + // 二分 + 不回退 + public static int kthABS2(int[] arr, int k) { + int n = arr.length; + if (n < 2 || k < 1 || k > ((n * (n - 1)) >> 1)) { + return -1; + } + Arrays.sort(arr); + // 0 ~ 大-小 二分 + // l ~ r + int left = 0; + int right = arr[n - 1] - arr[0]; + int mid = 0; + int rightest = -1; + while (left <= right) { + mid = (left + right) / 2; + // 数字对差值的绝对值<=mid的数字对个数,是不是 < k个的! + if (valid(arr, mid, k)) { + rightest = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return rightest + 1; + } + + // 假设arr中的所有数字对,差值绝对值<=limit的个数为x + // 如果 x < k,达标,返回true + // 如果 x >= k,不达标,返回false + public static boolean valid(int[] arr, int limit, int k) { + int x = 0; + for (int l = 0, r = 1; l < arr.length; r = Math.max(r, ++l)) { + while (r < arr.length && arr[r] - arr[l] <= limit) { + r++; + } + x += r - l - 1; + } + return x < k; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * v); + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int size = 100; + int value = 100; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * size); + int k = (int) (Math.random() * (n * (n - 1) / 2)) + 1; + int[] arr = randomArray(n, value); + int ans1 = kthABS1(arr, k); + int ans2 = kthABS2(arr, k); + if (ans1 != ans2) { + System.out.print("arr : "); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("k : " + k); + System.out.println("ans1 : " + ans1); + System.out.println("ans2 : " + ans2); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + + long start; + long end; + int n = 500000; + int v = 50000000; + int[] arr = randomArray(n, v); + int k = (int) (Math.random() * (n * (n - 1) / 2)) + 1; + start = System.currentTimeMillis(); + kthABS2(arr, k); + end = System.currentTimeMillis(); + System.out.println("大数据量运行时间(毫秒):" + (end - start)); + } + +} diff --git a/大厂刷题班/class48/Problem_0472_ConcatenatedWords.java b/大厂刷题班/class48/Problem_0472_ConcatenatedWords.java new file mode 100644 index 0000000..533b537 --- /dev/null +++ b/大厂刷题班/class48/Problem_0472_ConcatenatedWords.java @@ -0,0 +1,124 @@ +package class48; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Problem_0472_ConcatenatedWords { + + public static class TrieNode { + public boolean end; + public TrieNode[] nexts; + + public TrieNode() { + end = false; + nexts = new TrieNode[26]; + } + } + + public static void insert(TrieNode root, char[] s) { + int path = 0; + for (char c : s) { + path = c - 'a'; + if (root.nexts[path] == null) { + root.nexts[path] = new TrieNode(); + } + root = root.nexts[path]; + } + root.end = true; + } + + // 方法1:前缀树优化 + public static List findAllConcatenatedWordsInADict1(String[] words) { + List ans = new ArrayList<>(); + if (words == null || words.length < 3) { + return ans; + } + // 字符串数量 >= 3个 + Arrays.sort(words, (str1, str2) -> str1.length() - str2.length()); + TrieNode root = new TrieNode(); + for (String str : words) { + char[] s = str.toCharArray(); // "" 题目要求 + if (s.length > 0 && split1(s, root, 0)) { + ans.add(str); + } else { + insert(root, s); + } + } + return ans; + } + + // 字符串s[i....]能不能被分解? + // 之前的元件,全在前缀树上,r就是前缀树头节点 + public static boolean split1(char[] s, TrieNode r, int i) { + boolean ans = false; + if (i == s.length) { // 没字符了! + ans = true; + } else { // 还有字符 + TrieNode c = r; + // s[i.....] + // s[i..end]作前缀,看看是不是一个元件!f(end+1)... + for (int end = i; end < s.length; end++) { + int path = s[end] - 'a'; + if (c.nexts[path] == null) { + break; + } + c = c.nexts[path]; + if (c.end && split1(s, r, end + 1)) { + ans = true; + break; + } + } + } + return ans; + } + + // 提前准备好动态规划表 + public static int[] dp = new int[1000]; + + // 方法二:前缀树优化 + 动态规划优化 + public static List findAllConcatenatedWordsInADict2(String[] words) { + List ans = new ArrayList<>(); + if (words == null || words.length < 3) { + return ans; + } + Arrays.sort(words, (str1, str2) -> str1.length() - str2.length()); + TrieNode root = new TrieNode(); + for (String str : words) { + char[] s = str.toCharArray(); + Arrays.fill(dp, 0, s.length + 1, 0); + if (s.length > 0 && split2(s, root, 0, dp)) { + ans.add(str); + } else { + insert(root, s); + } + } + return ans; + } + + public static boolean split2(char[] s, TrieNode r, int i, int[] dp) { + if (dp[i] != 0) { + return dp[i] == 1; + } + boolean ans = false; + if (i == s.length) { + ans = true; + } else { + TrieNode c = r; + for (int end = i; end < s.length; end++) { + int path = s[end] - 'a'; + if (c.nexts[path] == null) { + break; + } + c = c.nexts[path]; + if (c.end && split2(s, r, end + 1, dp)) { + ans = true; + break; + } + } + } + dp[i] = ans ? 1 : -1; + return ans; + } + +} diff --git a/大厂刷题班/class48/Problem_0483_SmallestGoodBase.java b/大厂刷题班/class48/Problem_0483_SmallestGoodBase.java new file mode 100644 index 0000000..b11f38d --- /dev/null +++ b/大厂刷题班/class48/Problem_0483_SmallestGoodBase.java @@ -0,0 +1,33 @@ +package class48; + +public class Problem_0483_SmallestGoodBase { + + // ""4651" -> 4651 + public static String smallestGoodBase(String n) { + long num = Long.valueOf(n); + // n这个数,需要从m位开始试,固定位数,一定要有m位! + for (int m = (int) (Math.log(num + 1) / Math.log(2)); m > 2; m--) { + // num开m次方 + long l = (long) (Math.pow(num, 1.0 / m)); + long r = (long) (Math.pow(num, 1.0 / (m - 1))) + 1L; + while (l <= r) { + long k = l + ((r - l) >> 1); + long sum = 0L; + long base = 1L; + for (int i = 0; i < m && sum <= num; i++) { + sum += base; + base *= k; + } + if (sum < num) { + l = k + 1; + } else if (sum > num) { + r = k - 1; + } else { + return String.valueOf(k); + } + } + } + return String.valueOf(num - 1); + } + +} diff --git a/大厂刷题班/class48/Problem_0499_TheMazeIII.java b/大厂刷题班/class48/Problem_0499_TheMazeIII.java new file mode 100644 index 0000000..38bd2ec --- /dev/null +++ b/大厂刷题班/class48/Problem_0499_TheMazeIII.java @@ -0,0 +1,87 @@ +package class48; + +public class Problem_0499_TheMazeIII { + + // 节点:来到了哪?(r,c)这个位置 + // 从哪个方向来的!d -> 0 1 2 3 4 + // 之前做了什么决定让你来到这个位置。 + public static class Node { + public int r; + public int c; + public int d; + public String p; + + public Node(int row, int col, int dir, String path) { + r = row; + c = col; + d = dir; + p = path; + } + + } + + public static String findShortestWay(int[][] maze, int[] ball, int[] hole) { + int n = maze.length; + int m = maze[0].length; + Node[] q1 = new Node[n * m], q2 = new Node[n * m]; + int s1 = 0, s2 = 0; + boolean[][][] visited = new boolean[maze.length][maze[0].length][4]; + s1 = spread(maze, n, m, new Node(ball[0], ball[1], 4, ""), visited, q1, s1); + while (s1 != 0) { + for (int i = 0; i < s1; i++) { + Node cur = q1[i]; + if (hole[0] == cur.r && hole[1] == cur.c) { + return cur.p; + } + s2 = spread(maze, n, m, cur, visited, q2, s2); + } + Node[] tmp = q1; + q1 = q2; + q2 = tmp; + s1 = s2; + s2 = 0; + } + return "impossible"; + } + + public static int[][] to = { { 1, 0 }, { 0, -1 }, { 0, 1 }, { -1, 0 }, { 0, 0 } }; + + public static String[] re = { "d", "l", "r", "u" }; + + // maze迷宫,走的格子 + // n 行数 + // m 列数 + // 当前来到的节点,cur -> (r,c) 方向 路径(决定) + // v [行][列][方向] 一个格子,其实在宽度有限遍历时,是4个点! + // q 下一层的队列 + // s 下一层队列填到了哪,size + // 当前点cur,该分裂分裂,该继续走继续走,所产生的一下层的点,进入q,s++ + // 返回值:q增长到了哪?返回size -> s + public static int spread(int[][] maze, int n, int m, + Node cur, boolean[][][] v, Node[] q, int s) { + int d = cur.d; + int r = cur.r + to[d][0]; + int c = cur.c + to[d][1]; + // 分裂去! + if (d == 4 || r < 0 || r == n || c < 0 || c == m || maze[r][c] != 0) { + for (int i = 0; i < 4; i++) { + if (i != d) { + r = cur.r + to[i][0]; + c = cur.c + to[i][1]; + if (r >= 0 && r < n && c >= 0 && c < m && maze[r][c] == 0 && !v[r][c][i]) { + v[r][c][i] = true; + Node next = new Node(r, c, i, cur.p + re[i]); + q[s++] = next; + } + } + } + } else { // 不分裂!继续走! + if (!v[r][c][d]) { + v[r][c][d] = true; + q[s++] = new Node(r, c, d, cur.p); + } + } + return s; + } + +} diff --git a/大厂刷题班/class49/Problem_0377_CombinationSumIV.java b/大厂刷题班/class49/Problem_0377_CombinationSumIV.java new file mode 100644 index 0000000..13e7ffa --- /dev/null +++ b/大厂刷题班/class49/Problem_0377_CombinationSumIV.java @@ -0,0 +1,64 @@ +package class49; + +import java.util.Arrays; + +public class Problem_0377_CombinationSumIV { + + // 当前剩下的值是rest, + // nums中所有的值,都可能作为分解rest的,第一块!全试一试 + // nums, 无重复,都是正 + // rest, + public static int ways(int rest, int[] nums) { + if (rest < 0) { + return 0; + } + if (rest == 0) { + return 1; + } + int ways = 0; + for (int num : nums) { + ways += ways(rest - num, nums); + } + return ways; + } + + public static int[] dp = new int[1001]; + + public static int combinationSum41(int[] nums, int target) { + Arrays.fill(dp, 0, target + 1, -1); + return process1(nums, target); + } + + public static int process1(int[] nums, int rest) { + if (rest < 0) { + return 0; + } + if (dp[rest] != -1) { + return dp[rest]; + } + int ans = 0; + if (rest == 0) { + ans = 1; + } else { + for (int num : nums) { + ans += process1(nums, rest - num); + } + } + dp[rest] = ans; + return ans; + } + + // 剪枝 + 严格位置依赖的动态规划 + public static int combinationSum42(int[] nums, int target) { + Arrays.sort(nums); + int[] dp = new int[target + 1]; + dp[0] = 1; + for (int rest = 1; rest <= target; rest++) { + for (int i = 0; i < nums.length && nums[i] <= rest; i++) { + dp[rest] += dp[rest - nums[i]]; + } + } + return dp[target]; + } + +} diff --git a/大厂刷题班/class49/Problem_0440_KthSmallestInLexicographicalOrder.java b/大厂刷题班/class49/Problem_0440_KthSmallestInLexicographicalOrder.java new file mode 100644 index 0000000..9514700 --- /dev/null +++ b/大厂刷题班/class49/Problem_0440_KthSmallestInLexicographicalOrder.java @@ -0,0 +1,88 @@ +package class49; + +// 这道题在leetcode上,所有题解都只能做到O( (logN) 平方)的解 +// 我们课上讲的是O(logN)的解 +// 打败所有题解 +public class Problem_0440_KthSmallestInLexicographicalOrder { + + public static int[] offset = { 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + public static int[] number = { 0, 1, 11, 111, 1111, 11111, 111111, 1111111, 11111111, 111111111, 1111111111 }; + + public static int findKthNumber(int n, int k) { + // 数字num,有几位,len位 + // 65237, 5位,len = 5 + int len = len(n); + // 65237, 开头数字,6,first + int first = n / offset[len]; + // 65237,左边有几个? + int left = (first - 1) * number[len]; + int pick = 0; + int already = 0; + if (k <= left) { + // k / a 向上取整-> (k + a - 1) / a + pick = (k + number[len] - 1) / number[len]; + already = (pick - 1) * number[len]; + return kth((pick + 1) * offset[len] - 1, len, k - already); + } + int mid = number[len - 1] + (n % offset[len]) + 1; + if (k - left <= mid) { + return kth(n, len, k - left); + } + k -= left + mid; + len--; + pick = (k + number[len] - 1) / number[len] + first; + already = (pick - first - 1) * number[len]; + return kth((pick + 1) * offset[len] - 1, len, k - already); + } + + public static int len(int n) { + int len = 0; + while (n != 0) { + n /= 10; + len++; + } + return len; + } + + public static int kth(int max, int len, int kth) { + // 中间范围还管不管的着! + // 有任何一步,中间位置没命中,左或者右命中了,那以后就都管不着了! + // 但是开始时,肯定是管的着的! + boolean closeToMax = true; + int ans = max / offset[len]; + while (--kth > 0) { + max %= offset[len--]; + int pick = 0; + if (!closeToMax) { + pick = (kth - 1) / number[len]; + ans = ans * 10 + pick; + kth -= pick * number[len]; + } else { + int first = max / offset[len]; + int left = first * number[len]; + if (kth <= left) { + closeToMax = false; + pick = (kth - 1) / number[len]; + ans = ans * 10 + pick; + kth -= pick * number[len]; + continue; + } + kth -= left; + int mid = number[len - 1] + (max % offset[len]) + 1; + if (kth <= mid) { + ans = ans * 10 + first; + continue; + } + closeToMax = false; + kth -= mid; + len--; + pick = (kth + number[len] - 1) / number[len] + first; + ans = ans * 10 + pick; + kth -= (pick - first - 1) * number[len]; + } + } + return ans; + } + +} diff --git a/大厂刷题班/class49/Problem_0446_ArithmeticSlicesIISubsequence.java b/大厂刷题班/class49/Problem_0446_ArithmeticSlicesIISubsequence.java new file mode 100644 index 0000000..c34f2f3 --- /dev/null +++ b/大厂刷题班/class49/Problem_0446_ArithmeticSlicesIISubsequence.java @@ -0,0 +1,30 @@ +package class49; + +import java.util.ArrayList; +import java.util.HashMap; + +public class Problem_0446_ArithmeticSlicesIISubsequence { + + // 时间复杂度是O(N^2),最优解的时间复杂度 + public static int numberOfArithmeticSlices(int[] arr) { + int N = arr.length; + int ans = 0; + ArrayList> maps = new ArrayList<>(); + for (int i = 0; i < N; i++) { + maps.add(new HashMap<>()); + // ....j...i(结尾) + for (int j = i - 1; j >= 0; j--) { + long diff = (long) arr[i] - (long) arr[j]; + if (diff <= Integer.MIN_VALUE || diff > Integer.MAX_VALUE) { + continue; + } + int dif = (int) diff; + int count = maps.get(j).getOrDefault(dif, 0); + ans += count; + maps.get(i).put(dif, maps.get(i).getOrDefault(dif, 0) + count + 1); + } + } + return ans; + } + +} diff --git a/大厂刷题班/class49/Problem_0489_RobotRoomCleaner.java b/大厂刷题班/class49/Problem_0489_RobotRoomCleaner.java new file mode 100644 index 0000000..a52a3d2 --- /dev/null +++ b/大厂刷题班/class49/Problem_0489_RobotRoomCleaner.java @@ -0,0 +1,57 @@ +package class49; + +import java.util.HashSet; + +public class Problem_0489_RobotRoomCleaner { + + // 不要提交这个接口的内容 + interface Robot { + public boolean move(); + + public void turnLeft(); + + public void turnRight(); + + public void clean(); + } + + // 提交下面的内容 + public static void cleanRoom(Robot robot) { + clean(robot, 0, 0, 0, new HashSet<>()); + } + + private static final int[][] ds = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } }; + + // 机器人robot, + // 当前来到的位置(x,y),且之前没来过 + // 机器人脸冲什么方向d,0 1 2 3 + // visited里记录了机器人走过哪些位置 + // 函数的功能:不要重复走visited里面的位置,把剩下的位置,都打扫干净! + // 而且要回去! + public static void clean(Robot robot, int x, int y, int d, HashSet visited) { + robot.clean(); + visited.add(x + "_" + y); + for (int i = 0; i < 4; i++) { + // d = 0 : 0 1 2 3 + // d = 1 : 1 2 3 0 + // d = 2 : 2 3 0 1 + // d = 3 : 3 0 1 2 + // 下一步的方向! + int nd = (i + d) % 4; + // 当下一步的方向定了!下一步的位置在哪?(nx, ny) + int nx = ds[nd][0] + x; + int ny = ds[nd][1] + y; + if (!visited.contains(nx + "_" + ny) && robot.move()) { + clean(robot, nx, ny, nd, visited); + } + robot.turnRight(); + } + // 负责回去:之前的位置,怎么到你的!你要回去,而且方向和到你之前,要一致! + robot.turnRight(); + robot.turnRight(); + robot.move(); + robot.turnRight(); + robot.turnRight(); + } + +} diff --git a/大厂刷题班/class49/Problem_0527_WordAbbreviation.java b/大厂刷题班/class49/Problem_0527_WordAbbreviation.java new file mode 100644 index 0000000..97440d6 --- /dev/null +++ b/大厂刷题班/class49/Problem_0527_WordAbbreviation.java @@ -0,0 +1,48 @@ +package class49; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class Problem_0527_WordAbbreviation { + + public static List wordsAbbreviation(List words) { + int len = words.size(); + List res = new ArrayList<>(); + HashMap> map = new HashMap<>(); + for (int i = 0; i < len; i++) { + res.add(makeAbbr(words.get(i), 1)); + List list = map.getOrDefault(res.get(i), new ArrayList<>()); + list.add(i); + map.put(res.get(i), list); + } + int[] prefix = new int[len]; + for (int i = 0; i < len; i++) { + if (map.get(res.get(i)).size() > 1) { + List indexes = map.get(res.get(i)); + map.remove(res.get(i)); + for (int j : indexes) { + prefix[j]++; + res.set(j, makeAbbr(words.get(j), prefix[j])); + List list = map.getOrDefault(res.get(j), new ArrayList<>()); + list.add(j); + map.put(res.get(j), list); + } + i--; + } + } + return res; + } + + public static String makeAbbr(String s, int k) { + if (k >= s.length() - 2) { + return s; + } + StringBuilder builder = new StringBuilder(); + builder.append(s.substring(0, k)); + builder.append(s.length() - 1 - k); + builder.append(s.charAt(s.length() - 1)); + return builder.toString(); + } + +} diff --git a/大厂刷题班/class49/Problem_0548_SplitArrayEithEqualSum.java b/大厂刷题班/class49/Problem_0548_SplitArrayEithEqualSum.java new file mode 100644 index 0000000..3009e59 --- /dev/null +++ b/大厂刷题班/class49/Problem_0548_SplitArrayEithEqualSum.java @@ -0,0 +1,45 @@ +package class49; + +public class Problem_0548_SplitArrayEithEqualSum { + + public static boolean splitArray(int[] nums) { + if (nums.length < 7) { + return false; + } + int[] sumLeftToRight = new int[nums.length]; + int[] sumRightToLeft = new int[nums.length]; + int s = 0; + for (int i = 0; i < nums.length; i++) { + sumLeftToRight[i] = s; + s += nums[i]; + } + s = 0; + for (int i = nums.length - 1; i >= 0; i--) { + sumRightToLeft[i] = s; + s += nums[i]; + } + for (int i = 1; i < nums.length - 5; i++) { + for (int j = nums.length - 2; j > i + 3; j--) { + if (sumLeftToRight[i] == sumRightToLeft[j] && find(sumLeftToRight, sumRightToLeft, i, j)) { + return true; + } + } + } + return false; + } + + public static boolean find(int[] sumLeftToRight, int[] sumRightToLeft, int l, int r) { + int s = sumLeftToRight[l]; + int prefix = sumLeftToRight[l + 1]; + int suffix = sumRightToLeft[r - 1]; + for (int i = l + 2; i < r - 1; i++) { + int s1 = sumLeftToRight[i] - prefix; + int s2 = sumRightToLeft[i] - suffix; + if (s1 == s2 && s1 == s) { + return true; + } + } + return false; + } + +} diff --git a/大厂刷题班/class49/Problem_0564_FindTheClosestPalindrome.java b/大厂刷题班/class49/Problem_0564_FindTheClosestPalindrome.java new file mode 100644 index 0000000..b8ff906 --- /dev/null +++ b/大厂刷题班/class49/Problem_0564_FindTheClosestPalindrome.java @@ -0,0 +1,72 @@ +package class49; + +public class Problem_0564_FindTheClosestPalindrome { + + public static String nearestPalindromic(String n) { + Long num = Long.valueOf(n); + Long raw = getRawPalindrome(n); + Long big = raw > num ? raw : getBigPalindrome(raw); + Long small = raw < num ? raw : getSmallPalindrome(raw); + return String.valueOf(big - num >= num - small ? small : big); + } + + public static Long getRawPalindrome(String n) { + char[] chs = n.toCharArray(); + int len = chs.length; + for (int i = 0; i < len / 2; i++) { + chs[len - 1 - i] = chs[i]; + } + return Long.valueOf(String.valueOf(chs)); + } + + public static Long getBigPalindrome(Long raw) { + char[] chs = String.valueOf(raw).toCharArray(); + char[] res = new char[chs.length + 1]; + res[0] = '0'; + for (int i = 0; i < chs.length; i++) { + res[i + 1] = chs[i]; + } + int size = chs.length; + for (int j = (size - 1) / 2 + 1; j >= 0; j--) { + if (++res[j] > '9') { + res[j] = '0'; + } else { + break; + } + } + int offset = res[0] == '1' ? 1 : 0; + size = res.length; + for (int i = size - 1; i >= (size + offset) / 2; i--) { + res[i] = res[size - i - offset]; + } + return Long.valueOf(String.valueOf(res)); + } + + public static Long getSmallPalindrome(Long raw) { + char[] chs = String.valueOf(raw).toCharArray(); + char[] res = new char[chs.length]; + int size = res.length; + for (int i = 0; i < size; i++) { + res[i] = chs[i]; + } + for (int j = (size - 1) / 2; j >= 0; j--) { + if (--res[j] < '0') { + res[j] = '9'; + } else { + break; + } + } + if (res[0] == '0') { + res = new char[size - 1]; + for (int i = 0; i < res.length; i++) { + res[i] = '9'; + } + return size == 1 ? 0 : Long.parseLong(String.valueOf(res)); + } + for (int k = 0; k < size / 2; k++) { + res[size - 1 - k] = res[k]; + } + return Long.valueOf(String.valueOf(res)); + } + +} diff --git a/大厂刷题班/class50/Problem_0568_MaximumVacationDays.java b/大厂刷题班/class50/Problem_0568_MaximumVacationDays.java new file mode 100644 index 0000000..0821911 --- /dev/null +++ b/大厂刷题班/class50/Problem_0568_MaximumVacationDays.java @@ -0,0 +1,50 @@ +package class50; + +public class Problem_0568_MaximumVacationDays { + + public static int maxVacationDays(int[][] fly, int[][] day) { + int n = fly.length; + int k = day[0].length; + // pas[i] = {a, b, c} + // 从a、b、c能飞到i + int[][] pass = new int[n][]; + for (int i = 0; i < n; i++) { + int s = 0; + for (int j = 0; j < n; j++) { + if (fly[j][i] != 0) { + s++; + } + } + pass[i] = new int[s]; + for (int j = n - 1; j >= 0; j--) { + if (fly[j][i] != 0) { + pass[i][--s] = j; + } + } + } + // dp[i][j] -> 第i周必须在j这座城,0~i-1周(随意),最大休假天数 + int[][] dp = new int[k][n]; + // 飞的时机,是周一早上飞,认为对时间没有影响,直接到某个城,然后过一周 + dp[0][0] = day[0][0]; + for (int j = 1; j < n; j++) { + dp[0][j] = fly[0][j] != 0 ? day[j][0] : -1; + } + for (int i = 1; i < k; i++) { // 第i周 + for (int j = 0; j < n; j++) { // 在j号城过! + // 第i周,要怎么到j号城 + // 下面max的初始值,我第i-1周,就在j号城,选择不动地方,进入第i周 + int max = dp[i - 1][j]; + for (int p : pass[j]) { // 枚举什么?能到j号城的城市p + max = Math.max(max, dp[i - 1][p]); + } + dp[i][j] = max != -1 ? max + day[j][i] : -1; + } + } + int ans = 0; + for (int i = 0; i < n; i++) { + ans = Math.max(ans, dp[k - 1][i]); + } + return ans; + } + +} diff --git a/大厂刷题班/class50/Problem_0587_ErectTheFence.java b/大厂刷题班/class50/Problem_0587_ErectTheFence.java new file mode 100644 index 0000000..8b7aabb --- /dev/null +++ b/大厂刷题班/class50/Problem_0587_ErectTheFence.java @@ -0,0 +1,55 @@ +package class50; + +import java.util.Arrays; + +public class Problem_0587_ErectTheFence { + + public static int[][] outerTrees(int[][] points) { + int n = points.length; + int s = 0; + int[][] stack = new int[n << 1][]; + // x小的排前面,x一样的,y小的排前面 + Arrays.sort(points, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]); + for (int i = 0; i < n; i++) { + while (s > 1 && cross(stack[s - 2], stack[s - 1], points[i]) > 0) { + s--; + } + stack[s++] = points[i]; + } + for (int i = n - 2; i >= 0; i--) { + while (s > 1 && cross(stack[s - 2], stack[s - 1], points[i]) > 0) { + s--; + } + stack[s++] = points[i]; + } + // 去重返回 + Arrays.sort(stack, 0, s, (a, b) -> b[0] == a[0] ? b[1] - a[1] : b[0] - a[0]); + n = 1; + for (int i = 1; i < s; i++) { + // 如果i点,x和y,与i-1点,x和y都一样 + // i点与i-1点,在同一个位置,此时,i点不保留 + if (stack[i][0] != stack[i - 1][0] || stack[i][1] != stack[i - 1][1]) { + stack[n++] = stack[i]; + } + } + return Arrays.copyOf(stack, n); + } + + // 叉乘的实现 + // 假设有a、b、c三个点,并且给出每个点的(x,y)位置 + // 从a到c的向量,在从a到b的向量的哪一侧? + // 如果a到c的向量,在从a到b的向量右侧,返回正数 + // 如果a到c的向量,在从a到b的向量左侧,返回负数 + // 如果a到c的向量,和从a到b的向量重合,返回0 + public static int cross(int[] a, int[] b, int[] c) { + return (b[1] - a[1]) * (c[0] - b[0]) - (b[0] - a[0]) * (c[1] - b[1]); + } + + public static void main(String[] args) { + int[] a = { 4, 4 }; + int[] b = { 1, 1 }; + int[] c = { 1, 5 }; + System.out.println(cross(a, b, c)); + } + +} diff --git a/大厂刷题班/class50/Problem_0588_DesignInMemoryFileSystem.java b/大厂刷题班/class50/Problem_0588_DesignInMemoryFileSystem.java new file mode 100644 index 0000000..e44ea18 --- /dev/null +++ b/大厂刷题班/class50/Problem_0588_DesignInMemoryFileSystem.java @@ -0,0 +1,107 @@ +package class50; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; + +public class Problem_0588_DesignInMemoryFileSystem { + + class FileSystem { + + public class Node { + // 文件名、目录名 + public String name; + // content == null 意味着这个节点是目录 + // content != null 意味着这个节点是文件 + public StringBuilder content; + public TreeMap nexts; + + // 构造目录 + public Node(String n) { + name = n; + content = null; + nexts = new TreeMap<>(); + } + + // 构造文件,c是文件内容 + public Node(String n, String c) { + name = n; + content = new StringBuilder(c); + nexts = new TreeMap<>(); + } + + } + + public Node head; + + public FileSystem() { + head = new Node(""); + } + + public List ls(String path) { + List ans = new ArrayList<>(); + Node cur = head; + String[] parts = path.split("/"); + int n = parts.length; + for (int i = 1; i < n; i++) { + if (!cur.nexts.containsKey(parts[i])) { + return ans; + } + cur = cur.nexts.get(parts[i]); + } + // cur结束了!来到path最后的节点,该返回了 + // ls a/b/c cur 来到c目录 + // 如果c是目录,那么就要返回c下面所有的东西! + // 如果c是文件,那么就值返回c + if (cur.content == null) { + ans.addAll(cur.nexts.keySet()); + } else { + ans.add(cur.name); + } + return ans; + } + + public void mkdir(String path) { + Node cur = head; + String[] parts = path.split("/"); + int n = parts.length; + for (int i = 1; i < n; i++) { + if (!cur.nexts.containsKey(parts[i])) { + cur.nexts.put(parts[i], new Node(parts[i])); + } + cur = cur.nexts.get(parts[i]); + } + } + + public void addContentToFile(String path, String content) { + Node cur = head; + String[] parts = path.split("/"); + int n = parts.length; + for (int i = 1; i < n - 1; i++) { + if (!cur.nexts.containsKey(parts[i])) { + cur.nexts.put(parts[i], new Node(parts[i])); + } + cur = cur.nexts.get(parts[i]); + } + // 来到的是倒数第二的节点了!注意for! + if (!cur.nexts.containsKey(parts[n - 1])) { + cur.nexts.put(parts[n - 1], new Node(parts[n - 1], "")); + } + cur.nexts.get(parts[n - 1]).content.append(content); + } + + public String readContentFromFile(String path) { + Node cur = head; + String[] parts = path.split("/"); + int n = parts.length; + for (int i = 1; i < n; i++) { + if (!cur.nexts.containsKey(parts[i])) { + cur.nexts.put(parts[i], new Node(parts[i])); + } + cur = cur.nexts.get(parts[i]); + } + return cur.content.toString(); + } + } + +} diff --git a/大厂刷题班/class50/Problem_0600_NonnegativeIntegersWithoutConsecutiveOnes.java b/大厂刷题班/class50/Problem_0600_NonnegativeIntegersWithoutConsecutiveOnes.java new file mode 100644 index 0000000..48f461d --- /dev/null +++ b/大厂刷题班/class50/Problem_0600_NonnegativeIntegersWithoutConsecutiveOnes.java @@ -0,0 +1,80 @@ +package class50; + +public class Problem_0600_NonnegativeIntegersWithoutConsecutiveOnes { + +// // f(0, false, 5,n) +// // 6 5 ... 0 -1 n +// // 0 ? 停 +// +// // 5 4 3 2 1 0 -1 +// // n : 1 0 1 1 1 0 +// // 0 1 i +// // pre : 第i+1位做的决定,0还是1 +// // 在 第i+1位做的决定 是pre的情况下,从index位开始,往后都做决定! +// // 但是,不能有相邻的1,请问有多少决定!返回! +// // alreadyLess : 之前的决定,是不是已经导致你到index的时候,已经比n小了! +// // pre -> 0 1 +// // alreadyLess -> true false +// // index -> n的位数,(logN) +// // 2 * 2 * logN +// // dp[2][] +// // int alreadyLess 0 1 +// public int f(int pre, boolean alreadyLess, int index, int n) { +// if (index == -1) { +// return 1; +// } +// if (pre == 1) { +// // 只能做0的决定,然后去往i-1位置 +// boolean curLessOrMore = alreadyLess | ((n & 1 << index) != 0); +// return f(0, curLessOrMore, index - 1, n); +// } else { // pre == 0的决定 +// // 可能性1,继续做0的决定 +// boolean curLessOrMore = alreadyLess | ((n & 1 << index) != 0); +// int p1 = f(0, curLessOrMore, index - 1, n); +// // 可能性2,做1的决定 +// // 1)pre == 0的决定, 2) +// int p2 = 0; +// if (alreadyLess || (n & 1 << index) != 0) { +// p2 = f(1, alreadyLess, index - 1, n); +// } +// return p1 + p2; +// } +// } + + public static int findIntegers(int n) { + int i = 31; + for (; i >= 0; i--) { + if ((n & (1 << i)) != 0) { + break; + } + } + // for循环出来之后,i表示,n最高位的1,在哪? + // 从这个位置,往右边低位上走! + int[][][] dp = new int[2][2][i + 1]; + return f(0, 0, i, n, dp); + } + + + public static int f(int pre, int alreadyLess, int index, int num, int[][][] dp) { + if (index == -1) { + return 1; + } + if (dp[pre][alreadyLess][index] != 0) { + return dp[pre][alreadyLess][index]; + } + int ans = 0; + if (pre == 1) { + ans = f(0, Math.max(alreadyLess, (num & (1 << index)) != 0 ? 1 : 0), index - 1, num, dp); + } else { + if ((num & (1 << index)) == 0 && alreadyLess == 0) { + ans = f(0, alreadyLess, index - 1, num, dp); + } else { + ans = f(1, alreadyLess, index - 1, num, dp) + + f(0, Math.max(alreadyLess, (num & (1 << index)) != 0 ? 1 : 0), index - 1, num, dp); + } + } + dp[pre][alreadyLess][index] = ans; + return ans; + } + +} diff --git a/大厂刷题班/class51/LCP_0003_Robot.java b/大厂刷题班/class51/LCP_0003_Robot.java new file mode 100644 index 0000000..14451b3 --- /dev/null +++ b/大厂刷题班/class51/LCP_0003_Robot.java @@ -0,0 +1,144 @@ +package class51; + +import java.util.Arrays; +import java.util.HashSet; + +// leetcode题目 : https://leetcode-cn.com/problems/programmable-robot/ +public class LCP_0003_Robot { + + public static boolean robot1(String command, int[][] obstacles, int x, int y) { + int X = 0; + int Y = 0; + HashSet set = new HashSet<>(); + set.add(0); + for (char c : command.toCharArray()) { + X += c == 'R' ? 1 : 0; + Y += c == 'U' ? 1 : 0; + set.add((X << 10) | Y); + } + // 不考虑任何额外的点,机器人能不能到达,(x,y) + if (!meet1(x, y, X, Y, set)) { + return false; + } + for (int[] ob : obstacles) { // ob[0] ob[1] + if (ob[0] <= x && ob[1] <= y && meet1(ob[0], ob[1], X, Y, set)) { + return false; + } + } + return true; + } + + // 一轮以内,X,往右一共有几个单位 + // Y, 往上一共有几个单位 + // set, 一轮以内的所有可能性 + // (x,y)要去的点 + // 机器人从(0,0)位置,能不能走到(x,y) + public static boolean meet1(int x, int y, int X, int Y, HashSet set) { + if (X == 0) { // Y != 0 往上肯定走了! + return x == 0; + } + if (Y == 0) { + return y == 0; + } + // 至少几轮? + int atLeast = Math.min(x / X, y / Y); + // 经历过最少轮数后,x剩多少? + int rx = x - atLeast * X; + // 经历过最少轮数后,y剩多少? + int ry = y - atLeast * Y; + return set.contains((rx << 10) | ry); + } + + // 此处为一轮以内,x和y最大能移动的步数,对应的2的几次方 + // 比如本题,x和y最大能移动1000步,就对应2的10次方 + // 如果换一个数据量,x和y最大能移动5000步,就对应2的13次方 + // 只需要根据数据量修改这一个变量,剩下的代码不需要调整 + public static final int bit = 10; + // 如果,x和y最大能移动的步数,对应2的bit次方 + // 那么一个坐标(x,y),所有的可能性就是:(2 ^ bit) ^ 2 = 2 ^ (bit * 2) + // 也就是,(1 << (bit << 1))个状态,记为bits + public static int bits = (1 << (bit << 1)); + // 为了表示下bits个状态,需要几个整数? + // 32位只需要一个整数,所以bits个状态,需要bits / 32 个整数 + // 即整型长度需要 : bits >> 5 + public static int[] set = new int[bits >> 5]; + + public static boolean robot2(String command, int[][] obstacles, int x, int y) { + Arrays.fill(set, 0); + set[0] = 1; + int X = 0; + int Y = 0; + for (char c : command.toCharArray()) { + X += c == 'R' ? 1 : 0; + Y += c == 'U' ? 1 : 0; + add((X << 10) | Y); + } + if (!meet2(x, y, X, Y)) { + return false; + } + for (int[] ob : obstacles) { + if (ob[0] <= x && ob[1] <= y && meet2(ob[0], ob[1], X, Y)) { + return false; + } + } + return true; + } + + public static boolean meet2(int x, int y, int X, int Y) { + if (X == 0) { + return x == 0; + } + if (Y == 0) { + return y == 0; + } + int atLeast = Math.min(x / X, y / Y); + int rx = x - atLeast * X; + int ry = y - atLeast * Y; + return contains((rx << 10) | ry); + } + + public static void add(int status) { + set[status >> 5] |= 1 << (status & 31); + } + + public static boolean contains(int status) { + return (status < bits) && (set[status >> 5] & (1 << (status & 31))) != 0; + } + + // int num -> 32位的状态 + // 请打印这32位状态 + public static void printBinary(int num) { + for (int i = 31; i >= 0; i--) { + System.out.print((num & (1 << i)) != 0 ? "1" : "0"); + } + System.out.println(); + } + + public static void main(String[] args) { + int x = 7; + printBinary(x); + + int y = 4; + + printBinary(y); + + // x_y 组合! + int c = (x << 10) | y; + printBinary(c); + + System.out.println(c); + + // 0 ~ 1048575 任何一个数,bit来表示的! +// int[] set = new int[32768]; +// set[0] = int 32 位 0~31这些数出现过没出现过 +// set[1] = int 32 位 32~63这些数出现过没出现过 + + // 0 ~ 1048575 +// int[] set = new int[32768]; +// int num = 738473; // 32 bit int +//// set[ 734873 / 32 ] // 734873 % 32 +// boolean exist = (set[num / 32] & (1 << (num % 32))) != 0; + + } + +} diff --git a/大厂刷题班/class51/Problem_0630_CourseScheduleIII.java b/大厂刷题班/class51/Problem_0630_CourseScheduleIII.java new file mode 100644 index 0000000..fb7e70d --- /dev/null +++ b/大厂刷题班/class51/Problem_0630_CourseScheduleIII.java @@ -0,0 +1,33 @@ +package class51; + +import java.util.Arrays; +import java.util.PriorityQueue; + +public class Problem_0630_CourseScheduleIII { + + public static int scheduleCourse(int[][] courses) { + // courses[i] = {花费,截止} + Arrays.sort(courses, (a, b) -> a[1] - b[1]); + // 花费时间的大根堆 + PriorityQueue heap = new PriorityQueue<>((a, b) -> b - a); + // 时间点 + int time = 0; + for (int[] c : courses) { + // + if (time + c[0] <= c[1]) { // 当前时间 + 花费 <= 截止时间的 + heap.add(c[0]); + time += c[0]; + } else { // 当前时间 + 花费 > 截止时间的, 只有淘汰掉某课,当前的课才能进来! + if (!heap.isEmpty() && heap.peek() > c[0]) { +// time -= heap.poll(); +// heap.add(c[0]); +// time += c[0]; + heap.add(c[0]); + time += c[0] - heap.poll(); + } + } + } + return heap.size(); + } + +} diff --git a/大厂刷题班/class51/Problem_0642_DesignSearchAutocompleteSystem.java b/大厂刷题班/class51/Problem_0642_DesignSearchAutocompleteSystem.java new file mode 100644 index 0000000..6eec56f --- /dev/null +++ b/大厂刷题班/class51/Problem_0642_DesignSearchAutocompleteSystem.java @@ -0,0 +1,134 @@ +package class51; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.TreeSet; + +public class Problem_0642_DesignSearchAutocompleteSystem { + + class AutocompleteSystem { + + public class TrieNode { + public TrieNode father; + public String path; + public TrieNode[] nexts; + + public TrieNode(TrieNode f, String p) { + father = f; + path = p; + nexts = new TrieNode[27]; + } + } + + public class WordCount implements Comparable { + public String word; + public int count; + + public WordCount(String w, int c) { + word = w; + count = c; + } + + public int compareTo(WordCount o) { + return count != o.count ? (o.count - count) : word.compareTo(o.word); + } + } + + // 题目的要求,只输出排名前3的列表 + public final int top = 3; + public final TrieNode root = new TrieNode(null, ""); + // 某个前缀树节点,上面的有序表,不在这个节点内部 + // 外挂 + public HashMap> nodeRankMap = new HashMap<>(); + + // 字符串 "abc" 7次 -> ("abc", 7) + public HashMap wordCountMap = new HashMap<>(); + + + public String path; + public TrieNode cur; + + // ' ' -> 0 + // 'a' -> 1 + // 'b' -> 2 + // ... + // 'z' -> 26 + // '`' a b .. z + private int f(char c) { + return c == ' ' ? 0 : (c - '`'); + } + + public AutocompleteSystem(String[] sentences, int[] times) { + path = ""; + cur = root; + for (int i = 0; i < sentences.length; i++) { + String word = sentences[i]; + int count = times[i]; + WordCount wc = new WordCount(word, count - 1); + wordCountMap.put(word, wc); + for (char c : word.toCharArray()) { + input(c); + } + input('#'); + } + } + + // 之前已经有一些历史了! + // 当前键入 c 字符 + // 请顺着之前的历史,根据c字符是什么,继续 + // path : 之前键入的字符串整体 + // cur : 当前滑到了前缀树的哪个节点 + + public List input(char c) { + List ans = new ArrayList<>(); + if (c != '#') { + path += c; + int i = f(c); + if (cur.nexts[i] == null) { + cur.nexts[i] = new TrieNode(cur, path); + } + cur = cur.nexts[i]; + if (!nodeRankMap.containsKey(cur)) { + nodeRankMap.put(cur, new TreeSet<>()); + } + int k = 0; + // for循环本身就是根据排序后的顺序来遍历! + for (WordCount wc : nodeRankMap.get(cur)) { + if (k == top) { + break; + } + ans.add(wc.word); + k++; + } + } + // c = # path = "abcde" + // # + // # + // # + // a b .. # + if (c == '#' && !path.equals("")) { + // 真的有一个,有效字符串需要加入!path + if (!wordCountMap.containsKey(path)) { + wordCountMap.put(path, new WordCount(path, 0)); + } + // 有序表内部的小对象,该小对象参与排序的指标数据改变 + // 但是有序表并不会自动刷新 + // 所以,删掉老的,加新的! + WordCount oldOne = wordCountMap.get(path); + WordCount newOne = new WordCount(path, oldOne.count + 1); + while (cur != root) { + nodeRankMap.get(cur).remove(oldOne); + nodeRankMap.get(cur).add(newOne); + cur = cur.father; + } + wordCountMap.put(path, newOne); + path = ""; + // cur 回到头了 + } + return ans; + } + + } + +} diff --git a/大厂刷题班/class51/Problem_0875_KokoEatingBananas.java b/大厂刷题班/class51/Problem_0875_KokoEatingBananas.java new file mode 100644 index 0000000..9a8f4a4 --- /dev/null +++ b/大厂刷题班/class51/Problem_0875_KokoEatingBananas.java @@ -0,0 +1,34 @@ +package class51; + +public class Problem_0875_KokoEatingBananas { + + public static int minEatingSpeed(int[] piles, int h) { + int L = 1; + int R = 0; + for (int pile : piles) { + R = Math.max(R, pile); + } + int ans = 0; + int M = 0; + while (L <= R) { + M = L + ((R - L) >> 1); + if (hours(piles, M) <= h) { + ans = M; + R = M - 1; + } else { + L = M + 1; + } + } + return ans; + } + + public static long hours(int[] piles, int speed) { + long ans = 0; + int offset = speed - 1; + for (int pile : piles) { + ans += (pile + offset) / speed; + } + return ans; + } + +} diff --git a/大厂刷题班/class51/Problem_1035_UncrossedLines.java b/大厂刷题班/class51/Problem_1035_UncrossedLines.java new file mode 100644 index 0000000..04d1c4d --- /dev/null +++ b/大厂刷题班/class51/Problem_1035_UncrossedLines.java @@ -0,0 +1,86 @@ +package class51; + +import java.util.HashMap; + +public class Problem_1035_UncrossedLines { + + // 针对这个题的题意,做的动态规划 + public static int maxUncrossedLines1(int[] A, int[] B) { + if (A == null || A.length == 0 || B == null || B.length == 0) { + return 0; + } + int N = A.length; + int M = B.length; + // dp[i][j]代表: A[0...i]对应B[0...j]最多能划几条线 + int[][] dp = new int[N][M]; + if (A[0] == B[0]) { + dp[0][0] = 1; + } + for (int j = 1; j < M; j++) { + dp[0][j] = A[0] == B[j] ? 1 : dp[0][j - 1]; + } + for (int i = 1; i < N; i++) { + dp[i][0] = A[i] == B[0] ? 1 : dp[i - 1][0]; + } + // 某个值(key),上次在A中出现的位置(value) + HashMap AvalueLastIndex = new HashMap<>(); + AvalueLastIndex.put(A[0], 0); + // 某个值(key),上次在B中出现的位置(value) + HashMap BvalueLastIndex = new HashMap<>(); + for (int i = 1; i < N; i++) { + AvalueLastIndex.put(A[i], i); + BvalueLastIndex.put(B[0], 0); + for (int j = 1; j < M; j++) { + BvalueLastIndex.put(B[j], j); + // 可能性1,就是不让A[i]去划线 + int p1 = dp[i - 1][j]; + // 可能性2,就是不让B[j]去划线 + int p2 = dp[i][j - 1]; + // 可能性3,就是要让A[i]去划线,那么如果A[i]==5,它跟谁划线? + // 贪心的点:一定是在B[0...j]中,尽量靠右侧的5 + int p3 = 0; + if (BvalueLastIndex.containsKey(A[i])) { + int last = BvalueLastIndex.get(A[i]); + p3 = (last > 0 ? dp[i - 1][last - 1] : 0) + 1; + } + // 可能性4,就是要让B[j]去划线,那么如果B[j]==7,它跟谁划线? + // 贪心的点:一定是在A[0...i]中,尽量靠右侧的7 + int p4 = 0; + if (AvalueLastIndex.containsKey(B[j])) { + int last = AvalueLastIndex.get(B[j]); + p4 = (last > 0 ? dp[last - 1][j - 1] : 0) + 1; + } + dp[i][j] = Math.max(Math.max(p1, p2), Math.max(p3, p4)); + } + BvalueLastIndex.clear(); + } + return dp[N - 1][M - 1]; + } + + // 但是其实这个题,不就是求两个数组的最长公共子序列吗? + public static int maxUncrossedLines2(int[] A, int[] B) { + if (A == null || A.length == 0 || B == null || B.length == 0) { + return 0; + } + int N = A.length; + int M = B.length; + int[][] dp = new int[N][M]; + dp[0][0] = A[0] == B[0] ? 1 : 0; + for (int j = 1; j < M; j++) { + dp[0][j] = A[0] == B[j] ? 1 : dp[0][j - 1]; + } + for (int i = 1; i < N; i++) { + dp[i][0] = A[i] == B[0] ? 1 : dp[i - 1][0]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + int p1 = dp[i - 1][j]; + int p2 = dp[i][j - 1]; + int p3 = A[i] == B[j] ? (1 + dp[i - 1][j - 1]) : 0; + dp[i][j] = Math.max(p1, Math.max(p2, p3)); + } + } + return dp[N - 1][M - 1]; + } + +} diff --git a/大厂刷题班/class52/Problem_0656_CoinPath.java b/大厂刷题班/class52/Problem_0656_CoinPath.java new file mode 100644 index 0000000..04439c5 --- /dev/null +++ b/大厂刷题班/class52/Problem_0656_CoinPath.java @@ -0,0 +1,67 @@ +package class52; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class Problem_0656_CoinPath { + + // arr 0 -> n-1 + // arr[i] = -1 死了! + public static int minCost(int[] arr, int jump) { + if (arr == null || arr.length == 0) { + return 0; + } + int n = arr.length; + if (arr[0] == -1 || arr[n - 1] == -1) { + return -1; + } + // dp[i] : 从0位置开始出发,到达i位置的最小代价 + int[] dp = new int[n]; + dp[0] = arr[0]; + for (int i = 1; i < n; i++) { + dp[i] = Integer.MAX_VALUE; + if (arr[i] != -1) { + for (int pre = Math.max(0, i - jump); pre < i; pre++) { + if (dp[pre] != -1) { + dp[i] = Math.min(dp[i], dp[pre] + arr[i]); + } + } + } + dp[i] = dp[i] == Integer.MAX_VALUE ? -1 : dp[i]; + } + return dp[n - 1]; + } + + public static List cheapestJump(int[] arr, int jump) { + int n = arr.length; + int[] best = new int[n]; + int[] last = new int[n]; + int[] size = new int[n]; + Arrays.fill(best, Integer.MAX_VALUE); + Arrays.fill(last, -1); + best[0] = 0; + for (int i = 0; i < n; i++) { + if (arr[i] != -1) { + for (int j = Math.max(0, i - jump); j < i; j++) { + if (arr[j] != -1) { + int cur = best[j] + arr[i]; + // 1) 代价低换方案! + // 2) 代价一样,但是点更多,换方案! + if (cur < best[i] || (cur == best[i] && size[i] - 1 < size[j])) { + best[i] = cur; + last[i] = j; + size[i] = size[j] + 1; + } + } + } + } + } + List path = new LinkedList<>(); + for (int cur = n - 1; cur >= 0; cur = last[cur]) { + path.add(0, cur + 1); + } + return path.get(0) != 1 ? new LinkedList<>() : path; + } + +} diff --git a/大厂刷题班/class52/Problem_0683_KEmptySlots.java b/大厂刷题班/class52/Problem_0683_KEmptySlots.java new file mode 100644 index 0000000..7d9b8d7 --- /dev/null +++ b/大厂刷题班/class52/Problem_0683_KEmptySlots.java @@ -0,0 +1,75 @@ +package class52; + +public class Problem_0683_KEmptySlots { + + public static int kEmptySlots1(int[] bulbs, int k) { + int n = bulbs.length; + int[] days = new int[n]; + for (int i = 0; i < n; i++) { + days[bulbs[i] - 1] = i + 1; + } + int ans = Integer.MAX_VALUE; + if (k == 0) { + for (int i = 1; i < n; i++) { + ans = Math.min(ans, Math.max(days[i - 1], days[i])); + } + } else { + int[] minq = new int[n]; + int l = 0; + int r = -1; + for (int i = 1; i < n && i < k; i++) { + while (l <= r && days[minq[r]] >= days[i]) { + r--; + } + minq[++r] = i; + } + for (int i = 1, j = k; j < n - 1; i++, j++) { + while (l <= r && days[minq[r]] >= days[j]) { + r--; + } + minq[++r] = j; + int cur = Math.max(days[i - 1], days[j + 1]); + if (days[minq[l]] > cur) { + ans = Math.min(ans, cur); + } + if (i == minq[l]) { + l++; + } + } + } + return (ans == Integer.MAX_VALUE) ? -1 : ans; + } + + public static int kEmptySlots2(int[] bulbs, int k) { + int n = bulbs.length; + int[] days = new int[n]; + for (int i = 0; i < n; i++) { + days[bulbs[i] - 1] = i + 1; + } + int ans = Integer.MAX_VALUE; + for (int left = 0, mid = 1, right = k + 1; right < n; mid++) { + // 验证指针mid + // mid 永远不和left撞上的! + // 1) mid在left和right中间验证的时候,没通过! + // 2) mid是撞上right的时候 + if (days[mid] <= Math.max(days[left], days[right])) { +// if(mid == right) { // left...right 达标的! +// int cur = Math.max(days[left], days[right]); +// ans = Math.min(ans, cur); +// left = mid; +// right = mid + k + 1; +// } else { // 验证不通过! +// left = mid; +// right = mid + k + 1; +// } + if (mid == right) { // 收答案! + ans = Math.min(ans, Math.max(days[left], days[right])); + } + left = mid; + right = mid + k + 1; + } + } + return (ans == Integer.MAX_VALUE) ? -1 : ans; + } + +} diff --git a/大厂刷题班/class52/Problem_1488_AvoidFloodInTheCity.java b/大厂刷题班/class52/Problem_1488_AvoidFloodInTheCity.java new file mode 100644 index 0000000..ac6ec55 --- /dev/null +++ b/大厂刷题班/class52/Problem_1488_AvoidFloodInTheCity.java @@ -0,0 +1,81 @@ +package class52; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.PriorityQueue; + +public class Problem_1488_AvoidFloodInTheCity { + + // rains[i] = j 第i天轮到j号湖泊下雨 + // 规定,下雨日,干啥 : -1 + // 不下雨日,如果没有湖泊可抽 : 1 + public static int[] avoidFlood(int[] rains) { + int n = rains.length; + int[] ans = new int[n]; + int[] invalid = new int[0]; + // key : 某个湖 + // value : 这个湖在哪些位置降雨 + // 4 : {3,7,19,21} + // 1 : { 13 } + // 2 : {4, 56} + HashMap> map = new HashMap<>(); + for (int i = 0; i < n; i++) { + if (rains[i] != 0) { // 第i天要下雨,rains[i] + // 3天 9号 + // 9号 { 3 } + // 9号 {1, 3} + if (!map.containsKey(rains[i])) { + map.put(rains[i], new LinkedList<>()); + } + map.get(rains[i]).addLast(i); + } + } + // 没抽干的湖泊表 + // 某个湖如果满了,加入到set里 + // 某个湖被抽干了,从set中移除 + HashSet set = new HashSet<>(); + // 这个堆的堆顶表示最先处理的湖是哪个 + PriorityQueue heap = new PriorityQueue<>(); + for (int i = 0; i < n; i++) { // 0 1 2 3 ... + if (rains[i] != 0) { + if (set.contains(rains[i])) { + return invalid; + } + // 放入到没抽干的表里 + set.add(rains[i]); + map.get(rains[i]).pollFirst(); + if (!map.get(rains[i]).isEmpty()) { + heap.add(new Work(rains[i], map.get(rains[i]).peekFirst())); + } + // 题目规定 + ans[i] = -1; + } else { // 今天干活! + if (heap.isEmpty()) { + ans[i] = 1; + } else { + Work cur = heap.poll(); + set.remove(cur.lake); + ans[i] = cur.lake; + } + } + } + return ans; + } + + public static class Work implements Comparable { + public int lake; + public int nextRain; + + public Work(int l, int p) { + lake = l; + nextRain = p; + } + + @Override + public int compareTo(Work o) { + return nextRain - o.nextRain; + } + } + +} diff --git a/算法周更班/class_2021_11_4_week/Code01_RetainTree.java b/算法周更班/class_2021_11_4_week/Code01_RetainTree.java new file mode 100644 index 0000000..f787c7b --- /dev/null +++ b/算法周更班/class_2021_11_4_week/Code01_RetainTree.java @@ -0,0 +1,89 @@ +package class_2021_11_4_week; + +import java.util.ArrayList; +import java.util.List; + +public class Code01_RetainTree { + + public static class Node { + // 值 + public int value; + // 是否保留 + public boolean retain; + // 下级节点 + public List nexts; + + public Node(int v, boolean r) { + value = v; + retain = r; + nexts = new ArrayList<>(); + } + } + + // 给定一棵树的头节点head + // 请按照题意,保留节点,没有保留的节点删掉 + // 树调整完之后,返回头节点 + public static Node retain(Node x) { + if (x.nexts.isEmpty()) { + return x.retain ? x : null; + } + // x下层有节点 + List newNexts = new ArrayList<>(); + for (Node next : x.nexts) { + Node newNext = retain(next); + if (newNext != null) { + newNexts.add(newNext); + } + } + // x.nexts 老的链表,下级节点 + // newNexts 新的链表,只有保留的在里面 + // + if (!newNexts.isEmpty() || x.retain) { + x.nexts = newNexts; + return x; + } + return null; + } + + // 先序打印 + public static void preOrderPrint(Node head) { + System.out.println(head.value); + for (Node next : head.nexts) { + preOrderPrint(next); + } + } + + public static void main(String[] args) { + Node n1 = new Node(1, false); + Node n2 = new Node(2, true); + Node n3 = new Node(3, false); + Node n4 = new Node(4, false); + Node n5 = new Node(5, false); + Node n6 = new Node(6, true); + Node n7 = new Node(7, true); + Node n8 = new Node(8, false); + Node n9 = new Node(9, false); + Node n10 = new Node(10, false); + Node n11 = new Node(11, false); + Node n12 = new Node(12, false); + Node n13 = new Node(13, true); + + n1.nexts.add(n2); + n1.nexts.add(n3); + n2.nexts.add(n4); + n2.nexts.add(n5); + n3.nexts.add(n6); + n3.nexts.add(n7); + n6.nexts.add(n8); + n6.nexts.add(n9); + n6.nexts.add(n10); + n7.nexts.add(n11); + n7.nexts.add(n12); + n9.nexts.add(n13); + + Node head = retain(n1); + preOrderPrint(head); + + } + +} diff --git a/算法周更班/class_2021_11_4_week/Code02_GuessNumberHigherOrLowerII.java b/算法周更班/class_2021_11_4_week/Code02_GuessNumberHigherOrLowerII.java new file mode 100644 index 0000000..acc1216 --- /dev/null +++ b/算法周更班/class_2021_11_4_week/Code02_GuessNumberHigherOrLowerII.java @@ -0,0 +1,107 @@ +package class_2021_11_4_week; + +// 测试链接 : https://leetcode.com/problems/guess-number-higher-or-lower-ii/ +public class Code02_GuessNumberHigherOrLowerII { + + // 正确的数字,在1~n之间 + // 每次猜错,花费就是你猜的数字 + // 返回:永远最倒霉的情况下,也能赢的胜利,所需要的最少钱数 + public static int minGold(int n) { + return zuo(1, n); + } + + // 目前锁定了,正确的数字,在L~R范围上,除了这个范围都不可能了! + // 返回,永远最倒霉的情况下,猜中这个数字,所需要的最少钱数 + public static int zuo(int L, int R) { + if (L == R) { + return 0; + } + if (L == R - 1) { + return L; + } + int min = Math.min(L + zuo(L + 1, R), R + zuo(L, R - 1)); + for (int i = L + 1; i < R; i++) { + min = Math.min(min, i + Math.max(zuo(L, i - 1), zuo(i + 1, R))); + } + return min; + } + + public static int minGold2(int n) { + + // L -> 1~n + // R -> 1~n + int[][] dp = new int[n + 1][n + 1]; + // 因为初始化都是0,所以dp的对角线,不用填了 + for (int i = 1; i < n; i++) { + dp[i][i + 1] = i; + } + for (int L = n - 2; L >= 1; L--) { + for (int R = L + 2; R <= n; R++) { + int min = Math.min(L + dp[L + 1][R], R + dp[L][R - 1]); + for (int i = L + 1; i < R; i++) { + min = Math.min(min, i + Math.max(dp[L][i - 1], dp[i + 1][R])); + } + dp[L][R] = min; + } + } + return dp[1][n]; + } + + // 暴力递归 + public static int getMoneyAmount1(int n) { + return process1(1, n); + } + + // 假设现在在L ~ R的范围上, 猜数字 + // 返回:确保获胜的最小现金数,不管答案是哪个数字 + // 注意:所谓的“确保获胜”,以及“不管答案是哪个数字”,意味着你每次永远面临猜错的最差情况! + public static int process1(int L, int R) { + // 说明L~R范围,只剩下一个数字了,那不用猜了,获胜了 + if (L == R) { + return 0; + } + // 说明L~R范围,只剩下两个数字了 + // 比如: 5 6 + // 假设永远会遇到最差情况, + // 那么当然猜5,因为最差情况下,也只需要耗费5的代价,然后就知道了答案是6 + // 不能猜6,因为最差情况下,需要耗费6的代价,然后才知道答案是5 + // 所以当然选代价低的!请深刻理解:每次永远面临猜错的最差情况! + if (L == R - 1) { + return L; + } + // 如果说明L~R范围,不仅仅两个数字 + // 比如:5 6 7 8 9 + // 首先尝试5,如果最差情况出现,代价为:5 + 6~9范围上的尝试 + // 最后尝试9,如果最差情况出现,代价为:9 + 5~8范围上的尝试 + int ans = Math.min(L + process1(L + 1, R), R + process1(L, R - 1)); + // 进而尝试6,如果最差情况出现,代价为:6 + Max { 5~5范围上的尝试 ,7~9范围上的尝试} + // 这是因为猜了6,会告诉你,猜高了还是猜低了,所以左右两侧的待定范围,一定会只走一侧 + // 又因为永远会遇到最差情况,所以,一定会走最难受的那一侧,所以是 Max { 5~5范围上的尝试 ,7~9范围上的尝试} + // 进而尝试7,如果最差情况出现,代价为:7 + Max { 5~6范围上的尝试 ,8~9范围上的尝试} + // 进而尝试8,如果最差情况出现,代价为:8 + Max { 5~7范围上的尝试 ,9~9范围上的尝试} + // 所有尝试中,取代价最小值 + for (int M = L + 1; M < R; M++) { + ans = Math.min(ans, M + Math.max(process1(L, M - 1), process1(M + 1, R))); + } + return ans; + } + + // 上面的暴力递归改动态规划 + // 提交到leetcode上可以直接通过 + public static int getMoneyAmount2(int n) { + int[][] dp = new int[n + 1][n + 1]; + for (int i = 1; i < n; i++) { + dp[i][i + 1] = i; + } + for (int L = n - 2; L >= 1; L--) { + for (int R = L + 2; R <= n; R++) { + dp[L][R] = Math.min(L + dp[L + 1][R], R + dp[L][R - 1]); + for (int M = L + 1; M < R; M++) { + dp[L][R] = Math.min(dp[L][R], M + Math.max(dp[L][M - 1], dp[M + 1][R])); + } + } + } + return dp[1][n]; + } + +} diff --git a/算法周更班/class_2021_11_4_week/Code03_StartToEndBinaryOneTarget.java b/算法周更班/class_2021_11_4_week/Code03_StartToEndBinaryOneTarget.java new file mode 100644 index 0000000..fb477d1 --- /dev/null +++ b/算法周更班/class_2021_11_4_week/Code03_StartToEndBinaryOneTarget.java @@ -0,0 +1,337 @@ +package class_2021_11_4_week; + +import java.util.Arrays; + +// 来自真实面试,同学给我的问题 +// 限制:0 <= start <= end,0 <= target <= 64 +// [start,end]范围上的数字,有多少数字二进制中1的个数等于target +public class Code03_StartToEndBinaryOneTarget { + + // 暴力方法 + // 为了验证 + public static long nums1(long start, long end, int target) { + if (start < 0 || end < 0 || start > end || target < 0) { + return -1; + } + long ans = 0; + for (long i = start; i <= end; i++) { + if (bitOne(i) == target) { + ans++; + } + } + return ans; + } + + public static int bitOne(long num) { + int bits = 0; + for (int i = 63; i >= 0; i--) { + if ((num & (1L << i)) != 0) { + bits++; + } + } + return bits; + } + + // 正式方法 + // 递归展示其思路,但是不做动态规划的优化 + public static long nums2(long start, long end, int target) { + if (start < 0 || end < 0 || start > end || target < 0) { + return -1; + } + if (start == 0 && end == 0) { + return target == 0 ? 1 : 0; + } + // 寻找end这数,最高位的1在哪? + int ehigh = 62; + while ((end & (1L << ehigh)) == 0) { + ehigh--; + } + if (start == 0) { + return process2(ehigh, 0, target, end); + } else { // 170 ~ 3657 0 ~ 169 0 ~ 3657 + start--; + int shigh = 62; + while (shigh >= 0 && (start & (1L << shigh)) == 0) { + shigh--; + } + return process2(ehigh, 0, target, end) - process2(shigh, 0, target, start); + } + } + // 11 10 9 8 7 6 5 4 3 2 1 0 + // num : 0 1 1 0 0 1 1 0 0 1 0 1 0 + // 1..... + // 0..... + + // 如果num最高位的1在i位,也就是说num[i...0]才有意义,比i再高的位置都是0 + // 那么从第i位开始做决定,依次往低位进行决定 + // index : 当前来到哪一位做决定,index在i~0之间,从高到低 + // less : 之间做的决定是不是已经比num小了 + // rest : 还剩几个1需要出现 + // long process(index, less, rest, num)含义 : + // [i...index+1]上面已经做过决定了,接下来要在[index...0]上面做决定 + // 在[i...index+1]上面的决定是不是比num[i...index+1]小, + // 如果是,less = 1 + // 如果还没有,一定说明之前的决定==num[i...index+1],此时less = 0 + // 在[index...0]上面做决定的过程中一定要出现rest个1 + // 返回[index...0]上,有多少合法的决定 + // 这个方法可以改动态规划,因为:index范围62~0, less范围0或者1,rest范围0~target + // 自己改动态规划吧 + // 只有index、less、rest这三个有效可变参数、num是固定参数 + // 所以可以改成三维动态规划 + // + // num [h....index+1] 决定完了,需要保证之前做的决定,不能比num大, + // 1) 之前做的决定 已经小于 num所对应的前缀状态了, + // 2) 之前做的决定 等于 num所对应的前缀状态了, + // index..... 去做决定吧! + // less == 1 之前做的决定 已经小于 num所对应的前缀状态了 + // less == 0 之前做的决定 等于 num所对应的前缀状态 + // 剩余几个1,需要出现! + // [index.....] + // index -> 62~0 63种变化 + // less -> 0 1 2种变化 + // rest -> 0 64种变化 + // 63 * 2 * 64 + public static long process2(int index, int less, int rest, long num) { + if (rest > index + 1) { + return 0; + } + // rest <= index + 1 + if (rest == 0) { + return 1L; + } + // 0 < rest <= index + 1 + // 还有1需要去消耗 + // 位数也够用 + if (less == 1) { // less == 1 之前做的决定 已经小于 num所对应的前缀状态了 + if (rest == index + 1) { + return 1; + } else { + // 后面剩余的位数 > 需要消耗掉1的数量的! + // 某些位置填1,某些位置填0 + // index 0 1 + // index 0 + // process2(index - 1, 1, rest, num ); + // index 1 + // process2(index - 1, 1, rest - 1, num); + return process2(index - 1, 1, rest - 1, num) + process2(index - 1, 1, rest, num); + } + } else { // less == 0, 之前做的决定 等于 num所对应的前缀状态的 + if (rest == index + 1) { // 后面剩余的位数,必须都填1,才能消耗完 + // index + // 1 1 1 1 1 1 1 1 1 + // num + // 1 + // index 1 + // + // num 111111111 1 + return (num & (1L << index)) == 0 ? 0 : process2(index - 1, 0, rest - 1, num); + } else { + // less == 0, 之前做的决定 等于 num所对应的前缀状态的 + // 后面剩余的位数 > 需要消耗掉1的数量的! + // 某些位置填1,某些位置填0 + if ((num & (1L << index)) == 0) { + return process2(index - 1, 0, rest, num); + } else { // num 当前位置 1 + // index 1 + // index 0 + return process2(index - 1, 0, rest - 1, num) + process2(index - 1, 1, rest, num); + } + } + } + } + + // 最优解方法 + // 方法二的思路 + 动态规划 + public static long nums3(long start, long end, int target) { + if (start < 0 || end < 0 || start > end || target < 0) { + return -1; + } + if (start == 0 && end == 0) { + return target == 0 ? 1 : 0; + } + int ehigh = 62; + while ((end & (1L << ehigh)) == 0) { + ehigh--; + } + long[][][] dpe = new long[ehigh + 1][2][target + 1]; + for (int i = 0; i <= ehigh; i++) { + Arrays.fill(dpe[i][0], -1); + Arrays.fill(dpe[i][1], -1); + } + long anse = process3(ehigh, 0, target, end, dpe); + if (start == 0) { + return anse; + } else { + start--; + int shigh = 62; + while (shigh >= 0 && (start & (1L << shigh)) == 0) { + shigh--; + } + long[][][] dps = new long[shigh + 1][2][target + 1]; + for (int i = 0; i <= shigh; i++) { + Arrays.fill(dps[i][0], -1); + Arrays.fill(dps[i][1], -1); + } + long anss = process3(shigh, 0, target, start, dps); + return anse - anss; + } + } + + // dp 傻缓存 + // dp 63 * 2 * 64 + // 一定能装下所有的解! + // index+less+rest + // dp[index][less][rest] 请直接拿数据 + // dp[index][less][rest] == -1 表示 没算过,去算! + // dp[index][less][rest] != -1 表示 算过!结果就是dp[index][less][rest] + public static long process3(int index, int less, int rest, long num, long[][][] dp) { + if (rest > index + 1) { + return 0; + } + if (rest == 0) { + return 1L; + } + if (dp[index][less][rest] != -1) { + return dp[index][less][rest]; + } + // 没算过! + long ans = 0; + if (less == 1) { + if (rest == index + 1) { + ans = 1; + } else { + ans = process3(index - 1, 1, rest - 1, num, dp) + process3(index - 1, 1, rest, num, dp); + } + } else { + if (rest == index + 1) { + ans = (num & (1L << index)) == 0 ? 0 : process3(index - 1, 0, rest - 1, num, dp); + } else { + if ((num & (1L << index)) == 0) { + ans = process3(index - 1, 0, rest, num, dp); + } else { + ans = process3(index - 1, 0, rest - 1, num, dp) + process3(index - 1, 1, rest, num, dp); + } + } + } + dp[index][less][rest] = ans; + return ans; + } + + // 利用排列组合的方法 + public static long nums4(long start, long end, int target) { + if (start < 0 || end < 0 || start > end || target < 0) { + return -1; + } + long anse = process4(63, target, end); + if (start == 0) { + return anse; + } else { + long anss = process4(63, target, start - 1); + return anse - anss; + } + } + + public static long process4(int index, int rest, long num) { + if (rest > index + 1) { + return 0; + } + if (rest == 0) { + return 1; + } + if ((num & (1L << index)) == 0) { + return process4(index - 1, rest, num); + } else { + return c(index, rest) + process4(index - 1, rest - 1, num); + } + } + + // 求C(N,A)的解 + // N! / (A! * (N - A)!) + // 即 : (A+1 * A+2 * ... * N) / (2 * 3 * 4 * (N-A)) + // 为了不溢出,每一步求一个最大公约数,然后消掉 + public static long c(long n, long a) { + if (n < a) { + return 0L; + } + long up = 1L; + long down = 1L; + for (long i = a + 1, j = 2; i <= n || j <= n - a;) { + if (i <= n) { + up *= i++; + } + if (j <= n - a) { + down *= j++; + } + long gcd = gcd(up, down); + up /= gcd; + down /= gcd; + } + return up / down; + } + + // 求m和n的最大公约数 + public static long gcd(long m, long n) { + return n == 0 ? m : gcd(n, m % n); + } + + public static void main(String[] args) { + long range = 600L; + System.out.println("功能测试开始"); + for (long start = 0L; start < range; start++) { + for (long end = start; end < range; end++) { + int target = (int) (Math.random() * 10); + long ans1 = nums1(start, end, target); + long ans2 = nums2(start, end, target); + long ans3 = nums3(start, end, target); + long ans4 = nums4(start, end, target); + if (ans1 != ans2 || ans1 != ans3 || ans1 != ans4) { + System.out.println("出错了!"); + } + } + } + System.out.println("功能测试结束"); + + long start = 33281731L; + long end = 204356810L; + int target = 17; + long startTime; + long endTime; + long ans1; + long ans2; + long ans3; + long ans4; + + System.out.println("大范围性能测试,开始"); + startTime = System.currentTimeMillis(); + ans1 = nums1(start, end, target); + endTime = System.currentTimeMillis(); + System.out.println("方法一答案:" + ans1 + ", 运行时间(毫秒) : " + (endTime - startTime)); + startTime = System.currentTimeMillis(); + ans2 = nums2(start, end, target); + endTime = System.currentTimeMillis(); + System.out.println("方法二答案:" + ans2 + ", 运行时间(毫秒) : " + (endTime - startTime)); + startTime = System.currentTimeMillis(); + ans3 = nums3(start, end, target); + endTime = System.currentTimeMillis(); + System.out.println("方法三答案:" + ans3 + ", 运行时间(毫秒) : " + (endTime - startTime)); + ans4 = nums4(start, end, target); + endTime = System.currentTimeMillis(); + System.out.println("方法四答案:" + ans4 + ", 运行时间(毫秒) : " + (endTime - startTime)); + System.out.println("大范围性能测试,结束"); + + System.out.println("超大范围性能测试,开始"); + start = 88193819381L; + end = 92371283713182371L; + target = 30; + startTime = System.currentTimeMillis(); + ans3 = nums3(start, end, target); + endTime = System.currentTimeMillis(); + System.out.println("方法三答案:" + ans3 + ", 运行时间(毫秒) : " + (endTime - startTime)); + startTime = System.currentTimeMillis(); + ans4 = nums4(start, end, target); + endTime = System.currentTimeMillis(); + System.out.println("方法四答案:" + ans4 + ", 运行时间(毫秒) : " + (endTime - startTime)); + System.out.println("超大范围性能测试,结束"); + } + +} diff --git a/算法周更班/class_2021_12_1_week/Code01_XtoYMinDistance.java b/算法周更班/class_2021_12_1_week/Code01_XtoYMinDistance.java new file mode 100644 index 0000000..d0aca05 --- /dev/null +++ b/算法周更班/class_2021_12_1_week/Code01_XtoYMinDistance.java @@ -0,0 +1,171 @@ +package class_2021_12_1_week; + +import java.util.PriorityQueue; + +// 来自真实面试,同学给我的问题 +public class Code01_XtoYMinDistance { + + // 暴力方法 + // dfs尝试所有情况 + // 没有优化,就是纯暴力 + public static int minDistance1(int n, int[][] roads, int x, int y) { + // 第一步生成邻接矩阵 + int[][] map = new int[n + 1][n + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + map[i][j] = Integer.MAX_VALUE; + } + } + for (int[] road : roads) { + map[road[0]][road[1]] = Math.min(map[road[0]][road[1]], road[2]); + map[road[1]][road[0]] = Math.min(map[road[1]][road[0]], road[2]); + } + boolean[] visited = new boolean[n + 1]; + return process(x, y, n, map, visited); + } + + // 当前来到的城市是cur,最终目的地是aim,一共有1~n这些城市 + // 所有城市之间的距离都在map里 + // 之前已经走过了哪些城市都记录在了visited里面,请不要重复经过 + // 返回从cur到aim所有可能的路里,最小距离是多少 + public static int process(int cur, int aim, int n, int[][] map, boolean[] visited) { + if (visited[cur]) { + return Integer.MAX_VALUE; + } + if (cur == aim) { + return 0; + } + visited[cur] = true; + int ans = Integer.MAX_VALUE; + for (int next = 1; next <= n; next++) { + if (next != cur && map[cur][next] != Integer.MAX_VALUE) { + int rest = process(next, aim, n, map, visited); + if (rest != Integer.MAX_VALUE) { + ans = Math.min(ans, map[cur][next] + rest); + } + } + } + visited[cur] = false; + return ans; + } + + // Dijkstra的解 + // n是城市数量 + // 城市编号:1 ~ n 0弃而不用 + public static int minDistance2(int n, int[][] roads, int x, int y) { + // 第一步生成邻接矩阵 + int[][] map = new int[n + 1][n + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + map[i][j] = Integer.MAX_VALUE; + } + } + // 建立路! + for (int[] road : roads) { + map[road[0]][road[1]] = Math.min(map[road[0]][road[1]], road[2]); + map[road[1]][road[0]] = Math.min(map[road[1]][road[0]], road[2]); + } + // computed[i] = true,表示从源出发点到i这个城市,已经计算出最短距离了 + // computed[i] = false,表示从源出发点到i这个城市,还没有计算出最短距离 + boolean[] computed = new boolean[n + 1]; + // 距离小根堆 + PriorityQueue heap = new PriorityQueue<>((a, b) -> (a.pathSum - b.pathSum)); + heap.add(new Node(x, 0)); + while (!heap.isEmpty()) { + // x -> ... -> 当前的城市, 有距离 + Node cur = heap.poll(); + if (computed[cur.city]) { + continue; + } + // 没算过 + // 开始算! + if (cur.city == y) { + return cur.pathSum; + } + computed[cur.city] = true; + for (int next = 1; next <= n; next++) { + if (next != cur.city && map[cur.city][next] != Integer.MAX_VALUE && !computed[next]) { + heap.add(new Node(next, cur.pathSum + map[cur.city][next])); + } + } + } + return Integer.MAX_VALUE; + } + + // 当前来到的Node,注意这不是城市的意思,这是就是一个普通的封装类 + // Node封装了,当前来到的城市是什么,以及,从源出发点到这个城市的路径和是多少? + public static class Node { + // 当前来到的城市编号 + public int city; + // 从源出发点到这个城市的路径和 + public int pathSum; + + public Node(int c, int p) { + city = c; + pathSum = p; + } + } + + // 为了测试 + // 城市1~n + // 随机生成m条道路 + // 每一条路的距离,在1~v之间 + public static int[][] randomRoads(int n, int m, int v) { + int[][] roads = new int[m][3]; + for (int i = 0; i < m; i++) { + int from = (int) (Math.random() * n) + 1; + int to = (int) (Math.random() * n) + 1; + int distance = (int) (Math.random() * v) + 1; + roads[i] = new int[] { from, to, distance }; + } + return roads; + } + + // 为了测试 + public static void main(String[] args) { + // 城市数量n,下标从1开始,不从0开始 + int n = 4; + // 边的数量m,m的值不能大于n * (n-1) / 2 + int m = 4; + // 所的路有m条 + // [a,b,c]表示a和b之间有路,距离为3,根据题意,本题中的边都是无向边 + // 假设有两条路 + // [1,3,7],这条路是从1到3,距离是7 + // [1,3,4],这条路是从1到3,距离是4 + // 那么应该忽略[1,3,7],因为[1,3,4]比它好 + int[][] roads = new int[m][3]; + roads[0] = new int[] { 1, 2, 4 }; + roads[1] = new int[] { 1, 3, 1 }; + roads[2] = new int[] { 1, 4, 1 }; + roads[3] = new int[] { 2, 3, 1 }; + // 求从x到y的最短距离是多少,x和y应该在[1,n]之间 + int x = 2; + int y = 4; + + // 暴力方法的解 + System.out.println(minDistance1(n, roads, x, y)); + + // Dijkstra的解 + System.out.println(minDistance2(n, roads, x, y)); + + // 下面开始随机验证 + int cityMaxSize = 12; + int pathMax = 30; + int testTimes = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + n = (int) (Math.random() * cityMaxSize) + 1; + m = (int) (Math.random() * n * (n - 1) / 2) + 1; + roads = randomRoads(n, m, pathMax); + x = (int) (Math.random() * n) + 1; + y = (int) (Math.random() * n) + 1; + int ans1 = minDistance1(n, roads, x, y); + int ans2 = minDistance2(n, roads, x, y); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2021_12_1_week/Code02_4KeysKeyboard.java b/算法周更班/class_2021_12_1_week/Code02_4KeysKeyboard.java new file mode 100644 index 0000000..7b37442 --- /dev/null +++ b/算法周更班/class_2021_12_1_week/Code02_4KeysKeyboard.java @@ -0,0 +1,38 @@ +package class_2021_12_1_week; + +// 测试链接 : https://leetcode.com/problems/4-keys-keyboard/ +public class Code02_4KeysKeyboard { + + // 可以证明: + // 来到i的时候,包括i在内最多有连续4次粘贴行为 + // 不可能更多,如果有连续5次粘贴,一定就不再是最优解 + // 假设开始时,A的数量为S,看如下的变化过程,我们称这是行为一: + // 开始 全选 复制(粘贴板S个A) 粘贴 粘贴 粘贴 粘贴 粘贴 + // S S S 2*S 3*S 4*S 5*S 6*S + // 但是,注意看如下的行为二: + // 开始 全选 复制(粘贴板S个A) 粘贴 全选 复制(粘贴板2S个A) 粘贴 粘贴 + // S S S 2*S 2*S 2*S 4*S 6*S + // 行为一,经历8步,最后是6*S个A + // 行为二,经历8步,最后是6*S个A + // 但是行为二在粘贴板上有2S个A,而行为一在粘贴板上有S个A + // 所以行为一没有行为二优 + // 以此说明:来到i的时候,包括i在内最多有连续4次粘贴行为 + // 那么就尝试:连续1次、连续2次、连续3次、连续4次粘贴行为即可 + public static int maxA(int n) { + // dp[0] 1步以内的最优解 + // dp[1] 2步以内的最优解 + // dp[2] 3步以内的最优解 + // dp[i] i+1步以内的最优解 + int[] dp = new int[n]; + for (int i = 0; i < 6 && i < n; i++) { + dp[i] = i + 1; + } + for (int i = 6; i < n; i++) { + dp[i] = Math.max( + Math.max(dp[i - 3] * 2, dp[i - 4] * 3), + Math.max(dp[i - 5] * 4, dp[i - 6] * 5)); + } + return dp[n - 1]; + } + +} diff --git a/算法周更班/class_2021_12_1_week/Code03_RedundantConnectionII.java b/算法周更班/class_2021_12_1_week/Code03_RedundantConnectionII.java new file mode 100644 index 0000000..97ea609 --- /dev/null +++ b/算法周更班/class_2021_12_1_week/Code03_RedundantConnectionII.java @@ -0,0 +1,102 @@ +package class_2021_12_1_week; + +// 来自微软,真正考的时候阉割了难度 +// 测试链接 : https://leetcode.com/problems/redundant-connection-ii/ +public class Code03_RedundantConnectionII { + + // [ + // [1 , 5] 1 > 5 + // [7 , 3] 7 > 3 + + + // ] + // 边的数量 = 点的数量! + public static int[] findRedundantDirectedConnection(int[][] edges) { + // N是点的数量 + // 点的编号,1~N,没有0 + int N = edges.length; + // 并查集!N个点,去初始化,每个点各自是一个集合 + UnionFind uf = new UnionFind(N); + // pre[i] = 0 来到i节点是第一次 + // pre[i] = 6 之前来过i,是从6来的! + int[] pre = new int[N + 1]; + + // 如果,没有入度为2的点, + // first second 都维持是null + // 如果,有入度为2的点,那么也只可能有一个 + // 比如入度为2的点,是5 + // first = [3,5] + // second = [12,5] + int[] first = null; + int[] second = null; + // 有没有环!非常不单纯!含义复杂! + int[] circle = null; + for (int i = 0; i < N; i++) { // 遍历每条边! + int from = edges[i][0]; + int to = edges[i][1]; + if (pre[to] != 0) { // 不止一次来过to! + first = new int[] { pre[to], to }; + second = edges[i]; + } else { // 第一次到达to, + pre[to] = from; + if (uf.same(from, to)) { + circle = edges[i]; + } else { + uf.union(from, to); + } + } + } + // 重点解析!这是啥??? + // first != null + // 有入度为2的点! + return first != null ? (circle != null ? first : second) : circle; + } + + public static class UnionFind { + private int[] f; + private int[] s; + private int[] h; + + public UnionFind(int N) { + f = new int[N + 1]; + s = new int[N + 1]; + h = new int[N + 1]; + for (int i = 0; i <= N; i++) { + f[i] = i; + s[i] = 1; + } + } + + private int find(int i) { + int hi = 0; + while (i != f[i]) { + h[hi++] = i; + i = f[i]; + } + while (hi > 0) { + f[h[--hi]] = i; + } + return i; + } + + public boolean same(int i, int j) { + return find(i) == find(j); + } + + public void union(int i, int j) { + int fi = find(i); + int fj = find(j); + if (fi != fj) { + if (s[fi] >= s[fj]) { + f[fj] = fi; + s[fi] = s[fi] + s[fj]; + } else { + f[fi] = fj; + s[fj] = s[fi] + s[fj]; + } + } + } + + } + +} diff --git a/算法周更班/class_2021_12_2_week/Code01_FindAllPeopleWithSecret.java b/算法周更班/class_2021_12_2_week/Code01_FindAllPeopleWithSecret.java new file mode 100644 index 0000000..e59d154 --- /dev/null +++ b/算法周更班/class_2021_12_2_week/Code01_FindAllPeopleWithSecret.java @@ -0,0 +1,102 @@ +package class_2021_12_2_week; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +// 链接测试 : https://leetcode.com/problems/find-all-people-with-secret/ +public class Code01_FindAllPeopleWithSecret { + + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + // 0~n-1号专家,各自建立小集合 + // (0, firstPerson)合在一起,作为知道秘密的集合 + UnionFind uf = new UnionFind(n, firstPerson); + int m = meetings.length; + Arrays.sort(meetings, (a, b) -> a[2] - b[2]); + // [1,7,1] [2,4,2] [3,6,2] + // 1,7 2,4 3,6 + int[] help = new int[m << 1]; + help[0] = meetings[0][0]; + help[1] = meetings[0][1]; + int size = 2; + for (int i = 1; i < m; i++) { + // i 2 + if (meetings[i][2] != meetings[i - 1][2]) { + share(help, size, uf); + help[0] = meetings[i][0]; + help[1] = meetings[i][1]; + size = 2; + } else { + help[size++] = meetings[i][0]; + help[size++] = meetings[i][1]; + } + } + share(help, size, uf); + List ans = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (uf.know(i)) { + ans.add(i); + } + } + return ans; + } + + public static void share(int[] help, int size, UnionFind uf) { + for (int i = 0; i < size; i += 2) { + uf.union(help[i], help[i + 1]); + } + for (int i = 0; i < size; i++) { + if (!uf.know(help[i])) { + uf.isolate(help[i]); + } + } + } + + public static class UnionFind { + public int[] father; + public boolean[] sect; + public int[] help; + + public UnionFind(int n, int first) { + father = new int[n]; + sect = new boolean[n]; + help = new int[n]; + for (int i = 1; i < n; i++) { + father[i] = i; + } + father[first] = 0; + sect[0] = true; + } + + private int find(int i) { + int hi = 0; + while (i != father[i]) { + help[hi++] = i; + i = father[i]; + } + for (hi--; hi >= 0; hi--) { + father[help[hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int fatheri = find(i); + int fatherj = find(j); + if (fatheri != fatherj) { + father[fatherj] = fatheri; + sect[fatheri] |= sect[fatherj]; + } + } + + public boolean know(int i) { + return sect[find(i)]; + } + + public void isolate(int i) { + father[i] = i; + } + + } + +} diff --git a/算法周更班/class_2021_12_2_week/Code02_AwayFromBlackHole.java b/算法周更班/class_2021_12_2_week/Code02_AwayFromBlackHole.java new file mode 100644 index 0000000..709dcd8 --- /dev/null +++ b/算法周更班/class_2021_12_2_week/Code02_AwayFromBlackHole.java @@ -0,0 +1,122 @@ +package class_2021_12_2_week; + +// 来自美团 +// 所有黑洞的中心点记录在holes数组里 +// 比如[[3,5] [6,9]]表示,第一个黑洞在(3,5),第二个黑洞在(6,9) +// 并且所有黑洞的中心点都在左下角(0,0),右上角(x,y)的区域里 +// 飞船一旦开始进入黑洞,就会被吸进黑洞里 +// 返回: +// 如果统一所有黑洞的半径,最大半径是多少,依然能保证飞船从(0,0)能到达(x,y) +// 1000 1000*1000 10^6 * 二分 +public class Code02_AwayFromBlackHole { + + public static int maxRadius(int[][] holes, int x, int y) { + int L = 1; + int R = Math.max(x, y); + int ans = 0; + while (L <= R) { + int M = (L + R) / 2; + if (ok(holes, M, x, y)) { + ans = M; + L = M + 1; + } else { + R = M - 1; + } + } + return ans; + } + + public static boolean ok(int[][] holes, int r, int x, int y) { + int n = holes.length; + UnionFind uf = new UnionFind(holes, n, r); + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + if (touch(holes[i][0], holes[i][1], holes[j][0], holes[j][1], r)) { + uf.union(i, j); + } + if (uf.block(i, x, y)) { + return false; + } + } + } + return true; + } + + public static boolean touch(int x1, int y1, int x2, int y2, int r) { + return (r << 1) >= Math.sqrt((Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2))); + } + + public static class UnionFind { + public int[] father; + public int[] size; + public int[] xmin; + public int[] xmax; + public int[] ymin; + public int[] ymax; + public int[] help; + + public UnionFind(int[][] holes, int n, int r) { + father = new int[n]; + size = new int[n]; + xmin = new int[n]; + xmax = new int[n]; + ymin = new int[n]; + ymax = new int[n]; + help = new int[n]; + for (int i = 0; i < n; i++) { + father[i] = i; + size[i] = 1; + xmin[i] = holes[i][0] - r; + xmax[i] = holes[i][0] + r; + ymin[i] = holes[i][1] - r; + ymax[i] = holes[i][1] + r; + } + } + + private int find(int i) { + int hi = 0; + while (i != father[i]) { + help[hi++] = i; + i = father[i]; + } + for (hi--; hi >= 0; hi--) { + father[help[hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int fatheri = find(i); + int fatherj = find(j); + if (fatheri != fatherj) { + int sizei = size[fatheri]; + int sizej = size[fatherj]; + int big = sizei >= sizej ? fatheri : fatherj; + int small = big == fatheri ? fatherj : fatheri; + father[small] = big; + size[big] = sizei + sizej; + xmin[big] = Math.min(xmin[big], xmin[small]); + xmax[big] = Math.max(xmax[big], xmax[small]); + ymin[big] = Math.min(ymin[big], ymin[small]); + ymax[big] = Math.max(ymax[big], ymax[small]); + } + } + + public boolean block(int i, int x, int y) { + i = find(i); + return (xmin[i] <= 0 && xmax[i] >= x) + || (ymin[i] <= 0 && ymax[i] >= y) + || (xmin[i] <= 0 && ymin[i] <= 0) + || (xmax[i] >= x && ymax[i] >= y); + } + + } + + public static void main(String[] args) { + int[][] holes = { { 1, 2 }, { 4, 4 }, { 3, 0 }, { 5, 2 } }; + int x = 4; + int y = 6; + System.out.println(maxRadius(holes, x, y)); + } + +} diff --git a/算法周更班/class_2021_12_2_week/Code03_MagicSum.java b/算法周更班/class_2021_12_2_week/Code03_MagicSum.java new file mode 100644 index 0000000..5105972 --- /dev/null +++ b/算法周更班/class_2021_12_2_week/Code03_MagicSum.java @@ -0,0 +1,301 @@ +package class_2021_12_2_week; + +import java.util.Arrays; + +// 来自真实面试,同学给我的问题 +// arr数组长度为n, magic数组长度为m +// 含义: +// arr = { 3, 1, 4, 5, 7 } +// 如果完全不改变arr中的值,那么收益就是累加和 = 3 + 1 + 4 + 5 + 7 = 20 +// magics = { +// {0,2,5} 表示arr[0~2]中的任何一个值,都能改成5 +// {3,4,3} 表示arr[3~4]中的任何一个值,都能改成3 +// {1,3,7} 表示arr[1~3]中的任何一个值,都能改成7 +// } +// 就是说,magics中的任何一组数据{a,b,c},都表示一种操作, +// 你可以选择arr[a~b]中任何一个数字,变成c。 +// 并且每一种操作,都可以执行任意次 +// 其中 0 <= a <= b < n +// 那么经过若干次的魔法操作,你当然可能得到arr的更大的累加和 +// 返回arr尽可能大的累加和 +// n <= 10^7 +// m <= 10^6 +// arr中的值和c的范围 <= 10^12 +public class Code03_MagicSum { + + // 暴力解,写出来为了验证正式方法而已 + public static int maxSum1(int[] arr, int[][] magics) { + int[] help = Arrays.copyOf(arr, arr.length); + for (int[] m : magics) { + int l = m[0]; + int r = m[1]; + int c = m[2]; + for (int i = l; i <= r; i++) { + help[i] = Math.max(help[i], c); + } + } + int sum = 0; + for (int num : help) { + sum += num; + } + return sum; + } + + // O(N) + O(M * logM) + O(M * logN) + O(N * logN) + public static int maxSum2(int[] arr, int[][] magics) { + int n = arr.length; + // 线段树里的下标,从1开始,不从0开始! + SegmentTree2 st = new SegmentTree2(n); + Arrays.sort(magics, (a, b) -> (a[2] - b[2])); + for (int[] magic : magics) { + st.update(magic[0] + 1, magic[1] + 1, magic[2], 1, n, 1); + } + int ans = 0; + for (int i = 0; i < n; i++) { + ans += Math.max(st.query(i + 1, i + 1, 1, n, 1), arr[i]); + } + return ans; + } + + // 这是一棵普通的线段树 + // 区间上维持最大值的线段树 + // 支持区间值更新 + // 支持区间最大值查询 + public static class SegmentTree2 { + private int[] max; + private int[] change; + private boolean[] update; + + public SegmentTree2(int size) { + int N = size + 1; + max = new int[N << 2]; + change = new int[N << 2]; + update = new boolean[N << 2]; + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + private void pushDown(int rt, int ln, int rn) { + if (update[rt]) { + update[rt << 1] = true; + update[rt << 1 | 1] = true; + change[rt << 1] = change[rt]; + change[rt << 1 | 1] = change[rt]; + max[rt << 1] = change[rt]; + max[rt << 1 | 1] = change[rt]; + update[rt] = false; + } + } + + public void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + update[rt] = true; + change[rt] = C; + max[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return max[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int left = 0; + int right = 0; + if (L <= mid) { + left = query(L, R, l, mid, rt << 1); + } + if (R > mid) { + right = query(L, R, mid + 1, r, rt << 1 | 1); + } + return Math.max(left, right); + } + + } + + // O(N) + O(M * logM) + O(M * logN) + O(N) + public static int maxSum3(int[] arr, int[][] magics) { + int n = arr.length; + SegmentTree3 st = new SegmentTree3(n); + Arrays.sort(magics, (a, b) -> (a[2] - b[2])); + for (int[] magic : magics) { + st.update(magic[0] + 1, magic[1] + 1, magic[2], 1, n, 1); + } + int ans = 0; + int[] query = st.buildSingleQuery(n); + for (int i = 0; i < n; i++) { + ans += Math.max(query[i], arr[i]); + } + return ans; + } + + // 为方法三特别定制的线段树 + // 区间上维持最大值的线段树 + // 支持区间值更新 + // 为本道题定制了一个方法: + // 假设全是单点查询,请统一返回所有单点的结果(一个结果数组,里面有所有单点记录) + public static class SegmentTree3 { + private int[] max; + private int[] change; + private boolean[] update; + + public SegmentTree3(int size) { + int N = size + 1; + max = new int[N << 2]; + change = new int[N << 2]; + update = new boolean[N << 2]; + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + private void pushDown(int rt, int ln, int rn) { + if (update[rt]) { + update[rt << 1] = true; + update[rt << 1 | 1] = true; + change[rt << 1] = change[rt]; + change[rt << 1 | 1] = change[rt]; + max[rt << 1] = change[rt]; + max[rt << 1 | 1] = change[rt]; + update[rt] = false; + } + } + + public void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + update[rt] = true; + change[rt] = C; + max[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int index = 0; + + public int[] buildSingleQuery(int n) { + int[] ans = new int[n + 1]; + process(ans, 1, n, 1); + return ans; + } + + private void process(int[] ans, int l, int r, int rt) { + if (l == r) { + ans[index++] = max[rt]; + } else { + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + process(ans, l, mid, rt << 1); + process(ans, mid + 1, r, rt << 1 | 1); + } + } + + } + + // 为了测试 + public static int[] generateRandomArray(int n, int value) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * value) + 1; + } + return arr; + } + + // 为了测试 + public static int[][] generateRandomMagics(int n, int m, int value) { + int[][] magics = new int[m][3]; + for (int[] magic : magics) { + int a = (int) (Math.random() * n); + int b = (int) (Math.random() * n); + int c = (int) (Math.random() * value) + 1; + magic[0] = Math.min(a, b); + magic[1] = Math.max(a, b); + magic[2] = c; + } + return magics; + } + + // 为了测试 + public static void main(String[] args) { + int n = 30; + int m = 15; + int v = 1000; + int testTimes = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int N = (int) (Math.random() * n) + 1; + int M = (int) (Math.random() * m) + 1; + int[] arr = generateRandomArray(N, v); + int[][] magics = generateRandomMagics(N, M, v); + int ans1 = maxSum1(arr, magics); + int ans2 = maxSum2(arr, magics); + int ans3 = maxSum3(arr, magics); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + for (int[] magic : magics) { + System.out.println("[ " + magic[0] + " , " + magic[1] + " , " + magic[2] + " ] "); + } + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + + System.out.println("性能测试开始"); + System.out.println("n的数据量将达到10^7"); + System.out.println("m的数据量将达到10^6"); + System.out.println("为了防止溢出,每个值的范围控制在10以内"); + + n = 10000000; + m = 1000000; + v = 10; + + int[] arr = generateRandomArray(n, v); + int[][] magics = generateRandomMagics(n, m, v); + + long start; + long end; + + start = System.currentTimeMillis(); + int ans2 = maxSum2(arr, magics); + end = System.currentTimeMillis(); + System.out.println("方法二的结果 : " + ans2 + ", 方法二的运行时间: " + (end - start) + " 毫秒"); + + start = System.currentTimeMillis(); + int ans3 = maxSum3(arr, magics); + end = System.currentTimeMillis(); + System.out.println("方法三的结果 : " + ans3 + ", 方法三的运行时间: " + (end - start) + " 毫秒"); + + System.out.println("性能测试结束"); + + } + +} diff --git a/算法周更班/class_2021_12_2_week/Code04_LowestCommonAncestorOfABinaryTreeIV.java b/算法周更班/class_2021_12_2_week/Code04_LowestCommonAncestorOfABinaryTreeIV.java new file mode 100644 index 0000000..a1d654a --- /dev/null +++ b/算法周更班/class_2021_12_2_week/Code04_LowestCommonAncestorOfABinaryTreeIV.java @@ -0,0 +1,57 @@ +package class_2021_12_2_week; + +import java.util.HashSet; + +// 测试链接 : https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iv/ +public class Code04_LowestCommonAncestorOfABinaryTreeIV { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode[] nodes) { + HashSet set = new HashSet<>(); + for (TreeNode node : nodes) { + set.add(node.val); + } + return process(root, set, set.size()).find; + } + + public static class Info { + // 找没找到最低公共祖先 + // 没找到,find = null + // 找到了最低公共祖先,find是最低公共祖先 + public TreeNode find; + // 我这颗子树上,删掉了几个节点! + public int removes; + + public Info(TreeNode f, int r) { + find = f; + removes = r; + } + } + + public static Info process(TreeNode x, HashSet set, int all) { + if (x == null) { + return new Info(null, 0); + } + Info left = process(x.left, set, all); + if (left.find != null) { + return left; + } + Info right = process(x.right, set, all); + if (right.find != null) { + return right; + } + int cur = set.contains(x.val) ? 1 : 0; + set.remove(x.val); + if (left.removes + right.removes + cur == all) { + return new Info(x, all); + } else { + return new Info(null, left.removes + right.removes + cur); + } + } + +} diff --git a/算法周更班/class_2021_12_2_week/Code05_Colors.java b/算法周更班/class_2021_12_2_week/Code05_Colors.java new file mode 100644 index 0000000..20be746 --- /dev/null +++ b/算法周更班/class_2021_12_2_week/Code05_Colors.java @@ -0,0 +1,325 @@ +package class_2021_12_2_week; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +// 来自美团 +// 给定一棵多叉树的头节点head +// 每个节点的颜色只会是0、1、2、3中的一种 +// 任何两个节点之间的都有路径 +// 如果节点a和节点b的路径上,包含全部的颜色,这条路径算达标路径 +// (a -> ... -> b)和(b -> ... -> a)算两条路径 +// 求多叉树上达标的路径一共有多少? +// 点的数量 <= 10^5 +public class Code05_Colors { + + public static class Node { + public int color; + public List nexts; + + public Node(int c) { + color = c; + nexts = new ArrayList<>(); + } + } + + // 暴力方法 + // 为了验证 + public static int colors1(Node head) { + if (head == null) { + return 0; + } + HashMap map = new HashMap<>(); + parentMap(head, null, map); + List allNodes = new ArrayList<>(); + for (Node cur : map.keySet()) { + allNodes.add(cur); + } + int ans = 0; + for (int i = 0; i < allNodes.size(); i++) { + for (int j = i + 1; j < allNodes.size(); j++) { + if (ok(allNodes.get(i), allNodes.get(j), map)) { + ans++; + } + } + } + return ans << 1; + } + + public static void parentMap(Node cur, Node pre, HashMap map) { + if (cur != null) { + map.put(cur, pre); + for (Node next : cur.nexts) { + parentMap(next, cur, map); + } + } + } + + public static boolean ok(Node a, Node b, HashMap map) { + HashSet aPath = new HashSet<>(); + Node cur = a; + while (cur != null) { + aPath.add(cur); + cur = map.get(cur); + } + Node lowest = b; + while (!aPath.contains(lowest)) { + lowest = map.get(lowest); + } + int colors = 1 << lowest.color; + cur = a; + while (cur != lowest) { + colors |= (1 << cur.color); + cur = map.get(cur); + } + cur = b; + while (cur != lowest) { + colors |= (1 << cur.color); + cur = map.get(cur); + } + return colors == 15; + } + + // 正式方法 + public static long colors2(Node head) { + if (head == null) { + return 0; + } + return process2(head).all; + } + + public static class Info { + // 我这棵子树,总共合法的路径有多少? + public long all; + // 课上没有强调!但是请务必注意! + // 一定要从头节点出发的情况下! + // 一定要从头节点出发的情况下! + // 一定要从头节点出发的情况下! + // 走出来每种状态路径的条数 + public long[] colors; + + public Info() { + all = 0; + colors = new long[16]; + } + } + + public static Info process2(Node h) { + Info ans = new Info(); + // 头节点拥有的颜色 + // 2 0100 0 0001 3 1000 + int hs = 1 << h.color; + ans.colors[hs] = 1; + if (!h.nexts.isEmpty()) { + int n = h.nexts.size(); + // 0(不用) 1 2 3 4 + Info[] infos = new Info[n + 1]; + for (int i = 1; i <= n; i++) { + infos[i] = process2(h.nexts.get(i - 1)); + ans.all += infos[i].all; + } + long[][] lefts = new long[n + 2][16]; + for (int i = 1; i <= n; i++) { + for (int status = 1; status < 16; status++) { + lefts[i][status] = lefts[i - 1][status] + infos[i].colors[status]; + } + } + long[][] rights = new long[n + 2][16]; + for (int i = n; i >= 1; i--) { + for (int status = 1; status < 16; status++) { + rights[i][status] = rights[i + 1][status] + infos[i].colors[status]; + } + } + for (int status = 1; status < 16; status++) { + // x : 0010 子:0001 10个 + // 0011 + 10个 + ans.colors[status | hs] += rights[1][status]; + } + // 头节点出发,全颜色搞定,100个,200 + ans.all += ans.colors[15] << 1; + for (int from = 1; from <= n; from++) { + for (int fromStatus = 1; fromStatus < 16; fromStatus++) { + for (int toStatus = 1; toStatus < 16; toStatus++) { + if ((fromStatus | toStatus | hs) == 15) { + ans.all += infos[from].colors[fromStatus] + * (lefts[from - 1][toStatus] + rights[from + 1][toStatus]); + } + } + } + } + } + return ans; + } + + // 最后的优化版本 + // 和方法二没有本质区别 + // 优化的点:每个状态需要和哪些状态结合,都放在辅助数组consider里 + public static long colors3(Node head) { + if (head == null) { + return 0; + } + return process3(head).all; + } + + public static int[][] consider = { {}, // 0 + { 14, 15 }, // 1 -> 0001 + { 13, 15 }, // 2 -> 0010 + { 12, 13, 14, 15 }, // 3 -> 0011 + { 11, 15 }, // 4 -> 0100 + { 10, 11, 14, 15 }, // 5 -> 0101 + { 9, 11, 13, 15 }, // 6 -> 0110 + { 8, 9, 10, 11, 12, 13, 14, 15 }, // 7 -> 0111 + { 7, 15 }, // 8 -> 1000 + { 6, 7, 14, 15 }, // 9 -> 1001 + { 5, 7, 13, 15 }, // 10 -> 1010 + { 4, 5, 6, 7, 12, 13, 14, 15 }, // 11 -> 1011 + { 3, 7, 11, 15 }, // 12 -> 1100 + { 2, 3, 6, 7, 10, 11, 14, 15 }, // 13 -> 1101 + { 1, 3, 5, 7, 9, 11, 13, 15 }, // 14 -> 1110 + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } // 15 -> 1111 + }; + + public static Info process3(Node h) { + Info ans = new Info(); + int hs = 1 << h.color; + ans.colors[hs] = 1; + if (!h.nexts.isEmpty()) { + int n = h.nexts.size(); + Info[] infos = new Info[n + 1]; + for (int i = 1; i <= n; i++) { + infos[i] = process3(h.nexts.get(i - 1)); + ans.all += infos[i].all; + } + long[][] lefts = new long[n + 2][16]; + for (int i = 1; i <= n; i++) { + for (int status = 1; status < 16; status++) { + lefts[i][status] = lefts[i - 1][status] + infos[i].colors[status]; + } + } + long[][] rights = new long[n + 2][16]; + for (int i = n; i >= 1; i--) { + for (int status = 1; status < 16; status++) { + rights[i][status] = rights[i + 1][status] + infos[i].colors[status]; + } + } + for (int status = 1; status < 16; status++) { + ans.colors[status | hs] += rights[1][status]; + } + ans.all += ans.colors[15] << 1; + for (int from = 1; from <= n; from++) { + for (int fromStatus = 1; fromStatus < 16; fromStatus++) { + for (int toStatus : consider[fromStatus | hs]) { + ans.all += infos[from].colors[fromStatus] + * (lefts[from - 1][toStatus] + rights[from + 1][toStatus]); + } + } + } + } + return ans; + } + + // 为了测试 + public static Node randomTree(int len, int childs) { + Node head = new Node((int) (Math.random() * 4)); + generate(head, len - 1, childs); + return head; + } + + // 为了测试 + public static void generate(Node pre, int restLen, int childs) { + if (restLen == 0) { + return; + } + int size = (int) (Math.random() * childs); + for (int i = 0; i < size; i++) { + Node next = new Node((int) (Math.random() * 4)); + generate(next, restLen - 1, childs); + pre.nexts.add(next); + } + } + + // 为了测试 + public static void printTree(Node head) { + System.out.print(head.color + " "); + if (!head.nexts.isEmpty()) { + System.out.print("( "); + for (Node next : head.nexts) { + printTree(next); + System.out.print(" , "); + } + System.out.print(") "); + } + } + + // 为了测试 + // 生成高度为9的满5叉树,每个节点的颜色在0~3上随机 + // 这棵树的节点个数已经达到5 * 10^5的规模 + public static Node randomTree() { + Queue curq = new LinkedList<>(); + Queue nexq = new LinkedList<>(); + Node head = new Node((int) (Math.random() * 4)); + curq.add(head); + for (int len = 1; len < 9; len++) { + while (!curq.isEmpty()) { + Node cur = curq.poll(); + for (int i = 0; i < 5; i++) { + Node next = new Node((int) (Math.random() * 4)); + cur.nexts.add(next); + nexq.add(next); + } + } + Queue tmp = nexq; + nexq = curq; + curq = tmp; + } + return head; + } + + // 为了测试 + public static void main(String[] args) { + int len = 6; + int childs = 6; + int testTime = 3000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + Node head = randomTree(len, childs); + int ans1 = colors1(head); + long ans2 = colors2(head); + long ans3 = colors3(head); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("出错了"); + printTree(head); + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + + Node h = randomTree(); + System.out.println("节点数量达到 5*(10^5) 规模"); + long start; + long end; + + start = System.currentTimeMillis(); + long ans2 = colors2(h); + end = System.currentTimeMillis(); + System.out.println("方法二答案 : " + ans2 + ", 方法二运行时间 : " + (end - start) + " 毫秒"); + + start = System.currentTimeMillis(); + long ans3 = colors3(h); + end = System.currentTimeMillis(); + System.out.println("方法三答案 : " + ans3 + ", 方法三运行时间 : " + (end - start) + " 毫秒"); + + System.out.println("性能测试结束"); + } + +} diff --git a/算法周更班/class_2021_12_3_week/Code01_RightMoveInBinaryTree.java b/算法周更班/class_2021_12_3_week/Code01_RightMoveInBinaryTree.java new file mode 100644 index 0000000..b493b9f --- /dev/null +++ b/算法周更班/class_2021_12_3_week/Code01_RightMoveInBinaryTree.java @@ -0,0 +1,74 @@ +package class_2021_12_3_week; + +// 测试链接 : https://www.nowcoder.com/test/33701596/summary +// 本题目为第1题 +// 牛客网判题过程不好,卡常数了 +// 课上会说什么是"卡常数" +// 卡常数怎么反馈? +public class Code01_RightMoveInBinaryTree { + + // 这个类不需要提交 + public static class TreeNode { + int val = 0; + TreeNode left = null; + TreeNode right = null; + + public TreeNode(int val) { + this.val = val; + } + } + + // 提交下面的代码 + + public static TreeNode[] queue = new TreeNode[300000]; + + public static int[] ends = new int[50]; + + public static TreeNode cyclicShiftTree(TreeNode root, int k) { + int l = 0; + int r = 0; + queue[r++] = root; + int level = 0; + while (l != r) { + ends[level] = r; + while (l < ends[level]) { + TreeNode cur = queue[l++]; + if (cur != null) { + queue[r++] = cur.left; + queue[r++] = cur.right; + } + } + level++; + } + for (int i = level - 1; i > 0; i--) { + + // 当前层 : curLeft....curRight + // 3(null) 4(a) 5(null) 6(b) + // 下一层 :downLeft....downRight + // 7 8 9 10 + // downIndex : 下一层需要根据,k和下一层的长度,来右移。右移之后,从哪个位置开始,分配节点给当前层第一个不空的节点 + int downLeft = ends[i - 1]; + int downRight = ends[i] - 1; + int downRightSize = k % (downRight - downLeft + 1); + int downIndex = downRightSize == 0 ? downLeft : (downRight - downRightSize + 1); + int curLeft = i - 2 >= 0 ? ends[i - 2] : 0; + int curRight = ends[i - 1] - 1; + for (int j = curLeft; j <= curRight; j++) { + if (queue[j] != null) { + queue[j].left = queue[downIndex]; + downIndex = nextIndex(downIndex, downLeft, downRight); + queue[j].right = queue[downIndex]; + downIndex = nextIndex(downIndex, downLeft, downRight); + } + } + } + return root; + } + + // l......r i -> next index + // 4.....9 i = 7 8 9 4 + public static int nextIndex(int i, int l, int r) { + return i == r ? l : i + 1; + } + +} diff --git a/算法周更班/class_2021_12_3_week/Code02_BinaryNegate.java b/算法周更班/class_2021_12_3_week/Code02_BinaryNegate.java new file mode 100644 index 0000000..bf2e23e --- /dev/null +++ b/算法周更班/class_2021_12_3_week/Code02_BinaryNegate.java @@ -0,0 +1,25 @@ +package class_2021_12_3_week; + +//测试链接 : https://www.nowcoder.com/test/33701596/summary +//本题目为第2题 +public class Code02_BinaryNegate { + + public static String maxLexicographical(String num) { + char[] arr = num.toCharArray(); + int i = 0; + while (i < arr.length) { + if (arr[i] == '0') { + break; + } + i++; + } + while(i < arr.length) { + if(arr[i] == '1') { + break; + } + arr[i++] = '1'; + } + return String.valueOf(arr); + } + +} diff --git a/算法周更班/class_2021_12_3_week/Code03_OneCountsInKSystem.java b/算法周更班/class_2021_12_3_week/Code03_OneCountsInKSystem.java new file mode 100644 index 0000000..b7b6d2d --- /dev/null +++ b/算法周更班/class_2021_12_3_week/Code03_OneCountsInKSystem.java @@ -0,0 +1,58 @@ +package class_2021_12_3_week; + +// 测试链接 : https://www.nowcoder.com/test/33701596/summary +// 本题目为第3题 +// 核心方法,在大厂刷题班19节,第3题 +public class Code03_OneCountsInKSystem { + + public static long minM(int n, int k) { + int len = bits(n, k); + long l = 1; + long r = power(k, len + 1); + long ans = r; + while (l <= r) { + long m = l + ((r - l) >> 1); + if (ones(m, k) >= n) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int bits(long num, int k) { + int len = 0; + while (num != 0) { + len++; + num /= k; + } + return len; + } + + public static long power(long base, int power) { + long ans = 1; + while (power != 0) { + if ((power & 1) != 0) { + ans *= base; + } + base *= base; + power >>= 1; + } + return ans; + } + + public static long ones(long num, int k) { + int len = bits(num, k); + if (len <= 1) { + return len; + } + long offset = power(k, len - 1); + long first = num / offset; + long curOne = first == 1 ? (num % offset) + 1 : offset; + long restOne = first * (len - 1) * (offset / k); + return curOne + restOne + ones(num % offset, k); + } + +} diff --git a/算法周更班/class_2021_12_3_week/Code04_CutOffTreesForGolfEvent.java b/算法周更班/class_2021_12_3_week/Code04_CutOffTreesForGolfEvent.java new file mode 100644 index 0000000..87cd3a5 --- /dev/null +++ b/算法周更班/class_2021_12_3_week/Code04_CutOffTreesForGolfEvent.java @@ -0,0 +1,79 @@ +package class_2021_12_3_week; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +// 测试链接 : https://leetcode.com/problems/cut-off-trees-for-golf-event/ +public class Code04_CutOffTreesForGolfEvent { + + public static int cutOffTree(List> forest) { + int n = forest.size(); + int m = forest.get(0).size(); + // [ [3,5,2], [1,9,4] , [2,6,10] ] + // 低 中 高 + ArrayList cells = new ArrayList<>(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + int val = forest.get(i).get(j); + if (val > 1) { + cells.add(new int[] { i, j, val }); + } + } + } + cells.sort((a, b) -> a[2] - b[2]); + int ans = 0, lastR = 0, lastC = 0; + for (int[] cell : cells) { + int step = bestWalk(forest, lastR, lastC, cell[0], cell[1]); + if (step == -1) { + return -1; + } + ans += step; + lastR = cell[0]; + lastC = cell[1]; + forest.get(lastR).set(lastC, 1); + } + return ans; + } + + public static int[] next = { -1, 0, 1, 0, -1 }; + + // 0 1 2 3 4 + // i + // 行 + next[i-1] + // 列 + next[i] + // i == 1 -> 上 + // i == 2 -> 右 + // i == 3 -> 下 + // i == 4 -> 左 + public static int bestWalk(List> forest, int sr, int sc, int tr, int tc) { + int n = forest.size(); + int m = forest.get(0).size(); + boolean[][] seen = new boolean[n][m]; + LinkedList deque = new LinkedList<>(); + deque.offerFirst(new int[] { 0, sr, sc }); + while (!deque.isEmpty()) { + int[] cur = deque.pollFirst(); + int step = cur[0], r = cur[1], c = cur[2]; + if (r == tr && c == tc) { + return step; + } + seen[r][c] = true; + for (int i = 1; i < 5; i++) { // (r,c) 上下左右,全试试! + int nr = r + next[i - 1]; + int nc = c + next[i]; + if (nr >= 0 && nr < n && nc >= 0 && nc < m && !seen[nr][nc] && forest.get(nr).get(nc) > 0) { + int[] move = { step + 1, nr, nc }; + // 更近的话 + if ((i == 1 && r > tr) || (i == 2 && c < tc) || (i == 3 && r < tr) || (i == 4 && c > tc)) { + deque.offerFirst(move); + } else { // 更远的话,放到尾部! + deque.offerLast(move); + } + } + } + } + return -1; + } + +} diff --git a/算法周更班/class_2021_12_3_week/Code05_MinContinuousFragment.java b/算法周更班/class_2021_12_3_week/Code05_MinContinuousFragment.java new file mode 100644 index 0000000..002800a --- /dev/null +++ b/算法周更班/class_2021_12_3_week/Code05_MinContinuousFragment.java @@ -0,0 +1,162 @@ +package class_2021_12_3_week; + +// 来自CMU入学申请考试 +// 给定一个长度为 N 的字符串 S,由字符'a'和'b'组成,空隙由 '?' 表示 +// 你的任务是用a字符或b字符替换每个间隙, +// 替换完成后想让连续出现同一种字符的最长子串尽可能短 +// 例如,S = "aa??bbb", +// 如果将"??"替换为"aa" ,即"aaaabbb",则由相等字符组成的最长子串长度为4 +// 如果将"??"替换为"ba" ,即"aababbb",则由相等字符组成的最长子串长度为3 +// 那么方案二是更好的结果,返回3 +// S的长度 <= 10^6 +public class Code05_MinContinuousFragment { + + // 暴力方法 + // 为了验证 + public static int minContinuous1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + return process1(str, 0); + } + + public static int process1(char[] str, int index) { + if (index == str.length) { + return maxLen(str); + } else { + if (str[index] != '?') { + return process1(str, index + 1); + } else { + str[index] = 'a'; + int p1 = process1(str, index + 1); + str[index] = 'b'; + int p2 = process1(str, index + 1); + str[index] = '?'; + return Math.min(p1, p2); + } + } + } + + // 正式方法 + // 时间复杂度O(N) + public static int minContinuous2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int L = 0; + int R = -1; + for (int i = 0; i < N; i++) { + if (str[i] != '?') { + set(str, L, R); + L = i + 1; + R = i; + } else { + R++; + } + } + set(str, L, R); + // 下面的for循环,是单独处理,条件5) + for (int i = 1; i < N; i++) { + if (str[i] == '?') { + // baaaa?bbbbbbbba + for (L = i - 1; L >= 0 && str[L] == str[i - 1]; L--) + ; + for (R = i + 1; R < N && str[R] == str[i + 1]; R++) + ; + L = i - L - 1; + R = R - i - 1; + if (L <= R) { + str[i] = str[i - 1]; + } else { + str[i] = str[i + 1]; + } + } + } + return maxLen(str); + } + + // L...R 都是? + // 如果这一坨问号,满足1)2)3)4)中的一种,就填好 + // 如果满足5),就不填!a?b + public static void set(char[] str, int L, int R) { + int N = str.length; + if (L > R) { + return; + } + if (L == 0 && R == N - 1) { + for (int i = 0; i < N; i++) { + str[i] = (i & 1) == 0 ? 'a' : 'b'; + } + } else if (L == 0) { + for (int i = R; i >= 0; i--) { + str[i] = str[i + 1] == 'a' ? 'b' : 'a'; + } + } else if (R == N - 1) { + for (int i = L; i < str.length; i++) { + str[i] = str[i - 1] == 'a' ? 'b' : 'a'; + } + } else { + if (str[L - 1] == str[R + 1] || L != R) { + for (; L <= R; L++, R--) { + str[L] = str[L - 1] == 'a' ? 'b' : 'a'; + str[R] = str[R + 1] == 'a' ? 'b' : 'a'; + } + } + } + } + + public static int maxLen(char[] str) { + int ans = 1; + int cur = 1; + for (int i = 1; i < str.length; i++) { + if (str[i] != str[i - 1]) { + ans = Math.max(ans, cur); + cur = 1; + } else { + cur++; + } + } + ans = Math.max(ans, cur); + return ans; + } + + public static char[] arr = { 'a', 'b', '?' }; + + public static String randomString(int len) { + int N = (int) (Math.random() * (len + 1)); + char[] str = new char[N]; + for (int i = 0; i < N; i++) { + str[i] = arr[(int) (Math.random() * 3)]; + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int len = 35; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + String s = randomString(len); + int ans1 = minContinuous1(s); + int ans2 = minContinuous2(s); + if (ans1 != ans2) { + System.out.println(s); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + + len = 10000000; + String s = randomString(len); + long start = System.currentTimeMillis(); + minContinuous2(s); + long end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒):" + (end - start)); + + } + +} diff --git a/算法周更班/class_2021_12_4_week/Code01_FiveNodesListNumbers.java b/算法周更班/class_2021_12_4_week/Code01_FiveNodesListNumbers.java new file mode 100644 index 0000000..82ed17e --- /dev/null +++ b/算法周更班/class_2021_12_4_week/Code01_FiveNodesListNumbers.java @@ -0,0 +1,58 @@ +package class_2021_12_4_week; + +import java.util.HashSet; + +// 来自美团 +// 给定一个无向图 +// 从任何一个点x出发,比如有一条路径: x -> a -> b -> c -> y +// 这条路径上有5个点并且5个点都不一样的话,我们说(x,a,b,c,y)是一条合法路径 +// 这条合法路径的代表,就是x,a,b,c,y所组成的集合,我们叫做代表集合 +// 如果从b到y,还有一条路径叫(b,a,c,x,y),那么(x,a,b,c,y)和(b,a,c,x,y)是同一个代表集合 +// 返回这个无向图中所有合法路径的代表集合数量 +// 题目给定点的数量n <= 15,边的数量m <= 60 +// 所有的点编号都是从0~n-1的 +public class Code01_FiveNodesListNumbers { + + // graph[i] = { a, b, c} 代表:点i直接相邻的节点有a,b,c + // graph[j] = { d } 代表:点j直接相邻的节点有d + // 所以二维数组graph可以表示无向图 + // 0 : + // 1 : + // 2 : + // n-1 : + public static int validPathSets(int[][] graph) { + int n = graph.length; + // 任何一个合法路径的集合,都被弄成了整数形式 + // 0010010011 -> int + // 甲 : 0011010011 + // 乙 : 0011010011 + // 丙 : 0011010011 + HashSet set = new HashSet<>(); + // 下面的过程:从每个点出发,0、1、2、3、。。。 + // 从x点出发,往外最多迈5步,所产生的所有路径,都要! + for (int from = 0; from < n; from++) { + dfs(0, 0, from, graph, set); + } + return set.size(); + } + + // int status -> 已经走过了哪些点的集合 -> 00001101 + // int len -> 已经往外几步了! + // int cur -> 当前来到的是几号点! + // int[][] graph -> 图 + // HashSet set -> 收集所有合法路径的点集合! + public static void dfs(int status, int len, int cur, int[][] graph, HashSet set) { + if ((status & (1 << cur)) == 0) { // 之前走过的点,不包括cur,迈上去! + len++; + status |= 1 << cur; + if (len == 5) { + set.add(status); + } else { + for (int next : graph[cur]) { + dfs(status, len, next, graph, set); + } + } + } + } + +} diff --git a/算法周更班/class_2021_12_4_week/Code02_MergeArea.java b/算法周更班/class_2021_12_4_week/Code02_MergeArea.java new file mode 100644 index 0000000..ea97aa0 --- /dev/null +++ b/算法周更班/class_2021_12_4_week/Code02_MergeArea.java @@ -0,0 +1,143 @@ +package class_2021_12_4_week; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +// 来自北京北明数科信息技术有限公司 +// area表示的是地区全路径,最多可能有6级,用分隔符连接,分隔符是 spliter, +// 分隔符是逗号 +// 例如: +// area = 中国,四川,成都 或者 中国,浙江,杭州 或者 中国,浙江,义乌 +// spliter = , +// count表示门店数 +// class AreaResource { +// String area; +// String spliter; +// long count; +// } + +// area = "中国,四川,成都" +// spliter = "," +// count = 10 + +// 现在需要把 List 进行字符串转换,供下游处理,需要做到同级的地域能合并 +// 比如 +// area为 中国,四川,成都 有10个门店 +// 中国,浙江,杭州 有25个门店 +// 中国,浙江,义乌 有22个门店 +// 中国,四川,成都 有25个门店 +// spliter为逗号 "," 最终转化成JSON的形式,并且同级的地域需要被合并,最终生成的JSON字符串如下所示 +// +// 返回: { +// "中国": +// {"四川":{"成都":35]}, +// "浙江":{"义乌":22,"杭州":25}}} +// 请实现下面的方法 public String mergeCount(List areas) +public class Code02_MergeArea { + + // 系统给你的原始类 + public static class AreaResource { + public String area; + public String spliter; + public long count; + + public AreaResource(String a, String s, long c) { + area = a; + spliter = s; + count = c; + } + } + + // 要实现的方法 + public static String mergeCount(List areas) { + Area all = new Area("", 0); + for (AreaResource r : areas) { + // 中国,四川,成都 , 10个门店 + String[] path = r.area.split(r.spliter); + // 中国 四川 成都 + long count = r.count; + f(path, 0, all, count); + } + return all.toString(); + } + + // [中国,四川,成都] + // 0 1 2 + public static void f(String[] path, int index, Area pre, long count) { + if (index == path.length) { + pre.count += count; + } else { + // 前一个节点 pre 中国 + // cur = 四川 + String cur = path[index]; + if (!pre.next.containsKey(cur)) { + pre.next.put(cur, new Area(cur, 0)); + } + f(path, index + 1, pre.next.get(cur), count); + } + } + + // 自己定义的!前缀树节点 + public static class Area { + // 地区名称:中国 + // 四川 + // 成都 + public String name; + // 中国 key : 省名 value: 下级节点 + public HashMap next; + public long count; + + public Area(String n, long c) { + name = n; + count = c; + next = new HashMap<>(); + } + + public String toString() { + StringBuilder ans = new StringBuilder(); + // : "" + // str = "中国": + // str = "成都":100 + if (!name.equals("")) { + ans.append("\"" + name + "\"" + ":"); + } + if (next.isEmpty()) { + ans.append(count); + } else { + // "中国":{ 四川如何如何,河南如何如何,江苏如何如何} + ans.append("{"); + for (Area child : next.values()) { + ans.append(child.toString() + ","); + } + ans.setCharAt(ans.length() - 1, '}'); + } + return ans.toString(); + } + } + + public static void main(String[] args) { + AreaResource a1 = new AreaResource("中国,四川,成都", ",", 10); + AreaResource a2 = new AreaResource("中国,浙江,杭州", ",", 50); + AreaResource a3 = new AreaResource("中国,浙江,杭州", ",", 25); + AreaResource a4 = new AreaResource("中国,浙江,义务", ",", 22); + AreaResource a5 = new AreaResource("中国,四川,成都", ",", 15); + AreaResource a6 = new AreaResource("中国,四川,攀枝花", ",", 12); + AreaResource a7 = new AreaResource("中国,浙江,宁波", ",", 16); + + List areas = new ArrayList<>(); + areas.add(a1); + areas.add(a2); + areas.add(a3); + areas.add(a4); + areas.add(a5); + areas.add(a6); + areas.add(a7); + + String ans = mergeCount(areas); + + System.out.println(ans); + + } + +} diff --git a/算法周更班/class_2021_12_4_week/Code03_HowManyObtuseAngles.java b/算法周更班/class_2021_12_4_week/Code03_HowManyObtuseAngles.java new file mode 100644 index 0000000..1cae781 --- /dev/null +++ b/算法周更班/class_2021_12_4_week/Code03_HowManyObtuseAngles.java @@ -0,0 +1,42 @@ +package class_2021_12_4_week; + +import java.util.Arrays; + +// 来自hulu +// 有一个以原点为圆心,半径为1的圆 +// 在这个圆的圆周上,有一些点 +// 因为所有的点都在圆周上,所以每个点可以有很简练的表达 +// 比如:用0来表示一个圆周上的点,这个点就在(1,0)位置 +// 比如:用6000来表示一个点,这个点是(1,0)点沿着圆周逆时针转60.00度之后所在的位置 +// 比如:用18034来表示一个点,这个点是(1,0)点沿着圆周逆时针转180.34度之后所在的位置 +// 这样一来,所有的点都可以用[0, 36000)范围上的数字来表示 +// 那么任意三个点都可以组成一个三角形,返回能组成钝角三角形的数量 +public class Code03_HowManyObtuseAngles { + + public static long obtuseAngles(int[] arr) { + // n长度的排序,O(N * logN) + // O(N) + int n = arr.length; + int m = n << 1; + int[] enlarge = new int[m]; + Arrays.sort(arr); + for (int i = 0; i < n; i++) { + enlarge[i] = arr[i]; + enlarge[i + n] = arr[i] + 36000; + } + long ans = 0; + // 这里不用二分查找(太慢),能做一个不回退的优化 + for (int L = 0, R = 0; L < n; L++) { + while (R < m && enlarge[R] - enlarge[L] < 18000) { + R++; + } + ans += num(R - L - 1); + } + return ans; + } + + public static long num(long nodes) { + return nodes < 2 ? 0 : ((nodes - 1) * nodes) >> 1; + } + +} diff --git a/算法周更班/class_2021_12_4_week/Code04_MaximumNumberOfVisiblePoints.java b/算法周更班/class_2021_12_4_week/Code04_MaximumNumberOfVisiblePoints.java new file mode 100644 index 0000000..9e05247 --- /dev/null +++ b/算法周更班/class_2021_12_4_week/Code04_MaximumNumberOfVisiblePoints.java @@ -0,0 +1,43 @@ +package class_2021_12_4_week; + +import java.util.Arrays; +import java.util.List; + +// 整个二维平面算是一张地图,给定[x,y],表示你站在x行y列 +// 你可以选择面朝的任何方向 +// 给定一个正数值angle,表示你视野的角度为 +// 这个角度内你可以看无穷远,这个角度外你看不到任何东西 +// 给定一批点的二维坐标,返回你在朝向最好的情况下,最多能看到几个点 +// 测试链接 : https://leetcode.com/problems/maximum-number-of-visible-points/ +public class Code04_MaximumNumberOfVisiblePoints { + + public static int visiblePoints(List> points, int angle, List location) { + int n = points.size(); + int a = location.get(0); + int b = location.get(1); + int zero = 0; + double[] arr = new double[n << 1]; + int m = 0; + for (int i = 0; i < n; i++) { + int x = points.get(i).get(0) - a; + int y = points.get(i).get(1) - b; + if (x == 0 && y == 0) { + zero++; + } else { + arr[m] = Math.toDegrees(Math.atan2(y, x)); + arr[m + 1] = arr[m] + 360; + m += 2; + } + } + Arrays.sort(arr, 0, m); + int max = 0; + for (int L = 0, R = 0; L < n; L++) { + while (R < m && arr[R] - arr[L] <= angle) { + R++; + } + max = Math.max(max, R - L); + } + return max + zero; + } + +} diff --git a/算法周更班/class_2021_12_4_week/Code05_SplitApples.java b/算法周更班/class_2021_12_4_week/Code05_SplitApples.java new file mode 100644 index 0000000..f8d6e42 --- /dev/null +++ b/算法周更班/class_2021_12_4_week/Code05_SplitApples.java @@ -0,0 +1,123 @@ +package class_2021_12_4_week; + +//有m个同样的苹果,认为苹果之间无差别 +//有n个同样的盘子,认为盘子之间也无差别 +//还有,比如5个苹果如果放进3个盘子, +//那么1、3、1和1、1、3和3、1、1的放置方法,也认为是一种方法 +//如上的设定下,返回有多少种放置方法 +//测试链接 : https://www.nowcoder.com/practice/bfd8234bb5e84be0b493656e390bdebf +//提交以下的code,提交时请把类名改成"Main" +import java.util.Arrays; +import java.util.Scanner; + +public class Code05_SplitApples { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + int m = sc.nextInt(); + int n = sc.nextInt(); + int ways = ways3(m, n); + System.out.println(ways); + } + sc.close(); + } + + // 思路来自于分裂数问题 + // 体系学习班代码第22节,题目3,split number问题 + public static int ways1(int apples, int plates) { + return process1(1, apples, plates); + } + + // pre : 上一个盘子分到的苹果数量,当前的盘子分到的数量不能小于pre + // apples : 剩余的苹果数量 + // plates : 剩余的盘子数量 + // 在盘子够用的情况下,把苹果分完,有几种方法 + public static int process1(int pre, int apples, int plates) { + if (apples == 0) { + return 1; + } + // apples != 0 + if (plates == 0) { + return 0; + } + // apples != 0 && plates != 0 + if (pre > apples) { + return 0; + } + // apples != 0 && plates != 0 && pre <= apples + int way = 0; + // 之前的盘子分了3个苹果,现在还剩下8个苹果 + // 当前的盘子,可以装几个苹果:3、4、5、6、7、8 + for (int cur = pre; cur <= apples; cur++) { + way += process1(cur, apples - cur, plates - 1); + } + return way; + } + + // 新的尝试,最优解 + // 苹果有apples个,盘子有plates个 + // 返回有几种摆法 + // 如果苹果数为0,有1种摆法:什么也不摆 + // 如果苹果数不为0,但是盘子数为0,有0种摆法(做不到) + // 如果苹果数不为0,盘子数也不为0,进行如下的情况讨论: + // 假设苹果数为apples,盘子数为plates + // 可能性 1) apples < plates + // 这种情况下,一定有多余的盘子,这些盘子完全没用,所以砍掉 + // 后续是f(apples, apples) + // 可能性 2) apples >= plates + // 在可能性2)下,讨论摆法,有如下两种选择 + // 选择a) 不是所有的盘子都使用 + // 选择b) 就是所有的盘子都使用 + // 对于选择a),既然不是所有盘子都使用,那么后续就是f(apples, plates - 1) + // 意思是:既然不是所有盘子都使用,那盘子减少一个,然后继续讨论吧! + // 对于选择b),既然就是所有的盘子都使用,那么先把所有盘子都摆上1个苹果。 + // 剩余苹果数 = apples - plates + // 然后继续讨论,剩下的这些苹果,怎么摆进plates个盘子里, + // 所以后续是f(apples - plates, plates) + public static int ways2(int apples, int plates) { + if (apples == 0) { + return 1; + } + if (plates == 0) { + return 0; + } + if (plates > apples) { + return ways2(apples, apples); + } else { // apples >= plates; + return ways2(apples, plates - 1) + ways2(apples - plates, plates); + } + } + + // 上面最优解尝试的记忆化搜索版本 + public static int[][] dp = null; + + public static int ways3(int apples, int plates) { + if (dp == null) { + dp = new int[11][11]; + for (int i = 0; i <= 10; i++) { + Arrays.fill(dp[i], -1); + } + } + return process3(apples, plates, dp); + } + + public static int process3(int apples, int plates, int[][] dp) { + if (dp[apples][plates] != -1) { + return dp[apples][plates]; + } + int ans = 0; + if (apples == 0) { + ans = 1; + } else if (plates == 0) { + ans = 0; + } else if (plates > apples) { + ans = process3(apples, apples, dp); + } else { + ans = process3(apples, plates - 1, dp) + process3(apples - plates, plates, dp); + } + dp[apples][plates] = ans; + return ans; + } + +} \ No newline at end of file diff --git a/算法周更班/class_2021_12_5_week/Code01_LoudAndRich.java b/算法周更班/class_2021_12_5_week/Code01_LoudAndRich.java new file mode 100644 index 0000000..36fccd4 --- /dev/null +++ b/算法周更班/class_2021_12_5_week/Code01_LoudAndRich.java @@ -0,0 +1,68 @@ +package class_2021_12_5_week; + +import java.util.ArrayList; + +// 测试链接 : https://leetcode.com/problems/loud-and-rich/ +public class Code01_LoudAndRich { + + // richer[i] = {a, b} a比b更有钱 a -> b + // quiet[i] = k, i这个人安静值是k + public static int[] loudAndRich(int[][] richer, int[] quiet) { + int N = quiet.length; + // a -> b + // a -> c + // b -> c + // a : b c + // b : c + // nexts[0] = {5,7,3} + // 0 : 5 7 3 + // 5最没钱的, + // nexts[5] = { } + ArrayList> nexts = new ArrayList<>(); + for (int i = 0; i < N; i++) { + // 0 : {} + // 1 : {} + // n-1 : {} + nexts.add(new ArrayList<>()); + } + // 入度 + // 0 : 0 + // 1 : 2 + int[] degree = new int[N]; + for (int[] r : richer) { + // [a,b] a -> b + nexts.get(r[0]).add(r[1]); + degree[r[1]]++; + } + // 所有入度为0的点,入队列 + int[] zeroQueue = new int[N]; + int l = 0; + int r = 0; + for (int i = 0; i < N; i++) { + if (degree[i] == 0) { + zeroQueue[r++] = i; + } + } + // ans[i] = j : 比i有钱的所有人里,j最安静 + int[] ans = new int[N]; + for (int i = 0; i < N; i++) { + ans[i] = i; + } + while (l < r) { // 如果队列不空 + // 弹出一个入度为0的点 + int cur = zeroQueue[l++]; + // 1) 消除当前cur的影响! + for (int next : nexts.get(cur)) { + // cur : 比cur有钱,最安静的!ans[cur] + if (quiet[ans[next]] > quiet[ans[cur]]) { + ans[next] = ans[cur]; + } + if (--degree[next] == 0) { + zeroQueue[r++] = next; + } + } + } + return ans; + } + +} diff --git a/算法周更班/class_2021_12_5_week/Code02_DoAllJobs.java b/算法周更班/class_2021_12_5_week/Code02_DoAllJobs.java new file mode 100644 index 0000000..1081e2b --- /dev/null +++ b/算法周更班/class_2021_12_5_week/Code02_DoAllJobs.java @@ -0,0 +1,129 @@ +package class_2021_12_5_week; + +import java.util.Arrays; +import java.util.PriorityQueue; + +// 来自hulu +// 有n个人,m个任务,任务之间有依赖记录在int[][] depends里 +// 比如: depends[i] = [a, b],表示a任务依赖b任务的完成 +// 其中 0 <= a < m,0 <= b < m +// 1个人1天可以完成1个任务,每个人都会选当前能做任务里,标号最小的任务 +// 一个任务所依赖的任务都完成了,该任务才能开始做 +// 返回n个人做完m个任务,需要几天 +public class Code02_DoAllJobs { + + public static int days(int n, int m, int[][] depends) { + if (n < 1) { + return -1; + } + if (m <= 0) { + return 0; + } + // nexts[0] = {1,4} + int[][] nexts = nexts(depends, m); + int[] indegree = indegree(nexts, m); + // 工人队列! + PriorityQueue workers = new PriorityQueue<>(); + for (int i = 0; i < n; i++) { + workers.add(0); + } + // zeroIn : 放着工作,放着可以开始做的工作,不能做的任务,不在其中 + // 小根堆:标号小的任务,一定要先做! + PriorityQueue zeroIn = new PriorityQueue<>(); + for (int i = 0; i < m; i++) { + if (indegree[i] == 0) { + zeroIn.add(i); + } + } + // start[i] :i之前必须完成的任务,占了几天,导致i任务只能从那天开始! + int[] start = new int[m]; + // 完成所有任务的最大天数 + int finishAll = 0; + int done = 0; + while (!zeroIn.isEmpty()) { // 有任务可做 + // 当前可以做的任务中,标号最小的任务 + int job = zeroIn.poll(); + // 当前可用的工人里,最早醒的! + int wake = workers.poll(); + // job 何时完成呢? + // (工人醒来,开工时间)最晚的!+1 + int finish = Math.max(start[job], wake) + 1; + finishAll = Math.max(finishAll, finish); + done++; + // 消除影响 + for (int next : nexts[job]) { + start[next] = Math.max(start[next], finish); + if (--indegree[next] == 0) { + zeroIn.add(next); + } + } + workers.add(finish); + } + return done == m ? finishAll : -1; + } + + public static int[][] nexts(int[][] depends, int m) { + Arrays.sort(depends, (a, b) -> a[1] - b[1]); + int n = depends.length; + int[][] nexts = new int[m][0]; + if (n == 0) { + return nexts; + } + int size = 1; + for (int i = 1; i < n; i++) { + if (depends[i - 1][1] != depends[i][1]) { + int from = depends[i - 1][1]; + nexts[from] = new int[size]; + for (int k = 0, j = i - size; k < size; k++, j++) { + nexts[from][k] = depends[j][0]; + } + size = 1; + } else { + size++; + } + } + int from = depends[n - 1][1]; + nexts[from] = new int[size]; + for (int k = 0, j = n - size; k < size; k++, j++) { + nexts[from][k] = depends[j][0]; + } + return nexts; + } + + public static int[] indegree(int[][] nexts, int m) { + int[] indegree = new int[m]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < nexts[i].length; j++) { + indegree[nexts[i][j]]++; + } + } + return indegree; + } + + public static void main(String[] args) { + // 2 -> 5 -> 6 + // | + // v + // 1 -> 4 -> 7 + // ^ + // | + // 0 -> 3 + // 两个人 + // {1,2} 工人队列 + // 0 : 干0号工作 ,1 + // 0 : 干1号工作 ,1 + // 1 : 干2号工作,2 + int[][] d = { + { 3, 0 }, + { 4, 1 }, + { 5, 2 }, + { 4, 3 }, + { 6, 5 }, + { 7, 4 }, + { 7, 6 } + }; + System.out.println(days(3, 8, d)); + System.out.println(days(2, 8, d)); + } + +} diff --git a/算法周更班/class_2021_12_5_week/Code03_WaysToBuildWall.java b/算法周更班/class_2021_12_5_week/Code03_WaysToBuildWall.java new file mode 100644 index 0000000..5cdf26b --- /dev/null +++ b/算法周更班/class_2021_12_5_week/Code03_WaysToBuildWall.java @@ -0,0 +1,64 @@ +package class_2021_12_5_week; + +// 来自hulu +// 你只有1*1、1*2、1*3、1*4,四种规格的砖块 +// 你想铺满n行m列的区域,规则如下: +// 1)不管那种规格的砖,都只能横着摆 +// 比如1*3这种规格的砖,3长度是水平方向,1长度是竖直方向 +// 2)会有很多方法铺满整个区域,整块区域哪怕有一点点不一样,就算不同的方法 +// 3)区域内部(不算区域整体的4条边界),不能有任何砖块的边界线,是从上一直贯穿到下的直线 +// 返回符合三条规则下,铺满n行m列的区域,有多少种不同的摆放方法 +public class Code03_WaysToBuildWall { + + public static long[] r = { 0, 1, 2, 4, 8 }; + + public static long ways(int n, int m) { + if (n <= 0 || m <= 1) { + return 1; + } + // len[i] = 一共有1行的情况下,列的长度为i的时候有几种摆法(所有,不分合法和非法) + long[] len = new long[m + 1]; + for (int i = 1; i <= Math.min(m, 4); i++) { + len[i] = r[i]; + } + for (int i = 5; i <= m; i++) { + len[i] = len[i - 1] + len[i - 2] + len[i - 3] + len[i - 4]; + } + // any[i] = 一共有n行的情况下,列的长度为i的时候有几种摆法(所有,不分合法和非法) + long[] any = new long[m + 1]; + for (int i = 1; i <= m; i++) { + // n * i的区域:总共的摆法!不区分合法、不合法 + any[i] = power(len[i], n); + } + // solid[i] = 一共有n行的情况下,列的长度为i的时候有几种合法的摆法 + long[] solid = new long[m + 1]; + solid[1] = 1; + for (int i = 2; i <= m; i++) { + long invalid = 0; + // N * i + // 1) (N * 1 合法) * (N * (i-1) 总共) + // 2) (N * 2 合法) * (N * (i-2) 总共) + // 3) (N * 3 合法) * (N * (i-3) 总共) + // + // j) (N * j 合法) * (N * (i-j) 总共) + for (int j = 1; j < i; j++) { + invalid += solid[j] * any[i - j]; + } + solid[i] = any[i] - invalid; + } + return solid[m]; + } + + public static long power(long base, int power) { + long ans = 1; + while (power != 0) { + if ((power & 1) != 0) { + ans *= base; + } + base *= base; + power >>= 1; + } + return ans; + } + +} diff --git a/算法周更班/class_2022_01_1_week/Code01_ABDisappear.java b/算法周更班/class_2022_01_1_week/Code01_ABDisappear.java new file mode 100644 index 0000000..c842d9d --- /dev/null +++ b/算法周更班/class_2022_01_1_week/Code01_ABDisappear.java @@ -0,0 +1,131 @@ +package class_2022_01_1_week; + +// 来自阿里 +// 给定一个只由'a'和'b'组成的字符串str, +// str中"ab"和"ba"子串都可以消除,消除之后剩下字符会重新靠在一起,继续出现可以消除的子串... +// 你的任务是决定一种消除的顺序,最后让str消除到尽可能的短 +// 返回尽可能的短的剩余字符串 +public class Code01_ABDisappear { + + // 暴力尝试,尝试所有的可能性 + // 所有情况都枚举,所有先后消除的顺序都暴力尝试 + public static String disappear1(String str) { + String ans = str; + for (int i = 1; i < str.length(); i++) { + // i == 1 0 1 是不是a和b都全,2... + // i == 2 1 2 是不是a和b都全,03.... + // i == 3 2 3 是不是a和b都全,014.... + // (i-1 i)扣掉! + boolean hasA = str.charAt(i - 1) == 'a' || str.charAt(i) == 'a'; + boolean hasB = str.charAt(i - 1) == 'b' || str.charAt(i) == 'b'; + if (hasA && hasB) { + String curAns = disappear1(str.substring(0, i - 1) + str.substring(i + 1)); + if (curAns.length() < ans.length()) { + ans = curAns; + } + } + } + return ans; + } + + // 好一点的方法尝试 + // 遇到第一个能消除的就消除, + // 剩下的字符串继续:遇到第一个能消除的就消除 + // 剩下的字符串继续:遇到第一个能消除的就消除 + // ... + public static String disappear2(String str) { + String ans = str; + for (int i = 1; i < str.length(); i++) { + boolean hasA = str.charAt(i - 1) == 'a' || str.charAt(i) == 'a'; + boolean hasB = str.charAt(i - 1) == 'b' || str.charAt(i) == 'b'; + if (hasA && hasB) { + return disappear2(str.substring(0, i - 1) + str.substring(i + 1)); + } + } + return ans; + } + + // 时间复杂度O(N) + // 利用栈 + public static String disappear3(String s) { + char[] str = s.toCharArray(); + int n = str.length; + // 用数组结构,自己实现栈 + int[] stack = new int[n]; + int size = 0; + for (int i = 0; i < n; i++) { + boolean hasA = size != 0 && str[stack[size - 1]] == 'a'; + boolean hasB = size != 0 && str[stack[size - 1]] == 'b'; + hasA |= str[i] == 'a'; + hasB |= str[i] == 'b'; + if (hasA && hasB) { + size--; + } else { + stack[size++] = i; + } + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < size; i++) { + builder.append(str[stack[i]]); + } + return builder.toString(); + } + + // 贪心,时间复杂度O(N) + // A的个数如果比B的个数多,那么一定只剩下A + // B的个数如果比A的个数多,那么一定只剩下B + public static String disappear4(String s) { + int count = 0; + for (char cha : s.toCharArray()) { + count += cha == 'a' ? 1 : -1; + } + StringBuilder builder = new StringBuilder(); + char rest = count > 0 ? 'a' : 'b'; + count = Math.abs(count); + for (int i = 0; i < count; i++) { + builder.append(rest); + } + return builder.toString(); + } + + // 为了测试 + public static String randomString(int len, int varible) { + char[] str = new char[len]; + for (int i = 0; i < str.length; i++) { + str[i] = (char) ((int) (Math.random() * varible) + 'a'); + } + return String.valueOf(str); + } + + // 为了测试 + // 对数器,三种截然不同的方法进行验证 + public static void main(String[] args) { + // 字符串最大长度,可以随意改变 + // 不过长度太大的话,方法一会超时 + int n = 14; + // 字符的种类,可以随意改变 + int v = 2; + // 测试次数 + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n) + 1; + String test = randomString(len, v); + String ans1 = disappear1(test); + String ans2 = disappear2(test); + String ans3 = disappear3(test); + String ans4 = disappear4(test); + if (!ans1.equals(ans2) || !ans1.equals(ans3) || !ans1.equals(ans4)) { + System.out.println(test); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + System.out.println(ans4); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_01_1_week/Code02_CatAndMouse.java b/算法周更班/class_2022_01_1_week/Code02_CatAndMouse.java new file mode 100644 index 0000000..2803f1a --- /dev/null +++ b/算法周更班/class_2022_01_1_week/Code02_CatAndMouse.java @@ -0,0 +1,233 @@ +package class_2022_01_1_week; + +import java.util.ArrayList; +import java.util.Arrays; + +// 测试链接 : https://leetcode.com/problems/cat-and-mouse/ +public class Code02_CatAndMouse { + + // 不贪心,就递归 + 记忆化搜索 + public static int catMouseGame1(int[][] graph) { + int n = graph.length; + // 9 : 2 * 8 * 9 再大,平局 + int limit = ((n * (n - 1)) << 1) + 1; + int[][][] dp = new int[n][n][limit]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + Arrays.fill(dp[i][j], -1); + } + } + return process(graph, limit, 2, 1, 1, dp); + } + + // 贪心 + 递归 + 记忆化搜索 + // 但是不对! + // 再次强调!不要去证明! + public static int catMouseGame2(int[][] graph) { + int n = graph.length; + // 这里! + // int limit = (n << 1) + 2; 还会出错,但是概率很小,需要多跑几次 + // int limit = (n << 1) + 3; 就没错了,或者说,概率小到很难重现 + // 为啥?我屌你为啥! + // 讲了这节课之后,leetcode又增加了测试用例 + // 导致 int limit = (n << 1) + 3; 也会出错 + // 改成如下的+12,可以通过,但是,如果去人为构造错误用例,一定还会出现错误 + int limit = (n << 1) + 12; + int[][][] dp = new int[n][n][limit]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + Arrays.fill(dp[i][j], -1); + } + } + return process(graph, limit, 2, 1, 1, dp); + } + + // dp[][][] 傻缓存! + // dp[cat][mouse][turn] == -1 这个状态之前没算过! + // dp[cat][mouse][turn] == 0 这个状态之前算过!平局! + // dp[cat][mouse][turn] == 1 这个状态之前算过!老鼠赢! + // dp[cat][mouse][turn] == 2 这个状态之前算过!猫赢! + // 固定参数!轮数不要超过limit!如果超过,就算平局! + public static int process(int[][] graph, int limit, int cat, int mouse, int turn, int[][][] dp) { + if (turn == limit) { + return 0; + } + if (dp[cat][mouse][turn] != -1) { + return dp[cat][mouse][turn]; + } + int ans = 0; + if (cat == mouse) { + ans = 2; + } else if (mouse == 0) { + ans = 1; + } else { + if ((turn & 1) == 1) { // 老鼠回合 + ans = 2; + for (int next : graph[mouse]) { + int p = process(graph, limit, cat, next, turn + 1, dp); + ans = p == 1 ? 1 : (p == 0 ? 0 : ans); + if (ans == 1) { + break; + } + } + } else { // 猫回合 + ans = 1; + for (int next : graph[cat]) { + if (next != 0) { + int p = process(graph, limit, next, mouse, turn + 1, dp); + ans = p == 2 ? 2 : (p == 0 ? 0 : ans); + if (ans == 2) { + break; + } + } + } + } + } + dp[cat][mouse][turn] = ans; + return ans; + } + + // 为了测试 + // 暴力尝试 + public static int right(int[][] graph) { + int n = graph.length; + boolean[][][] path = new boolean[n][n][2]; + return win(graph, 2, 1, 1, path); + } + + // 暴力尝试 + // 返回值:int + // 0 : 平局 + // 1 : 老鼠赢 + // 2 : 猫赢 + // int[][] graph + // 0 : {3, 7, 9} + // 1 : + // 2 : + // 3 : {0, } + // ... + // 7 : {0, } + // ... + // 9 : {0, } + // 猫此时的位置 -> cat + // mouse + // turn == 1 老鼠的回合 turn == 0 猫的回合 + // 当第一次出现,在老鼠的回合,猫在5位置 ,老鼠在7位置 + // path[cat][mouse][1] = false + // 不是第一次出现 path[cat][mouse][1] = true + public static int win(int[][] graph, int cat, int mouse, int turn, boolean[][][] path) { + if (path[cat][mouse][turn]) { // 之前来到过这个状态!平局! + return 0; + } + // 没来过! + path[cat][mouse][turn] = true; + int ans = 0; + if (cat == mouse) { + ans = 2; + } else if (mouse == 0) { + ans = 1; + } else { // 游戏继续 + if (turn == 1) { // 老鼠回合 + // 最差情况,是猫赢! + ans = 2; + for (int next : graph[mouse]) { + int p = win(graph, cat, next, 0, path); + // p的返回值如果是1,代表老鼠赢,那么最差结果ans = 1,直接返回! + ans = p == 1 ? 1 : (p == 0 ? 0 : ans); + if (ans == 1) { + break; + } + } + } else { // 猫回合 + ans = 1; + for (int next : graph[cat]) { + if (next != 0) { + int p = win(graph, next, mouse, turn ^ 1, path); + ans = p == 2 ? 2 : (p == 0 ? 0 : ans); + if (ans == 2) { + break; + } + } + } + } + } + path[cat][mouse][turn] = false; + return ans; + } + + // 为了测试 + public static int[][] randomGraph(int n) { + ArrayList> g = new ArrayList<>(); + for (int i = 0; i < n; i++) { + g.add(new ArrayList<>()); + } + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (Math.random() > 0.5) { + g.get(i).add(j); + g.get(j).add(i); + } + } + } + int[][] graph = new int[n][]; + for (int i = 0; i < n; i++) { + int m = g.get(i).size(); + graph[i] = new int[m]; + for (int j = 0; j < m; j++) { + graph[i][j] = g.get(i).get(j); + } + } + return graph; + } + + // 为了测试 + public static void main(String[] args) { + System.out.println("证什么证!?对数器万岁!"); + int N = 7; + int testTime = 3000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 3; + int[][] graph = randomGraph(n); + int ans1 = catMouseGame1(graph); + int ans2 = catMouseGame2(graph); + if (ans1 != ans2) { + for (int row = 0; row < graph.length; row++) { + System.out.print(row + " : "); + for (int next : graph[row]) { + System.out.print(next + " "); + } + System.out.println(); + } + System.out.println("ans1 : " + ans1); + System.out.println("ans2 : " + ans2); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + + // 给你记录一个错误的例子 + int[][] graph = { + // 0 : + { 2, 6, 7 }, + // 1 : + { 3, 4, 5, 7 }, + // 2 : + { 0, 3, 4, 7 }, + // 3 : + { 1, 2, 5, 6 }, + // 4 : + { 1, 2, 5, 7 }, + // 5 : + { 1, 3, 4, 6 }, + // 6 : + { 0, 3, 5, 7 }, + // 7 : + { 0, 1, 2, 4, 6 } }; + System.out.println(catMouseGame1(graph)); + System.out.println(catMouseGame2(graph)); + + } + +} diff --git a/算法周更班/class_2022_01_1_week/Code03_MaximumScoreFromPerformingMultiplicationOperations.java b/算法周更班/class_2022_01_1_week/Code03_MaximumScoreFromPerformingMultiplicationOperations.java new file mode 100644 index 0000000..9eb588b --- /dev/null +++ b/算法周更班/class_2022_01_1_week/Code03_MaximumScoreFromPerformingMultiplicationOperations.java @@ -0,0 +1,58 @@ +package class_2022_01_1_week; + +// 测试链接 : https://leetcode.com/problems/maximum-score-from-performing-multiplication-operations/ +public class Code03_MaximumScoreFromPerformingMultiplicationOperations { + + // B数组消耗完之前,A数组不会耗尽,题目输入保证的! + // A[left...right] + // B[0..i-1]已经消耗完了!B[i...m-1] + // 直到把B数组消耗完,能获得的最大分数返回 + public static int zuo(int[] A, int[] B, int left, int right) { + int leftAlready = left; + int rightAlready = A.length - right - 1; + int i = leftAlready + rightAlready; + if (i == B.length) { + return 0; + } + // 没消耗完 + int p1 = A[left] * B[i] + zuo(A, B, left + 1, right); + int p2 = A[right] * B[i] + zuo(A, B, left, right - 1); + return Math.max(p1, p2); + } + + public static int maximumScore1(int[] A, int[] B) { + if (A == null || A.length == 0 || B == null || B.length == 0 || A.length < B.length) { + return 0; + } + return process1(A, B, 0, A.length - 1); + } + + public static int process1(int[] A, int[] B, int L, int R) { + int indexB = L + A.length - R - 1; + if (indexB == B.length) { + return 0; + } else { + int p1 = A[L] * B[indexB] + process1(A, B, L + 1, R); + int p2 = A[R] * B[indexB] + process1(A, B, L, R - 1); + return Math.max(p1, p2); + } + } + + public static int maximumScore2(int[] A, int[] B) { + if (A == null || A.length == 0 || B == null || B.length == 0 || A.length < B.length) { + return 0; + } + int N = A.length; + int M = B.length; + int[][] dp = new int[M + 1][M + 1]; + for (int L = M - 1; L >= 0; L--) { + for (int j = L + 1; j <= M; j++) { + int R = N - M + j - 1; + int indexB = L + N - R - 1; + dp[L][j] = Math.max(A[L] * B[indexB] + dp[L + 1][j], A[R] * B[indexB] + dp[L][j - 1]); + } + } + return dp[0][M]; + } + +} diff --git a/算法周更班/class_2022_01_1_week/Code04_MinDistanceFromLeftUpToRightDownWalk4Directions.java b/算法周更班/class_2022_01_1_week/Code04_MinDistanceFromLeftUpToRightDownWalk4Directions.java new file mode 100644 index 0000000..9f2f6ff --- /dev/null +++ b/算法周更班/class_2022_01_1_week/Code04_MinDistanceFromLeftUpToRightDownWalk4Directions.java @@ -0,0 +1,119 @@ +package class_2022_01_1_week; + +import java.util.PriorityQueue; + +// 来自学员问题 +// 给定一个二维数组,其中全是非负数 +// 每一步都可以往上、下、左、右四个方向运动 +// 走过的路径,会沿途累加数字 +// 返回从左下角走到右下角的累加和最小的多少 +public class Code04_MinDistanceFromLeftUpToRightDownWalk4Directions { + + public static int bestWalk1(int[][] map) { + int n = map.length; + int m = map[0].length; + int step = n * m - 1; + return process(map, n, m, step, 0, 0, 0, 0); + } + + public static int process(int[][] map, int n, int m, int limit, int step, int row, int col, int cost) { + if (row < 0 || row == n || col < 0 || col == m || step > limit) { + return Integer.MAX_VALUE; + } + if (row == n - 1 && col == m - 1) { + return cost + map[row][col]; + } + cost += map[row][col]; + int p1 = process(map, n, m, limit, step + 1, row - 1, col, cost); + int p2 = process(map, n, m, limit, step + 1, row + 1, col, cost); + int p3 = process(map, n, m, limit, step + 1, row, col - 1, cost); + int p4 = process(map, n, m, limit, step + 1, row, col + 1, cost); + return Math.min(Math.min(p1, p2), Math.min(p3, p4)); + } + + public static int bestWalk2(int[][] map) { + + int n = map.length; + int m = map[0].length; + // 堆 + // 每一个对象,都是一个小数组 + // {dis, row, col} + // 0 1 2 + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + // X,0,1 已经弹出了! 以后在遇到(0,1)的事情,不管! + // poped记录哪些位置弹出,哪些没有! + boolean[][] poped = new boolean[n][m]; + heap.add(new int[] { map[0][0], 0, 0 }); + int ans = 0; + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + int dis = cur[0]; + int row = cur[1]; + int col = cur[2]; + if (poped[row][col]) { + continue; + } + // 接下来就是要处理这个位置了! + poped[row][col] = true; + if (row == n - 1 && col == m - 1) { + ans = dis; + break; + } + add(dis, row - 1, col, n, m, map, poped, heap); + add(dis, row + 1, col, n, m, map, poped, heap); + add(dis, row, col - 1, n, m, map, poped, heap); + add(dis, row, col + 1, n, m, map, poped, heap); + } + return ans; + } + + public static void add(int pre, int row, int col, int n, int m, int[][] map, boolean[][] used, + PriorityQueue heap) { + if (row >= 0 && row < n && col >= 0 && col < m && !used[row][col]) { + heap.add(new int[] { pre + map[row][col], row, col }); + } + } + + // 为了测试 + public static int[][] randomMatrix(int n, int m, int v) { + int[][] ans = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + ans[i][j] = (int) (Math.random() * v); + } + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int n = 4; + int m = 4; + int v = 10; + int testTime = 10; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int[][] map = randomMatrix(n, m, v); + int ans1 = bestWalk1(map); + int ans2 = bestWalk2(map); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + + n = 1000; + m = 1000; + v = 100; + int[][] map = randomMatrix(n, m, v); + System.out.println("性能测试开始"); + System.out.println("数据规模 : " + n + " * " + m); + System.out.println("值的范围 : 0 ~ " + v); + long start = System.currentTimeMillis(); + bestWalk2(map); + long end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒) : " + (end - start)); + System.out.println("性能测试结束"); + } + +} diff --git a/算法周更班/class_2022_01_1_week/Problem_0913_CatAndMouse.java b/算法周更班/class_2022_01_1_week/Problem_0913_CatAndMouse.java new file mode 100644 index 0000000..a02a782 --- /dev/null +++ b/算法周更班/class_2022_01_1_week/Problem_0913_CatAndMouse.java @@ -0,0 +1,93 @@ +package class_2022_01_1_week; + +import java.util.ArrayList; + +// 本题是Code02_CatAndMoused的补充code +// leetcode官网增加了测试用例 +// 进一步说明,贪心的方式其实是没有道理的 +// 不过原来贪心的方法,把源代码第35行 +// int limit = (n << 1) + 3; +// 改成: +// int limit = (n << 1) + 12; +// 依然继续能通过 +// 但是贪心依然是没道理的,人为构造的话总能找出反例来 +// 所以补充了一个拓扑排序的解法 +// 这个方法,没在课上讲,有空我们补充一下 +public class Problem_0913_CatAndMouse { + + public static final int MOUSE_TURN = 0, CAT_TURN = 1; + public static final int DRAW = 0, MOUSE_WIN = 1, CAT_WIN = 2; + + public int catMouseGame(int[][] graph) { + int n = graph.length; + int[][][] indegree = new int[n][n][2]; + for (int i = 0; i < n; i++) { + for (int j = 1; j < n; j++) { + indegree[i][j][MOUSE_TURN] = graph[i].length; + indegree[i][j][CAT_TURN] = graph[j].length; + } + } + for (int i = 0; i < n; i++) { + for (int node : graph[0]) { + indegree[i][node][CAT_TURN]--; + } + } + int[][][] ans = new int[n][n][2]; + int[][] queue = new int[n * n * 2][]; + int left = 0; + int right = 0; + for (int j = 1; j < n; j++) { + ans[0][j][MOUSE_TURN] = MOUSE_WIN; + ans[0][j][CAT_TURN] = MOUSE_WIN; + queue[right++] = new int[] { 0, j, MOUSE_TURN }; + queue[right++] = new int[] { 0, j, CAT_TURN }; + } + for (int i = 1; i < n; i++) { + ans[i][i][MOUSE_TURN] = CAT_WIN; + ans[i][i][CAT_TURN] = CAT_WIN; + queue[right++] = new int[] { i, i, MOUSE_TURN }; + queue[right++] = new int[] { i, i, CAT_TURN }; + } + while (left != right) { + int[] cur = queue[left++]; + int mouse = cur[0], cat = cur[1], turn = cur[2]; + int curAns = ans[mouse][cat][turn]; + for (int[] prevState : preStatus(graph, mouse, cat, turn)) { + int prevMouse = prevState[0], prevCat = prevState[1], prevTurn = prevState[2]; + if (ans[prevMouse][prevCat][prevTurn] == DRAW) { + boolean canWin = (curAns == MOUSE_WIN && prevTurn == MOUSE_TURN) + || (curAns == CAT_WIN && prevTurn == CAT_TURN); + if (canWin) { + ans[prevMouse][prevCat][prevTurn] = curAns; + queue[right++] = new int[] { prevMouse, prevCat, prevTurn }; + } else { + if (--indegree[prevMouse][prevCat][prevTurn] == 0) { + int lose = prevTurn == MOUSE_TURN ? CAT_WIN : MOUSE_WIN; + ans[prevMouse][prevCat][prevTurn] = lose; + queue[right++] = new int[] { prevMouse, prevCat, prevTurn }; + } + } + } + } + } + return ans[1][2][MOUSE_TURN]; + } + + public ArrayList preStatus(int[][] graph, int mouse, int cat, int turn) { + ArrayList prevStates = new ArrayList(); + int prevTurn = turn == MOUSE_TURN ? CAT_TURN : MOUSE_TURN; + if (prevTurn == MOUSE_TURN) { + for (int prev : graph[mouse]) { + prevStates.add(new int[] { prev, cat, prevTurn }); + } + } else { + for (int prev : graph[cat]) { + if (prev != 0) { + prevStates.add(new int[] { mouse, prev, prevTurn }); + } + } + } + return prevStates; + } + +} diff --git a/算法周更班/class_2022_01_2_week/Code01_StringCounts.java b/算法周更班/class_2022_01_2_week/Code01_StringCounts.java new file mode 100644 index 0000000..dd029a7 --- /dev/null +++ b/算法周更班/class_2022_01_2_week/Code01_StringCounts.java @@ -0,0 +1,13 @@ +package class_2022_01_2_week; + +// 给定一个非常大的List list +// 每一个字符串类似 : "hello,world,have,hello,world" +// 这一个字符串中,有2个hello,2个world,1个have +// 请设计一种多线程处理方案,统计list中每一个字符串,切分出来的单词数量,并且汇总 +// 最终返回一个HashMap表示每个字符串在list中一共出现几次 +public class Code01_StringCounts { + + // 多线程设计 + 算法 + // 本题没有代码实现,会在课上讲述思路 + +} diff --git a/算法周更班/class_2022_01_2_week/Code02_BrickAll.java b/算法周更班/class_2022_01_2_week/Code02_BrickAll.java new file mode 100644 index 0000000..c6c1cc9 --- /dev/null +++ b/算法周更班/class_2022_01_2_week/Code02_BrickAll.java @@ -0,0 +1,49 @@ +package class_2022_01_2_week; + +import java.util.Arrays; + +// 给定一个正数数组arr,其中每个值代表砖块长度 +// 所有砖块等高等宽,只有长度有区别 +// 每一层可以用1块或者2块砖来摆 +// 要求每一层的长度一样 +// 要求必须使用所有的砖块 +// 请问最多摆几层 +// 如果无法做到,返回-1 +public class Code02_BrickAll { + + public static int maxLevels(int[] arr) { + if (arr == null) { + return 0; + } + int n = arr.length; + if (n < 2) { + return n; + } + Arrays.sort(arr); + int p1 = levels(arr, arr[n - 1]); + int p2 = levels(arr, arr[n - 1] + arr[0]); + return Math.max(p1, p2); + } + + // 要求所有砖必须都使用,并且每一层最多两块砖,并且每一层长度都是len + // 返回能摆几层,如果无法做到返回-1 + public static int levels(int[] arr, int len) { + int ans = 0; + int L = 0; + int R = arr.length - 1; + while (L <= R) { + if (arr[R] == len) { + R--; + ans++; + } else if (L < R && arr[L] + arr[R] == len) { + L++; + R--; + ans++; + } else { + return -1; + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_01_2_week/Code03_StringNumberConvertBinaryAndHexadecimal.java b/算法周更班/class_2022_01_2_week/Code03_StringNumberConvertBinaryAndHexadecimal.java new file mode 100644 index 0000000..fbccc34 --- /dev/null +++ b/算法周更班/class_2022_01_2_week/Code03_StringNumberConvertBinaryAndHexadecimal.java @@ -0,0 +1,119 @@ +package class_2022_01_2_week; + +// 来自兴业数金 +// 给定一个字符串形式的数,比如"3421"或者"-8731" +// 如果这个数不在-32768~32767范围上,那么返回"NODATA" +// 如果这个数在-32768~32767范围上,那么这个数就没有超过16个二进制位所能表达的范围 +// 返回这个数的2进制形式的字符串和16进制形式的字符串,用逗号分割 +public class Code03_StringNumberConvertBinaryAndHexadecimal { + + // 请保证输入的num字符串一定是数字的形式 + public static String convert(String num) { + // 因为-32768~32767所有的数,最多6个字符,所以超过就返回"NODATA" + if (num == null || num.length() == 0 || num.length() > 6) { + return "NODATA"; + } + // 既然长度不超过6,那么转成整数一定不会有问题 + // 当然你也可以自己写这个转化过程,这个是比较简单的 + int n = Integer.valueOf(num); + // 如果转换完成后超过了范围,那么返回"NODATA" + if (n < -32768 || n > 32767) { + return "NODATA"; + } + // 接下来n就是一个在范围上的数字 + // 我们要取出16位信息(info),这包括: + // 提取出n的14位~0位的信息 : 也就是(n & 65535) + // 提取出第15位符号位信息 : 如果n<0,第15位就是1,如果n>=0第15位就是0 + // 然后把(15位)和(14位~0位),或在一起 + // 比如5323,32位二进制形式如下: + // 00000000000000000001010011001011 + // 14位~0位是: + // _________________001010011001011 + // 15位符号位应该是0: + // ________________0_______________ + // 所以info应该是: + // ________________0001010011001011 + // + // 再比如-6725,32位二进制形式如下: + // 11111111111111111110010110111011 + // 14位~0位是: + // _________________110010110111011 + // 15位符号位应该是1: + // ________________1_______________ + // 所以info应该是: + // ________________1110010110111011 + int info = (n < 0 ? (1 << 15) : 0) | (n & 65535); + // 此时info的15位~0位,就是我们要的;info的更高位完全没用 + StringBuilder builder = new StringBuilder(); + // 依次提取二进制信息很简单 + for (int i = 15; i >= 0; i--) { + builder.append((info & (1 << i)) != 0 ? '1' : '0'); + } + builder.append(",0x"); + // 依次提取16进制的时候,每4位提取 + // 依次提取4位信息的时候 + // 0000 -> 0 -> '0' + // 0001 -> 1 -> '1' + // ... + // 1001 -> 9 -> '9' + // 1010 -> 10 -> 'a' + // 1011 -> 11 -> 'b' + // 1100 -> 12 -> 'c' + // 1101 -> 13 -> 'd' + // 1110 -> 14 -> 'e' + // 1111 -> 15 -> 'f' + for (int i = 12; i >= 0; i -= 4) { + // 1111 << 12 + // ( info & (1111 << 12) ) >> 12 + // ( info & (1111 << 8)) >> 8 + // ( info & (1111 << 4)) >> 4 + // ( info & (1111 << 0)) >> 0 + int cur = (info & (15 << i)) >> i; + if (cur < 10) { + builder.append(cur); + } else { + switch (cur) { + case 10: + builder.append('a'); + break; + case 11: + builder.append('b'); + break; + case 12: + builder.append('c'); + break; + case 13: + builder.append('d'); + break; + case 14: + builder.append('e'); + break; + default: + builder.append('f'); + } + } + } + return builder.toString(); + } + + public static void main(String[] args) { + String num1 = "0"; + System.out.println(convert(num1)); + + String zuo = "457"; + System.out.println(convert(zuo)); + + String num2 = "-32768"; + System.out.println(convert(num2)); + + String num3 = "32768"; + System.out.println(convert(num3)); + + String num4 = "32767"; + System.out.println(convert(num4)); + + String num5 = "-1"; + System.out.println(convert(num5)); + } + +} diff --git a/算法周更班/class_2022_01_2_week/Code04_MinimumOperationsToMakeTheArrayKIncreasing.java b/算法周更班/class_2022_01_2_week/Code04_MinimumOperationsToMakeTheArrayKIncreasing.java new file mode 100644 index 0000000..06aebcc --- /dev/null +++ b/算法周更班/class_2022_01_2_week/Code04_MinimumOperationsToMakeTheArrayKIncreasing.java @@ -0,0 +1,49 @@ +package class_2022_01_2_week; + +// 来自Leetcode周赛第5题 +// 测试链接 : https://leetcode.com/problems/minimum-operations-to-make-the-array-k-increasing/ +public class Code04_MinimumOperationsToMakeTheArrayKIncreasing { + + public static int kIncreasing(int[] arr, int k) { + int n = arr.length; + // a / b = 向下取整 + // a / b 向上取整 -> (a + b - 1) / b + int[] help = new int[(n + k - 1) / k]; + int ans = 0; + for (int i = 0; i < k; i++) { + ans += need(arr, help, n, i, k); + } + return ans; + } + + // arr[start , start + k, start + 2k, start + 3k,....] + // 辅助数组help,为了求最长递增子序列,需要开辟的空间,具体看体系学习班 + // 上面的序列,要改几个数,能都有序! + public static int need(int[] arr, int[] help, int n, int start, int k) { + int j = 0; + int size = 0; + for (; start < n; start += k, j++) { + size = insert(help, size, arr[start]); + } + return j - size; + } + + public static int insert(int[] help, int size, int num) { + int l = 0; + int r = size - 1; + int m = 0; + int ans = size; + while (l <= r) { + m = (l + r) / 2; + if (help[m] > num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + help[ans] = num; + return ans == size ? size + 1 : size; + } + +} diff --git a/算法周更班/class_2022_01_2_week/Code05_MagicTowSubarrayMakeMaxSum.java b/算法周更班/class_2022_01_2_week/Code05_MagicTowSubarrayMakeMaxSum.java new file mode 100644 index 0000000..d169640 --- /dev/null +++ b/算法周更班/class_2022_01_2_week/Code05_MagicTowSubarrayMakeMaxSum.java @@ -0,0 +1,129 @@ +package class_2022_01_2_week; + +// 来自美团 +// 小美有一个长度为n的数组,为了使得这个数组的和尽量大,她向会魔法的小团进行求助 +// 小团可以选择数组中至多两个不相交的子数组,并将区间里的数全都变为原来的10倍 +// 小团想知道他的魔法最多可以帮助小美将数组的和变大到多少? +public class Code05_MagicTowSubarrayMakeMaxSum { + + // 比较暴力的方法 + // 用于对数器 + public static int maxSum1(int[] arr) { + int n = arr.length; + int[] presum = presum(arr); + int ans = maxSumAtMostOneRangeMagic(presum, 0, n - 1); + for (int split = 0; split < n - 1; split++) { + ans = Math.max(ans, + maxSumAtMostOneRangeMagic(presum, 0, split) + maxSumAtMostOneRangeMagic(presum, split + 1, n - 1)); + } + return ans; + } + + public static int[] presum(int[] arr) { + int n = arr.length; + int[] presum = new int[n]; + presum[0] = arr[0]; + for (int i = 1; i < n; i++) { + presum[i] = presum[i - 1] + arr[i]; + } + return presum; + } + + public static int sum(int[] presum, int l, int r) { + return l > r ? 0 : (l == 0 ? presum[r] : (presum[r] - presum[l - 1])); + } + + public static int maxSumAtMostOneRangeMagic(int[] presum, int l, int r) { + if (l > r) { + return 0; + } + int ans = sum(presum, l, r); + for (int s = l; s <= r; s++) { + for (int e = s; e <= r; e++) { + ans = Math.max(ans, sum(presum, l, s - 1) + sum(presum, s, e) * 10 + sum(presum, e + 1, r)); + } + } + return ans; + } + + // 正式方法,时间复杂度O(N) + public static int maxSum2(int[] arr) { + int n = arr.length; + if (n == 0) { + return 0; + } + if (n == 1) { + return Math.max(arr[0], arr[0] * 10); + } + // dp[i] + // 1) arr[0...i]原始累加和 + // 2) dp[i-1] + arr[i] + // 3) magic[i] + + // : arr[0..i]范围上,可以没有10倍区域、或者有10倍区域但是最多有一个的情况下, + // 最大累加和是多少? + // 可能性1:就是没有10倍区域,那就是arr[0..i]的累加和, 这个好弄! + // + // 可能性2:有一个10倍区域 + // a : arr[i]不在10倍区域里,但是之前可能有,那么就是dp[i-1] + arr[i] + // + // b : arr[i]在10倍区域里 + // 甲:arr[0..i-1]没有10倍区域,arr[i]自己10倍,arr[0..i-1] + 10 * arr[i] + // 乙:arr[0..i-1]中i-1位置在10倍区域里,arr[i]也在10倍区域里 + // magic[i] : magic[i] ..i i + // 对于乙,要求知道magic[j]的信息 + // magic[j]:arr[0..j]范围上,j一定要在10倍区域里,并且只有一个10倍区域的情况下,最大累加和 + // 可能性1:只有arr[j]是10倍,arr[0..j-1]没有10倍 + // 可能性2:magic[j-1] + 10 * arr[j] + int sum = arr[n - 1]; + int magic = sum * 10; + int[] right = new int[n]; + right[n - 1] = Math.max(sum, sum * 10); + for (int i = n - 2; i >= 0; i--) { + magic = 10 * arr[i] + Math.max(sum, magic); + sum += arr[i]; + right[i] = Math.max(Math.max(sum, right[i + 1] + arr[i]), magic); + } + int ans = right[0]; + sum = arr[0]; + magic = sum * 10; + int dp = Math.max(sum, sum * 10); + ans = Math.max(ans, dp + right[1]); + for (int i = 1; i < n - 1; i++) { + magic = 10 * arr[i] + Math.max(sum, magic); + sum += arr[i]; + dp = Math.max(Math.max(sum, dp + arr[i]), magic); + ans = Math.max(ans, dp + right[i + 1]); + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value) - (int) (Math.random() * value); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 30; + int value = 100; + int testTime = 500000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int ans1 = maxSum1(arr); + int ans2 = maxSum2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_01_2_week/Code06_QuietSum.java b/算法周更班/class_2022_01_2_week/Code06_QuietSum.java new file mode 100644 index 0000000..faa1584 --- /dev/null +++ b/算法周更班/class_2022_01_2_week/Code06_QuietSum.java @@ -0,0 +1,290 @@ +package class_2022_01_2_week; + +// 给定一个非负数组arr,学生依次坐在0~N-1位置,每个值表示学生的安静值 +// 如果在i位置安置插班生,那么i位置的安静值变成0,同时任何同学都会被影响到而减少安静值 +// 同学安静值减少的量: N - 这个同学到插班生的距离 +// 但是减到0以下的话,当做0处理 +// 返回一个和arr等长的ans数组,ans[i]表示如果把插班生安排在i位置,所有学生的安静值的和 +// 比如 : arr = {3,4,2,1,5},应该返回{4,3,2,3,4} +// 比如 : arr = {10,1,10,10,10},应该返回{24,27,20,20,22} +// arr长度 <= 10^5 +// arr中值 <= 2 * 10^5 +public class Code06_QuietSum { + + // 为了测试 + // 彻底暴力的方法 + public static long[] quiet1(int[] arr) { + if (arr == null || arr.length == 0) { + return new long[0]; + } + int n = arr.length; + long[] ans = new long[n]; + for (int i = 0; i < n; i++) { + long sum = 0; + for (int j = 0; j < i; j++) { + sum += Math.max(0, arr[j] - (n - Math.abs(i - j))); + } + for (int j = i + 1; j < n; j++) { + sum += Math.max(0, arr[j] - (n - Math.abs(i - j))); + } + ans[i] = sum; + } + return ans; + } + + // 时间复杂度O(N * logN)的方法 + public static long[] quiet2(int[] arr) { + if (arr == null || arr.length == 0) { + return new long[0]; + } + int n = arr.length; + SBTree sbt = new SBTree(); + long[] ans = new long[n]; + // arr[n-1] + n - 1 - n + // + sbt.add(arr[n - 1] - 1); + for (int i = n - 2; i >= 0; i--) { + long moreCount = sbt.moreCount(i); + long moreSum = sbt.moreSum(i); + ans[i] = moreSum - (moreCount * i); + sbt.add(arr[i] + i - n); + } + sbt = new SBTree(); + sbt.add(arr[0] - 1); + for (int i = 1, j = n - 2; i < n; i++, j--) { + long moreCount = sbt.moreCount(j); + long moreSum = sbt.moreSum(j); + ans[i] += moreSum - (moreCount * j); + sbt.add(arr[i] + j - n); + } + return ans; + } + + public static class SBTNode { + public int value; + // 和业务无关 + // 不同key的size,纯粹为了树的平衡调整 + public int size; + // 和业务有关 + // 加过几个数字 + public int all; + // 和业务有关 + // 子树的累加和 + public long sum; + public SBTNode l; + public SBTNode r; + + public SBTNode(int v) { + value = v; + size = 1; + all = 1; + sum = v; + } + } + + // add count(?) sum(?) logN复杂度! + // 有序表提供的性能,size - balanced - tree + public static class SBTree { + + private SBTNode root; + + private SBTNode rightRotate(SBTNode cur) { + int curCount = cur.all - (cur.l != null ? cur.l.all : 0) - (cur.r != null ? cur.r.all : 0); + long curSum = cur.sum - (cur.l != null ? cur.l.sum : 0) - (cur.r != null ? cur.r.sum : 0); + SBTNode leftNode = cur.l; + cur.l = leftNode.r; + leftNode.r = cur; + leftNode.size = cur.size; + leftNode.all = cur.all; + leftNode.sum = cur.sum; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + cur.all = (cur.l != null ? cur.l.all : 0) + (cur.r != null ? cur.r.all : 0) + curCount; + cur.sum = (cur.l != null ? cur.l.sum : 0) + (cur.r != null ? cur.r.sum : 0) + curSum; + return leftNode; + } + + private SBTNode leftRotate(SBTNode cur) { + int curCount = cur.all - (cur.l != null ? cur.l.all : 0) - (cur.r != null ? cur.r.all : 0); + long curSum = cur.sum - (cur.l != null ? cur.l.sum : 0) - (cur.r != null ? cur.r.sum : 0); + SBTNode rightNode = cur.r; + cur.r = rightNode.l; + rightNode.l = cur; + rightNode.size = cur.size; + rightNode.all = cur.all; + rightNode.sum = cur.sum; + cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1; + cur.all = (cur.l != null ? cur.l.all : 0) + (cur.r != null ? cur.r.all : 0) + curCount; + cur.sum = (cur.l != null ? cur.l.sum : 0) + (cur.r != null ? cur.r.sum : 0) + curSum; + return rightNode; + } + + private SBTNode maintain(SBTNode cur) { + if (cur == null) { + return null; + } + long leftSize = cur.l != null ? cur.l.size : 0; + long leftLeftSize = cur.l != null && cur.l.l != null ? cur.l.l.size : 0; + long leftRightSize = cur.l != null && cur.l.r != null ? cur.l.r.size : 0; + long rightSize = cur.r != null ? cur.r.size : 0; + long rightLeftSize = cur.r != null && cur.r.l != null ? cur.r.l.size : 0; + long rightRightSize = cur.r != null && cur.r.r != null ? cur.r.r.size : 0; + if (leftLeftSize > rightSize) { + cur = rightRotate(cur); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (leftRightSize > rightSize) { + cur.l = leftRotate(cur.l); + cur = rightRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } else if (rightRightSize > leftSize) { + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur = maintain(cur); + } else if (rightLeftSize > leftSize) { + cur.r = rightRotate(cur.r); + cur = leftRotate(cur); + cur.l = maintain(cur.l); + cur.r = maintain(cur.r); + cur = maintain(cur); + } + return cur; + } + + public void add(int v) { + root = add(root, v, contains(root, v)); + } + + private boolean contains(SBTNode cur, int v) { + if (cur == null) { + return false; + } else if (cur.value == v) { + return true; + } else if (cur.value > v) { + return contains(cur.l, v); + } else { + return contains(cur.r, v); + } + } + + private SBTNode add(SBTNode cur, int v, boolean contains) { + if (cur == null) { + return new SBTNode(v); + } else { + if (!contains) { + cur.size++; + } + cur.all++; + cur.sum += v; + if (cur.value == v) { + return cur; + } else if (cur.value > v) { + cur.l = add(cur.l, v, contains); + } else { + cur.r = add(cur.r, v, contains); + } + return maintain(cur); + } + } + + public long moreCount(int num) { + return moreCount(root, num); + } + + private long moreCount(SBTNode cur, int num) { + if (cur == null) { + return 0; + } + if (cur.value <= num) { + return moreCount(cur.r, num); + } else { // cur.value > num + return cur.all - (cur.l != null ? cur.l.all : 0) + moreCount(cur.l, num); + } + } + + public long moreSum(int num) { + return moreSum(root, num); + } + + private long moreSum(SBTNode cur, int num) { + if (cur == null) { + return 0; + } + if (cur.value <= num) { + return moreSum(cur.r, num); + } else { // cur.value > num + return cur.sum - (cur.l != null ? cur.l.sum : 0) + moreSum(cur.l, num); + } + } + + } + + // 为了测试 + public static int[] randomArray(int len, int v) { + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * (v + 1)); + } + return ans; + } + + public static boolean sameArray(long[] arr1, long[] arr2) { + if (arr1.length != arr2.length) { + return false; + } + int n = arr1.length; + for (int i = 0; i < n; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // 为了测试 + public static void main(String[] args) { + int N = 1000; + int V = 5000; + int testTime = 10000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * N) + 1; + int[] arr = randomArray(size, V); + long[] ans1 = quiet1(arr); + long[] ans2 = quiet2(arr); + if (!sameArray(ans1, ans2)) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + for (long num : ans1) { + System.out.print(num + " "); + } + System.out.println(); + for (long num : ans2) { + System.out.print(num + " "); + } + System.out.println(); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + N = 200000; + V = 500000; + long start; + long end; + int[] arr = randomArray(N, V); + start = System.currentTimeMillis(); + quiet2(arr); + end = System.currentTimeMillis(); + System.out.println("测试数组长度 : " + N); + System.out.println("测试数组数值最大值 : " + V); + System.out.println("运行时间(毫秒) : " + (end - start)); + System.out.println("性能测试结束"); + } + +} diff --git a/算法周更班/class_2022_01_3_week/Code01_AStarAlgorithm.java b/算法周更班/class_2022_01_3_week/Code01_AStarAlgorithm.java new file mode 100644 index 0000000..0116371 --- /dev/null +++ b/算法周更班/class_2022_01_3_week/Code01_AStarAlgorithm.java @@ -0,0 +1,195 @@ +package class_2022_01_3_week; + +import java.util.PriorityQueue; + +// A*算法 +// 过程和Dijskra高度相处 +// 有到终点的预估函数 +// 只要预估值<=客观上最优距离,就是对的 +// 预估函数是一种吸引力: +// 1)合适的吸引力可以提升算法的速度 +// 2)吸引力“过强”会出现错误 +// 讲述A*算法 +// 预估终点距离选择曼哈顿距离 +// 要求先在体系学习班图的章节听过"Dijkstra算法" +public class Code01_AStarAlgorithm { + + // Dijkstra算法 + // map[i][j] == 0 代表障碍 + // map[i][j] > 0 代表通行代价 + public static int minDistance1(int[][] map, int startX, int startY, int targetX, int targetY) { + if (map[startX][startY] == 0 || map[targetX][targetY] == 0) { + return Integer.MAX_VALUE; + } + int n = map.length; + int m = map[0].length; + // heap小根堆 + // [20,1,7] + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + // 1,7已经处理过了,closed[1][7] = true + boolean[][] closed = new boolean[n][m]; + heap.add(new int[] { map[startX][startY], startX, startY }); + int ans = Integer.MAX_VALUE; + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + int dis = cur[0]; + int row = cur[1]; + int col = cur[2]; + if (closed[row][col]) { + continue; + } + closed[row][col] = true; + if (row == targetX && col == targetY) { + ans = dis; + break; + } + add1(dis, row - 1, col, n, m, map, closed, heap); + add1(dis, row + 1, col, n, m, map, closed, heap); + add1(dis, row, col - 1, n, m, map, closed, heap); + add1(dis, row, col + 1, n, m, map, closed, heap); + } + return ans; + } + + public static void add1(int pre, int row, int col, int n, int m, int[][] map, boolean[][] closed, + PriorityQueue heap) { + if (row >= 0 && row < n && col >= 0 && col < m && map[row][col] != 0 && !closed[row][col]) { + heap.add(new int[] { pre + map[row][col], row, col }); + } + } + + // A*算法 + // map[i][j] == 0 代表障碍 + // map[i][j] > 0 代表通行代价 + public static int minDistance2(int[][] map, int startX, int startY, int targetX, int targetY) { + if (map[startX][startY] == 0 || map[targetX][targetY] == 0) { + return Integer.MAX_VALUE; + } + int n = map.length; + int m = map[0].length; + // [20,1,7] + // [20,?,1,7] + PriorityQueue heap = new PriorityQueue<>((a, b) -> (a[0] + a[1]) - (b[0] + b[1])); + boolean[][] closed = new boolean[n][m]; + heap.add(new int[] { map[startX][startY], distance(startX, startY, targetX, targetY), startX, startY }); + int ans = Integer.MAX_VALUE; + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + int fromDistance = cur[0]; + int row = cur[2]; + int col = cur[3]; + if (closed[row][col]) { + continue; + } + closed[row][col] = true; + if (row == targetX && col == targetY) { + ans = fromDistance; + break; + } + add2(fromDistance, row - 1, col, targetX, targetY, n, m, map, closed, heap); + add2(fromDistance, row + 1, col, targetX, targetY, n, m, map, closed, heap); + add2(fromDistance, row, col - 1, targetX, targetY, n, m, map, closed, heap); + add2(fromDistance, row, col + 1, targetX, targetY, n, m, map, closed, heap); + } + return ans; + } + + public static void add2(int pre, int row, int col, int targetX, int targetY, int n, int m, int[][] map, + boolean[][] closed, PriorityQueue heap) { + if (row >= 0 && row < n && col >= 0 && col < m && map[row][col] != 0 && !closed[row][col]) { + heap.add(new int[] { pre + map[row][col], distance(row, col, targetX, targetY), row, col }); + } + + } + + // 曼哈顿距离 + public static int distance(int curX, int curY, int targetX, int targetY) { + return Math.abs(targetX - curX) + Math.abs(targetY - curY); + } + + // 为了测试 + // map[i][j] == 0 代表障碍 + // map[i][j] > 0 代表通行代价 + public static int[][] randomMap(int len, int value) { + int[][] ans = new int[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + if (Math.random() < 0.2) { + ans[i][j] = 0; + } else { + ans[i][j] = (int) (Math.random() * value); + } + } + } + return ans; + } + + // 为了测试 + public static void printMap(int[][] map) { + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + System.out.print((map[i][j] == 0 ? "X" : map[i][j]) + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int len = 100; + int value = 50; + int testTime = 10000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 2; + int[][] map = randomMap(n, value); + int startX = (int) (Math.random() * n); + int startY = (int) (Math.random() * n); + int targetX = (int) (Math.random() * n); + int targetY = (int) (Math.random() * n); + int ans1 = minDistance1(map, startX, startY, targetX, targetY); + int ans2 = minDistance2(map, startX, startY, targetX, targetY); + if (ans1 != ans2) { + System.out.println("出错了!"); + printMap(map); + System.out.println(startX + "," + startY); + System.out.println(targetX + "," + targetY); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + + int[][] map = new int[4000][4000]; + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + map[i][j] = (int) (Math.random() * 10); + } + } + int startX = 0; + int startY = 0; + int targetX = 3456; + int targetY = 3728; + + long start = System.currentTimeMillis(); + minDistance2(map, startX, startY, targetX, targetY); + long end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒) : " + (end - start)); + +// System.out.println("性能测试开始"); +// int n = 10000; +// int v = 500; +// int[][] map = randomMap(n, v); +// int startX = 0; +// int startY = 0; +// int targetX = n - 1; +// int targetY = n - 1; +// System.out.println("数据规模 : " + n + " * " + n); +// long start = System.currentTimeMillis(); +// minDistance2(map, startX, startY, targetX, targetY); +// long end = System.currentTimeMillis(); +// System.out.println("运行时间(毫秒) : " + (end - start)); +// System.out.println("性能测试结束"); + } + +} diff --git a/算法周更班/class_2022_01_3_week/Code02_EscapeALargeMaze.java b/算法周更班/class_2022_01_3_week/Code02_EscapeALargeMaze.java new file mode 100644 index 0000000..1482d8d --- /dev/null +++ b/算法周更班/class_2022_01_3_week/Code02_EscapeALargeMaze.java @@ -0,0 +1,74 @@ +package class_2022_01_3_week; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; + +// 在一个10^6 * 10^6的网格中, +// source = [sx, sy]是出发位置,target = [tx, ty]是目标位置 +// 数组blocked是封锁的方格列表, +// blocked[i] = [xi, yi] 表示(xi, yi)的方格是禁止通行的 +// 每次移动都可以走上、下、左、右四个方向 +// 但是来到的位置不能在封锁列表blocked上 +// 同时不允许走出网格 +// 如果从source能到达target返回 true。否则返回false。 +// 测试链接 : https://leetcode.com/problems/escape-a-large-maze/ +// 本题最强的优化是一个剪枝 +public class Code02_EscapeALargeMaze { + + public static long offset = 1000000; + + public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) { + int n = blocked.length; + int maxPoints = n * (n - 1) / 2; + HashSet blockSet = new HashSet<>(); + for (int i = 0; i < n; i++) { + blockSet.add((long) blocked[i][0] * offset + blocked[i][1]); + } + return bfs(source[0], source[1], target[0], target[1], maxPoints, blockSet) + && bfs(target[0], target[1], source[0], source[1], maxPoints, blockSet); + } + + public static boolean bfs(int fromX, int fromY, int toX, int toY, int maxPoints, HashSet blockSet) { + HashSet visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + visited.add((long) fromX * offset + fromY); + queue.add((long) fromX * offset + fromY); + while (!queue.isEmpty() && (visited.size() <= maxPoints)) { + long cur = queue.poll(); + long curX = cur / offset; + long curY = cur - curX * offset; + if (findAndAdd(curX - 1, curY, toX, toY, blockSet, visited, queue) + || findAndAdd(curX + 1, curY, toX, toY, blockSet, visited, queue) + || findAndAdd(curX, curY - 1, toX, toY, blockSet, visited, queue) + || findAndAdd(curX, curY + 1, toX, toY, blockSet, visited, queue)) { + return true; + } + } + return visited.size() > maxPoints; + } + + // 来到的点,(row, col) + // 要寻找的目标点,toX, toY + // HashSet blockSet存着不能走的格子!障碍点! + // HashSet visited, Queue queue 为了宽度优先遍历服务的! + // visited,已经处理过的点,请不要重复的放入queue + // 如果已经到达了(toX, toY) + public static boolean findAndAdd(long row, long col, + int toX, int toY, HashSet blockSet, + HashSet visited, Queue queue) { + if (row < 0 || row == offset || col < 0 || col == offset) { + return false; + } + if (row == toX && col == toY) { + return true; + } + long value = row * offset + col; + if (!blockSet.contains(value) && !visited.contains(value)) { + visited.add(value); + queue.add(value); + } + return false; + } + +} diff --git a/算法周更班/class_2022_01_3_week/Code03_ShortestSubarrayWithSumAtLeastK.java b/算法周更班/class_2022_01_3_week/Code03_ShortestSubarrayWithSumAtLeastK.java new file mode 100644 index 0000000..59d12d3 --- /dev/null +++ b/算法周更班/class_2022_01_3_week/Code03_ShortestSubarrayWithSumAtLeastK.java @@ -0,0 +1,74 @@ +package class_2022_01_3_week; + +// 来自字节跳动 +// 给定一个数组arr,其中的值有可能正、负、0 +// 给定一个正数k +// 返回累加和>=k的所有子数组中,最短的子数组长度 +// 本题测试链接 : https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/ +public class Code03_ShortestSubarrayWithSumAtLeastK { + + public static int shortestSubarray1(int[] arr, int k) { + if (arr == null || arr.length < 1) { + return -1; + } + int n = arr.length + 1; + long[] sum = new long[n]; + for (int i = 1; i < n; i++) { + sum[i] = sum[i - 1] + arr[i - 1]; + } + int[] stack = new int[n]; + int size = 1; + int ans = Integer.MAX_VALUE; + for (int i = 1; i < n; i++) { + int mostRight = mostRight(sum, stack, size, sum[i] - k); + ans = Math.min(ans, mostRight == -1 ? Integer.MAX_VALUE : (i - mostRight)); + while (size > 0 && sum[stack[size - 1]] >= sum[i]) { + size--; + } + stack[size++] = i; + } + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + public static int mostRight(long[] sum, int[] stack, int size, long aim) { + int l = 0; + int r = size - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = (l + r) / 2; + if (sum[stack[m]] <= aim) { + ans = stack[m]; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static int shortestSubarray2(int[] arr, int K) { + int N = arr.length; + long[] sum = new long[N + 1]; + for (int i = 0; i < N; i++) { + sum[i + 1] = sum[i] + arr[i]; + } + int ans = Integer.MAX_VALUE; + int[] dq = new int[N + 1]; + int l = 0; + int r = 0; + for (int i = 0; i < N + 1; i++) { + // 头部开始,符合条件的,从头部弹出! + while (l != r && sum[i] - sum[dq[l]] >= K) { + ans = Math.min(ans, i - dq[l++]); + } + // 尾部开始,前缀和比当前的前缀和大于等于的,从尾部弹出! + while (l != r && sum[dq[r - 1]] >= sum[i]) { + r--; + } + dq[r++] = i; + } + return ans != Integer.MAX_VALUE ? ans : -1; + } + +} diff --git a/算法周更班/class_2022_01_4_week/Code01_BuyThingsAboutCollocation.java b/算法周更班/class_2022_01_4_week/Code01_BuyThingsAboutCollocation.java new file mode 100644 index 0000000..d52ffb3 --- /dev/null +++ b/算法周更班/class_2022_01_4_week/Code01_BuyThingsAboutCollocation.java @@ -0,0 +1,172 @@ +package class_2022_01_4_week; + +// things是一个N*3的二维数组,商品有N件,商品编号从1~N +// 比如things[3] = [300, 2, 6] +// 代表第3号商品:价格300,重要度2,它是6号商品的附属商品 +// 再比如things[6] = [500, 3, 0] +// 代表第6号商品:价格500,重要度3,它不是任何附属,它是主商品 +// 每件商品的收益是价格*重要度,花费就是价格 +// 如果一个商品是附属品,那么只有它附属的主商品购买了,它才能被购买 +// 任何一个附属商品,只会有1个主商品 +// 任何一个主商品的附属商品数量,不会超过2件 +// 主商品和附属商品的层级最多有2层 +// 给定二维数组things、钱数money,返回整体花费不超过money的情况下,最大的收益总和 +// 测试链接 : https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4 +// 请把如下的代码的主类名改为"Main", 可以直接通过 +import java.util.ArrayList; +import java.util.Scanner; + +public class Code01_BuyThingsAboutCollocation { + +// // index.....货自由选择!剩余的钱数是rest, +// // 返回:在不花超的情况下,返回最大的价值 +// public static int f(int[] prices, int[] values, int index, int rest) { +// if(rest < 0) { +// return -1; +// } +// // rest >= 0 +// if(index == prices.length) { // 没货了! +// return 0; +// } +// // rest >=0 有货! +// // 可能1 :index.... 自由选择,不要index位置的货! +// int p1 = f(prices, values, index + 1, rest); +// // 可能性2:index.... 自由选择,要index位置的货! +// int p2 = -1; +// int next = f(prices, values, index + 1, rest - prices[index]); +// if(next != -1) { +// p2 = values[index] + next; +// } +// return Math.max(p1, p2); +// } +// +// // 商品组 主商品,重要度3,价格9 附件!重要度6,价格7 +// // 0 : [ [3,9], [6,7], [4, 3] ] +// // 1 : [ [4,7] ] 没有附件,只有主商品 +// // 2 : [ [5,100] , [2,1] ] +// +// public static int p(int[][][] matrix, int index, int rest) { +// if(rest < 0) { +// return -1; +// } +// if(index == matrix.length) { +// return 0; +// } +// // 有商品组!也有钱>=0; +// int[][] team = matrix[index]; +// if(team.length == 1) { // 要 、不要 +// int p1 = p(matrix, index + 1, rest); +// int p2 = -1; +// int next = p(matrix, index + 1, rest - team[0][1]); +// if(next != -1) { +// p2 = team[0][0] * team[0][1] + next; +// } +// return Math.max(p1, p2); +// }else if(team.length == 2) { // a b 不要 a ab +// int[] a = team[0]; +// int[] b = team[1]; +// int p1 = p(matrix, index + 1, rest); +// +// int p2 = -1;// 只要a +// int next2 = p(matrix, index + 1, rest - a[1]); +// if(next2 != -1) { +// p2 = a[0] * a[1] + next2; +// } +// +// int p3 = -1;// 要 ab +// int next3= p(matrix, index + 1, rest - a[1] - b[1]); +// if(next3 != -1) { +// p3 = a[0] * a[1] + b[0] * b[1] + next3; +// } +// return Math.max(p1, Math.max(p2, p3)); +// }else { // a : b c 不要 a ab ac abc +// +// } +// +// +// +// } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + int money = sc.nextInt(); + int size = sc.nextInt(); + ArrayList> things = new ArrayList<>(); + things.add(new ArrayList<>()); + for (int i = 0; i < size; i++) { + ArrayList cur = new ArrayList<>(); + cur.add(new int[] { sc.nextInt(), sc.nextInt(), sc.nextInt() }); + things.add(cur); + } + int n = clean(things, size); + int ans = maxScore(things, n, money); + System.out.println(ans); + } + sc.close(); + } + + public static int clean(ArrayList> things, int size) { + for (int i = 1; i <= size; i++) { + int[] cur = things.get(i).get(0); + if (cur[2] != 0) { + things.get(i).clear(); + things.get(cur[2]).add(cur); + } + } + int n = 0; + for (int i = 0; i <= size; i++) { + if (!things.get(i).isEmpty()) { + things.set(n++, things.get(i)); + } + } + return n; + } + + public static int maxScore(ArrayList> things, int n, int money) { + int[][] dp = new int[n][money + 1]; + for (int i = 0; i < n; i++) { + for (int j = 0; j <= money; j++) { + dp[i][j] = -2; + } + } + return process(things, n, 0, money, dp); + } + + public static int process(ArrayList> things, int n, int index, int rest, int[][] dp) { + if (rest < 0) { + return -1; + } + if (index == n) { + return 0; + } + if (dp[index][rest] != -2) { + return dp[index][rest]; + } + ArrayList project = things.get(index); + int[] a = project.get(0); + int[] b = project.size() > 1 ? project.get(1) : null; + int[] c = project.size() > 2 ? project.get(2) : null; + int p1 = process(things, n, index + 1, rest, dp); + int p2 = process(things, n, index + 1, rest - a[0], dp); + if (p2 != -1) { + p2 += a[0] * a[1]; + } + int p3 = b != null ? process(things, n, index + 1, rest - a[0] - b[0], dp) : -1; + if (p3 != -1) { + p3 += a[0] * a[1] + b[0] * b[1]; + } + int p4 = c != null ? process(things, n, index + 1, rest - a[0] - c[0], dp) : -1; + if (p4 != -1) { + p4 += a[0] * a[1] + c[0] * c[1]; + } + int p5 = c != null ? process(things, n, index + 1, rest - a[0] - b[0] - c[0], dp) : -1; + if (p5 != -1) { + p5 += a[0] * a[1] + b[0] * b[1] + c[0] * c[1]; + } + int ans = Math.max(Math.max(Math.max(p1, p2), Math.max(p3, p4)), p5); + dp[index][rest] = ans; + return ans; + } + +} diff --git a/算法周更班/class_2022_01_4_week/Code02_SplitToMArraysMinScore.java b/算法周更班/class_2022_01_4_week/Code02_SplitToMArraysMinScore.java new file mode 100644 index 0000000..fd6ac74 --- /dev/null +++ b/算法周更班/class_2022_01_4_week/Code02_SplitToMArraysMinScore.java @@ -0,0 +1,178 @@ +package class_2022_01_4_week; + +import java.util.ArrayList; +import java.util.LinkedList; + +// 来自美团 +// 小团去参加军训,军训快要结束了,长官想要把大家一排n个人分成m组,然后让每组分别去参加阅兵仪式 +// 只能选择相邻的人一组,不能随意改变队伍中人的位置 +// 阅兵仪式上会进行打分,其中有一个奇怪的扣分点是每组的最大差值,即每组最大值减去最小值 +// 长官想要让这分成的m组总扣分量最小,即这m组分别的极差之和最小 +// 长官正在思索如何安排中,就让小团来帮帮他吧 +public class Code02_SplitToMArraysMinScore { + + +// public static int splitArrayMinScore(int[] arr, int k) { +// // arr[L...R] +// int[][] record = creatRecord(arr); +// return f(arr, arr.length - 1, k, record, .); +// } +// +// +// +// public static int[][] creatRecord(int[] arr) { +// +// // arr[L...R] 最大值 - 最小值 是多少? +// int n = arr.length; +// int[][] ans = new int[n][n]; +// for() { +// for() { +// +// } +// } +// +// // ans[3][6] = arr[3...6] 最大值 - 最小值 是多少? +// +// } +// +// // arr[0......7] 8 +// // arr[0...3] 4 +// // arr[0..index] index + 1 +// // arr[0....index]的数,一定要分成part组 +// // 返回最小的扣分量 +// public static int f(int[] arr, int index, int part, int[][] record) { +// if (index + 1 <= part) { +// return 0; +// } +// // 数字的个数 > 划分部分的 +// // 0...index part组 +// // 0..7 3份! +// // 最后一份,最右的一份: 7..7 +// // 最后一份,最右的一份: 6..7 之前 0...5 2份 +// // 最后一份,最右的一份: 5..7 +// // 最后一份,最右的一份: 4..7 +// // ... +// // 最后一份,最右的一份: 0..7 +// int ans = Integer.MAX_VALUE; +// for (int rightTeamFirst = index; rightTeamFirst >= 0; rightTeamFirst--) { +// int rightTeamCost = record[rightTeamFirst][index]; +// int preCost = f(arr, rightTeamFirst - 1, part - 1, record); +// int curAllCost = preCost + rightTeamCost; +// ans = Math.min(ans, curAllCost); +// } +// return ans; +// } + + // 暴力方法 + // 为了验证 + public static int minScore1(int[] arr, int m) { + if (m == 0) { + return 0; + } + return process(arr, 1, m, arr[0], arr[0]); + } + + public static int process(int[] arr, int index, int rest, int preMin, int preMax) { + if (index == arr.length) { + return rest == 1 ? (preMax - preMin) : -1; + } + int p1 = process(arr, index + 1, rest, Math.min(preMin, arr[index]), Math.max(preMax, arr[index])); + int p2next = process(arr, index + 1, rest - 1, arr[index], arr[index]); + int p2 = p2next == -1 ? -1 : (preMax - preMin + p2next); + if (p1 == -1) { + return p2; + } + if (p2 == -1) { + return p1; + } + return Math.min(p1, p2); + } + + public static int score(ArrayList> sets) { + int ans = 0; + for (LinkedList set : sets) { + if (set.isEmpty()) { + return Integer.MAX_VALUE; + } + int max = Integer.MIN_VALUE; + int min = Integer.MAX_VALUE; + for (int num : set) { + max = Math.max(max, num); + min = Math.min(min, num); + } + ans += max - min; + } + return ans; + } + + // 正式方法 + // 时间复杂度O(M * N * N) + // 特别难的一个结论 : 这道题不能利用四边形不等式,我试了 + // 这个特别难的结论不要求必须掌握,因为四边形不等式的技巧非常冷门 + public static int minScore2(int[] arr, int m) { + if (m == 0) { + return 0; + } + int n = arr.length; + int[][] score = new int[n][n]; + for (int i = 0; i < n; i++) { + int max = arr[i]; + int min = arr[i]; + score[i][i] = max - min; + for (int j = i + 1; j < n; j++) { + max = Math.max(max, arr[j]); + min = Math.min(min, arr[j]); + score[i][j] = max - min; + } + } + int[][] dp = new int[m + 1][n]; + for (int i = 0; i < n; i++) { + dp[1][i] = score[0][i]; + } + for (int split = 2; split <= m; split++) { + for (int i = split; i < n; i++) { + dp[split][i] = dp[split - 1][i]; + for (int j = 1; j <= i; j++) { + dp[split][i] = Math.min(dp[split][i], dp[split - 1][j - 1] + score[j][i]); + } + } + } + return dp[m][n - 1]; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v); + } + return arr; + } + + public static void main(String[] args) { + int len = 15; + int value = 50; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int m = (int) (Math.random() * n) + 1; + int ans1 = minScore1(arr, m); + int ans2 = minScore2(arr, m); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("m : " + m); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_01_4_week/Code03_RandomPickWithBlacklist.java b/算法周更班/class_2022_01_4_week/Code03_RandomPickWithBlacklist.java new file mode 100644 index 0000000..55f6365 --- /dev/null +++ b/算法周更班/class_2022_01_4_week/Code03_RandomPickWithBlacklist.java @@ -0,0 +1,52 @@ +package class_2022_01_4_week; + +import java.util.Arrays; +import java.util.HashMap; + +// 给定一个包含 [0,n) 中不重复整数的黑名单 blacklist +// 写一个函数从 [0, n) 中返回一个不在 blacklist 中的随机整数 +// 对它进行优化使其尽量少调用系统方法 Math.random() +// 1 <= n <= 1000000000 +// 0 <= blacklist.length < min(100000, N) +// 测试链接: https://leetcode.com/problems/random-pick-with-blacklist/ +public class Code03_RandomPickWithBlacklist { + + class Solution { + + // 0~99 所有的数字都可以随机,可能有若干黑名单! + // 填到洞里去,size -> 0~size-1 是最后调整的下标范围,紧实的! + private int size; + + // 13 -> 99 + // 27 -> 98 + private HashMap convert = new HashMap<>(); + + public Solution(int n, int[] blackArray) { + Arrays.sort(blackArray); + int m = blackArray.length; + // [34, 56, 78, 103, ..., 980] + // 0 1 2 3 100 + for (int i = 0; i < m && blackArray[i] < n; i++) { + for (n--; n > blackArray[i]; n--) { + if (n == blackArray[m - 1]) { + m--; + } else { + convert.put(blackArray[i], n); + break; + } + } + } + size = n; + } + + public int pick() { + int ans = (int) (Math.random() * size); + // 13 -> 99 + // 17 -> 98 + // 35 -> 35 + return convert.containsKey(ans) ? convert.get(ans) : ans; + } + + } + +} \ No newline at end of file diff --git a/算法周更班/class_2022_01_4_week/Code04_BattleshipsInABoard.java b/算法周更班/class_2022_01_4_week/Code04_BattleshipsInABoard.java new file mode 100644 index 0000000..3708220 --- /dev/null +++ b/算法周更班/class_2022_01_4_week/Code04_BattleshipsInABoard.java @@ -0,0 +1,21 @@ +package class_2022_01_4_week; + +// 来自米哈游 +// 测试链接 : https://leetcode.com/problems/battleships-in-a-board/ +public class Code04_BattleshipsInABoard { + + public static int countBattleships(char[][] m) { + int ans = 0; + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[0].length; j++) { + if ((m[i][j] == 'X') + && (i == 0 || m[i - 1][j] != 'X') + && (j == 0 || m[i][j - 1] != 'X')) { + ans++; + } + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_02_2_week/Code01_24Game.java b/算法周更班/class_2022_02_2_week/Code01_24Game.java new file mode 100644 index 0000000..af75442 --- /dev/null +++ b/算法周更班/class_2022_02_2_week/Code01_24Game.java @@ -0,0 +1,97 @@ +package class_2022_02_2_week; + +// 测试链接 : https://leetcode.com/problems/24-game/ +public class Code01_24Game { + + public static boolean judgePoint24(int[] cards) { + if (cards == null || cards.length == 0) { + return false; + } + int n = cards.length; + Number[] arr = new Number[n]; + for (int i = 0; i < n; i++) { + arr[i] = new Number(cards[i], 1); + } + return judge(arr, cards.length); + } + + // arr中,有效的范围arr[0...size-1] ... 再往右,都无效了,不用看了! + public static boolean judge(Number[] arr, int size) { + if (size == 1) { + return arr[0].numerator == 24 && arr[0].denominator == 1; + } + for (int i = 0; i < size; i++) { + for (int j = i + 1; j < size; j++) { + Number inum = arr[i]; + Number jnum = arr[j]; + arr[j] = arr[size - 1]; + arr[i] = add(inum, jnum); + if (judge(arr, size - 1)) { + return true; + } + arr[i] = minus(inum, jnum); + if (judge(arr, size - 1)) { + return true; + } + arr[i] = minus(jnum, inum); + if (judge(arr, size - 1)) { + return true; + } + arr[i] = multiply(inum, jnum); + if (judge(arr, size - 1)) { + return true; + } + arr[i] = divide(inum, jnum); + if (arr[i] != null && judge(arr, size - 1)) { + return true; + } + arr[i] = divide(jnum, inum); + if (arr[i] != null && judge(arr, size - 1)) { + return true; + } + arr[i] = inum; + arr[j] = jnum; + } + } + return false; + } + + public static class Number { + public int numerator; + public int denominator; + + public Number(int n, int d) { + numerator = n; + denominator = d; + } + } + + public static Number add(Number a, Number b) { + return simple(a.numerator * b.denominator + b.numerator * a.denominator, a.denominator * b.denominator); + } + + public static Number minus(Number a, Number b) { + return simple(a.numerator * b.denominator - b.numerator * a.denominator, a.denominator * b.denominator); + } + + public static Number multiply(Number a, Number b) { + return simple(a.numerator * b.numerator, a.denominator * b.denominator); + } + + public static Number divide(Number a, Number b) { + return b.numerator == 0 ? null : simple(a.numerator * b.denominator, a.denominator * b.numerator); + } + + public static Number simple(int up, int down) { + if (up == 0) { + return new Number(0, 1); + } + int gcd = Math.abs(gcd(up, down)); + return new Number(up / gcd, down / gcd); + } + + public static int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } + +} diff --git a/算法周更班/class_2022_02_2_week/Code02_DesignBitset.java b/算法周更班/class_2022_02_2_week/Code02_DesignBitset.java new file mode 100644 index 0000000..f5842f9 --- /dev/null +++ b/算法周更班/class_2022_02_2_week/Code02_DesignBitset.java @@ -0,0 +1,95 @@ +package class_2022_02_2_week; + +// 测试链接 : https://leetcode-cn.com/problems/design-bitset/ +public class Code02_DesignBitset { + + class Bitset { + + // size int 32 + // size / 32 向上取整 + private int[] bits; + private final int size; + private int zeros; + private int ones; + private boolean reverse; + + public Bitset(int n) { + bits = new int[(n + 31) / 32]; + size = n; + zeros = n; + ones = 0; + reverse = false; + } + + // 把idx位置的状态,如果是0,变成1;如果是1,没有变化! + public void fix(int idx) { + int index = idx / 32; + int bit = idx % 32; + if (!reverse) { + if ((bits[index] & (1 << bit)) == 0) { + zeros--; + ones++; + bits[index] |= (1 << bit); + } + } else { + if ((bits[index] & (1 << bit)) != 0) { + zeros--; + ones++; + bits[index] ^= (1 << bit); + } + } + } + + // 1 > 0 + public void unfix(int idx) { + int index = idx / 32; + int bit = idx % 32; + if (!reverse) { + if ((bits[index] & (1 << bit)) != 0) { + ones--; + zeros++; + bits[index] ^= (1 << bit); + } + } else { + if ((bits[index] & (1 << bit)) == 0) { + ones--; + zeros++; + bits[index] |= (1 << bit); + } + } + } + + // 0变1,1变0 + public void flip() { + reverse = !reverse; + int tmp = zeros; + zeros = ones; + ones = tmp; + } + + // 是不是所有的位都是1 + public boolean all() { + return ones == size; + } + + // 是不是至少有1位是1 + public boolean one() { + return ones > 0; + } + + // 返回1的数量! + public int count() { + return ones; + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < size; i++) { + int status = bits[i / 32] & (1 << (i % 32)); + builder.append(reverse ? (status == 0 ? '1' : '0') : (status == 0 ? '0' : '1')); + } + return builder.toString(); + } + } + +} diff --git a/算法周更班/class_2022_02_2_week/Code03_FindKthSmallestPairDistance.java b/算法周更班/class_2022_02_2_week/Code03_FindKthSmallestPairDistance.java new file mode 100644 index 0000000..225af64 --- /dev/null +++ b/算法周更班/class_2022_02_2_week/Code03_FindKthSmallestPairDistance.java @@ -0,0 +1,64 @@ +package class_2022_02_2_week; + +import java.util.Arrays; + +// 测试链接 : https://leetcode.com/problems/find-k-th-smallest-pair-distance/ +public class Code03_FindKthSmallestPairDistance { + +// public static int smallestDistancePair(int[] nums, int k) { +// int n = nums.length; +// Arrays.sort(nums); +// int l = 0; +// int r = nums[n - 1] - nums[0]; +// int ans = 0; +// while (l <= r) { +// int cnt = 0; +// int m = l + ((r - l) >> 1); +// for (int i = 0, j = 0; i < n; i++) { +// while (j < n && nums[j] <= nums[i] + m) { +// j++; +// } +// cnt += j - i - 1; +// } +// if (cnt >= k) { +// ans = m; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return ans; +// } + + public static int smallestDistancePair(int[] nums, int k) { + int n = nums.length; + Arrays.sort(nums); + int l = 0; + int r = nums[n - 1] - nums[0]; + int ans = 0; + while (l <= r) { + int dis = l + ((r - l) >> 1); + int cnt = f(nums, dis); + if (cnt >= k) { + ans = dis; + r = dis - 1; + } else { + l = dis + 1; + } + } + return ans; + } + + // <= dis的数字对,有几个,返回 + public static int f(int[] arr, int dis) { + int cnt = 0; + for (int l = 0, r = 0; l < arr.length; l++) { + while (r < arr.length && arr[r] <= arr[l] + dis) { + r++; + } + cnt += r - l - 1; + } + return cnt; + } + +} diff --git a/算法周更班/class_2022_02_2_week/Code04_ReachingPoints.java b/算法周更班/class_2022_02_2_week/Code04_ReachingPoints.java new file mode 100644 index 0000000..b4f2e39 --- /dev/null +++ b/算法周更班/class_2022_02_2_week/Code04_ReachingPoints.java @@ -0,0 +1,38 @@ +package class_2022_02_2_week; + +// 测试链接 : https://leetcode.com/problems/reaching-points/ +public class Code04_ReachingPoints { + + // 会超时,但是揭示了大思路 + public static boolean reachingPoints1(int sx, int sy, int tx, int ty) { + while (tx != ty) { + if (tx < ty) { + ty -= tx; + } else { + tx -= ty; + } + if (sx == tx && sy == ty) { + return true; + } + } + return false; + } + + // 对大体思路的优化 + // s ( 5, 10) + // t (100, 65) + public static boolean reachingPoints2(int sx, int sy, int tx, int ty) { + while (sx < tx && sy < ty) { + if (tx < ty) { + ty %= tx; + } else { + tx %= ty; + } + } + // 1) startx >= tx + // 2) starty >= ty + return (sx == tx && sy <= ty && (ty - sy) % sx == 0) + || (sy == ty && sx <= tx && (tx - sx) % sy == 0); + } + +} diff --git a/算法周更班/class_2022_02_2_week/Code05_RecoverTheOriginalArray.java b/算法周更班/class_2022_02_2_week/Code05_RecoverTheOriginalArray.java new file mode 100644 index 0000000..43c665b --- /dev/null +++ b/算法周更班/class_2022_02_2_week/Code05_RecoverTheOriginalArray.java @@ -0,0 +1,48 @@ +package class_2022_02_2_week; + +import java.util.Arrays; + +// 测试链接 : https://leetcode.com/problems/recover-the-original-array/ +public class Code05_RecoverTheOriginalArray { + + public static int[] recoverArray(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + // nums[0] -> 小数组的第0个 + int m = n >> 1; + // 谁是大数组的第0个?不知道,试!first位置的数! + for (int first = 1; first <= m; first++) { + // d = 2 * k; k正数! + int d = nums[first] - nums[0]; + if (d > 0 && (d & 1) == 0) { + // 试图生成原始数组!ans! + int[] ans = new int[m]; + int i = 0; + boolean[] set = new boolean[n]; + int k = d >> 1; + int l = 0; + int r = first; + while (r < n) { + while (set[l]) { + l++; + } + if (l == r) { + r++; + } else if (nums[r] - nums[l] > d) { + break; + } else if (nums[r] - nums[l] < d) { + r++; + } else { + set[r++] = true; + ans[i++] = nums[l++] + k; + } + } + if (i == m) { + return ans; + } + } + } + return null; + } + +} diff --git a/算法周更班/class_2022_02_3_week/Code01_CheapestFlightsWithinKStops.java b/算法周更班/class_2022_02_3_week/Code01_CheapestFlightsWithinKStops.java new file mode 100644 index 0000000..5833260 --- /dev/null +++ b/算法周更班/class_2022_02_3_week/Code01_CheapestFlightsWithinKStops.java @@ -0,0 +1,75 @@ +package class_2022_02_3_week; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map.Entry; + +// 测试链接 : https://leetcode.com/problems/cheapest-flights-within-k-stops/ +public class Code01_CheapestFlightsWithinKStops { + + // 类似宽度优先遍历 + // n,城市数量,0...n-1 + // flights = { {0,7,100}, {15,3,300} .... } + // src -> 源点 + // dst -> 目标点 + // k -> 最多能中转几次 + public static int findCheapestPrice1(int n, int[][] flights, int src, int dst, int k) { + + // 图 + // 0 : {1, 500} {7, 20} {13, 70} + ArrayList> graph = new ArrayList<>(); + for (int i = 0; i < n; i++) { + graph.add(new ArrayList()); + } + for (int[] line : flights) { + // [0, 7 , 200] + // 0 : { {7, 200} } + graph.get(line[0]).add(new int[] { line[1], line[2] }); + } + // distance[i] : 从源点src出发的情况下,到达i号点,最少费用是多少 + int[] cost = new int[n]; + Arrays.fill(cost, Integer.MAX_VALUE); + cost[src] = 0; + HashMap curMap = new HashMap<>(); + curMap.put(src, 0); + for (int i = 0; i <= k; i++) { + HashMap nextMap = new HashMap<>(); + for (Entry entry : curMap.entrySet()) { + // A -> from 100 + // from -> ???? + int from = entry.getKey(); + int preCost = entry.getValue(); + for (int[] line : graph.get(from)) { + int to = line[0]; + int curCost = line[1]; + cost[to] = Math.min(cost[to], preCost + curCost); + nextMap.put(to, + Math.min(nextMap.getOrDefault(to, Integer.MAX_VALUE), + preCost + curCost)); + } + } + curMap = nextMap; + } + return cost[dst] == Integer.MAX_VALUE ? -1 : cost[dst]; + } + + // Bellman Ford + // 快! + public static int findCheapestPrice2(int n, int[][] flights, int src, int dst, int k) { + int[] cost = new int[n]; + Arrays.fill(cost, Integer.MAX_VALUE); + cost[src] = 0; + for (int i = 0; i <= k; i++) { + int[] next = Arrays.copyOf(cost, n); + for (int[] f : flights) { + if (cost[f[0]] != Integer.MAX_VALUE) { + next[f[1]] = Math.min(next[f[1]], cost[f[0]] + f[2]); + } + } + cost = next; + } + return cost[dst] == Integer.MAX_VALUE ? -1 : cost[dst]; + } + +} diff --git a/算法周更班/class_2022_02_3_week/Code02_MinimumNumberOfDaysToEatNOranges.java b/算法周更班/class_2022_02_3_week/Code02_MinimumNumberOfDaysToEatNOranges.java new file mode 100644 index 0000000..ff78efd --- /dev/null +++ b/算法周更班/class_2022_02_3_week/Code02_MinimumNumberOfDaysToEatNOranges.java @@ -0,0 +1,35 @@ +package class_2022_02_3_week; + +import java.util.HashMap; + +// 来自学员看到的腾讯面试 +// 测试链接 : https://leetcode.com/problems/minimum-number-of-days-to-eat-n-oranges/ +public class Code02_MinimumNumberOfDaysToEatNOranges { + + // 所有的答案都填在这个表里 + // 这个表对所有的过程共用 + public static HashMap dp = new HashMap<>(); + + public static int minDays(int n) { + if (n <= 1) { + return n; + } + if (dp.containsKey(n)) { + return dp.get(n); + } + // 1) 吃掉一个橘子 + // 2) 如果n能被2整除,吃掉一半的橘子,剩下一半 + // 3) 如果n能被3正数,吃掉三分之二的橘子,剩下三分之一 + // 因为方法2)和3),是按比例吃橘子,所以必然会非常快 + // 所以,决策如下: + // 可能性1:为了使用2)方法,先把橘子吃成2的整数倍,然后直接干掉一半,剩下的n/2调用递归 + // 即,n % 2 + 1 + minDays(n/2) + // 可能性2:为了使用3)方法,先把橘子吃成3的整数倍,然后直接干掉三分之二,剩下的n/3调用递归 + // 即,n % 3 + 1 + minDays(n/3) + // 至于方法1),完全是为了这两种可能性服务的,因为能按比例吃,肯定比一个一个吃快(显而易见的贪心) + int ans = Math.min(n % 2 + 1 + minDays(n / 2), n % 3 + 1 + minDays(n / 3)); + dp.put(n, ans); + return ans; + } + +} diff --git a/算法周更班/class_2022_02_3_week/Code03_RobotBoundedInCircle.java b/算法周更班/class_2022_02_3_week/Code03_RobotBoundedInCircle.java new file mode 100644 index 0000000..b13dd5e --- /dev/null +++ b/算法周更班/class_2022_02_3_week/Code03_RobotBoundedInCircle.java @@ -0,0 +1,40 @@ +package class_2022_02_3_week; + +// 测试链接 : https://leetcode.com/problems/robot-bounded-in-circle/ +public class Code03_RobotBoundedInCircle { + + public static boolean isRobotBounded(String ins) { + int row = 0; + int col = 0; + int direction = 0; // 0 1 2 3 + char[] str = ins.toCharArray(); + for (char cur : str) { + if (cur == 'R') { + direction = right(direction); + } else if (cur == 'L') { + direction = left(direction); + } else { + row = row(direction, row); + col = col(direction, col); + } + } + return row == 0 && col == 0 || direction != 0; + } + + public static int left(int direction) { + return direction == 0 ? 3 : (direction - 1); + } + + public static int right(int direction) { + return direction == 3 ? 0 : (direction + 1); + } + + public static int row(int direction, int r) { + return (direction == 1 || direction == 3) ? r : (r + (direction == 0 ? 1 : -1)); + } + + public static int col(int direction, int c) { + return (direction == 0 || direction == 2) ? c : (c + (direction == 1 ? 1 : -1)); + } + +} diff --git a/算法周更班/class_2022_02_3_week/Code04_MaxTeamNumber.java b/算法周更班/class_2022_02_3_week/Code04_MaxTeamNumber.java new file mode 100644 index 0000000..75a5ecc --- /dev/null +++ b/算法周更班/class_2022_02_3_week/Code04_MaxTeamNumber.java @@ -0,0 +1,94 @@ +package class_2022_02_3_week; + +import java.util.Arrays; + +// 来自微软 +// 给定一个数组arr,一个正数num,一个正数k +// 可以把arr中的某些数字拿出来组成一组,要求该组中的最大值减去最小值<=num +// 且该组数字的个数一定要正好等于k +// 每个数字只能选择进某一组,不能进多个组 +// 返回arr中最多有多少组 +public class Code04_MaxTeamNumber { + + // 对数器方法 + public static int maxTeams1(int[] arr, int num, int k) { + Arrays.sort(arr); + return process1(arr, 0, new int[arr.length], 0, num, k); + } + + public static int process1(int[] arr, int index, int[] path, int size, int num, int k) { + if (index == arr.length) { + if (size % k != 0) { + return 0; + } else { + for (int start = 0; start < size; start += k) { + if (path[start + k - 1] - path[start] > num) { + return 0; + } + } + return size / k; + } + } else { + int p1 = process1(arr, index + 1, path, size, num, k); + path[size] = arr[index]; + int p2 = process1(arr, index + 1, path, size + 1, num, k); + return Math.max(p1, p2); + } + } + + // 正式方法 + // 时间复杂度O(N * logN) + public static int maxTeams2(int[] arr, int num, int k) { + int n = arr.length; + if (k > n) { + return 0; + } + Arrays.sort(arr); + int[] dp = new int[n]; + dp[k - 1] = arr[k - 1] - arr[0] <= num ? 1 : 0; + for (int i = k; i < n; i++) { + int p1 = dp[i - 1]; + int p2 = (arr[i] - arr[i - k + 1] <= num ? 1 : 0) + dp[i - k]; + dp[i] = Math.max(p1, p2); + } + return dp[n - 1]; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * value); + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int n = 18; + int v = 50; + int testTimes = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int len = (int) (Math.random() * n) + 1; + int[] arr = randomArray(len, v); + int num = (int) (Math.random() * v) + 1; + int k = (int) (Math.random() * len) + 1; + int ans1 = maxTeams1(arr, num, k); + int ans2 = maxTeams2(arr, num, k); + if (ans1 != ans2) { + for (int number : arr) { + System.out.print(number + " "); + } + System.out.println(); + System.out.println("num : " + num); + System.out.println("k : " + k); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_02_3_week/Code05_StoneGameIX.java b/算法周更班/class_2022_02_3_week/Code05_StoneGameIX.java new file mode 100644 index 0000000..86015f7 --- /dev/null +++ b/算法周更班/class_2022_02_3_week/Code05_StoneGameIX.java @@ -0,0 +1,16 @@ +package class_2022_02_3_week; + +// 测试链接 : https://leetcode.com/problems/stone-game-ix/ +public class Code05_StoneGameIX { + + public static boolean stoneGameIX(int[] stones) { + int[] counts = new int[3]; + for (int num : stones) { + counts[num % 3]++; + } + return counts[0] % 2 == 0 + ? counts[1] != 0 && counts[2] != 0 + : Math.abs(counts[1] - counts[2]) > 2; + } + +} diff --git a/算法周更班/class_2022_02_4_week/Code01_SplitSameNumberWays.java b/算法周更班/class_2022_02_4_week/Code01_SplitSameNumberWays.java new file mode 100644 index 0000000..122d3ad --- /dev/null +++ b/算法周更班/class_2022_02_4_week/Code01_SplitSameNumberWays.java @@ -0,0 +1,36 @@ +package class_2022_02_4_week; + +// 来自微软 +// 比如,str = "ayxbx" +// 有以下4种切法 : a | yxbx、ay | xbx、ayx | bx、ayxb | x +// 其中第1、3、4种切法符合:x和y的个数,至少在左右两块中的一块里有相同的数量 +// 所以返回3 +// 给定一个字符串str,长度为N +// 你有N-1种划分方法,把str切成左右两半,返回有几种切法满足: +// x和y的个数,至少在左右两块中的一块里有相同的数量 +public class Code01_SplitSameNumberWays { + + public static int splitSameNumberWays(char[] str) { + if (str == null || str.length == 0) { + return 0; + } + int xAll = 0; + int yAll = 0; + for (char c : str) { + xAll += c == 'x' ? 1 : 0; + yAll += c == 'y' ? 1 : 0; + } + int leftX = str[0] == 'x' ? 1 : 0; + int leftY = str[0] == 'y' ? 1 : 0; + int ans = 0; + for (int i = 1; i < str.length; i++) { + if (leftX == leftY || (xAll - leftX) == (yAll - leftY)) { + ans++; + } + leftX += str[i] == 'x' ? 1 : 0; + leftY += str[i] == 'y' ? 1 : 0; + } + return ans; + } + +} diff --git a/算法周更班/class_2022_02_4_week/Code02_NearBiggerNoSameNeighbour.java b/算法周更班/class_2022_02_4_week/Code02_NearBiggerNoSameNeighbour.java new file mode 100644 index 0000000..5ad0e47 --- /dev/null +++ b/算法周更班/class_2022_02_4_week/Code02_NearBiggerNoSameNeighbour.java @@ -0,0 +1,69 @@ +package class_2022_02_4_week; + +// 来自微软 +// 给定一个正数num,要返回一个大于num的数,并且每一位和相邻位的数字不能相等 +// 返回达标的数字中,最小的那个 +// 10^9 +public class Code02_NearBiggerNoSameNeighbour { + + public static int near(int num) { + // num = 174 + // "0175" + // num = 899 + // "0900" + // num = 999 + // "01000" + char[] raw = ("0" + String.valueOf(num + 1)).toCharArray(); + process(raw); + return Integer.valueOf(String.valueOf(raw)); + } + + public static void process(char[] raw) { + for (int i = 1; i < raw.length; i++) { + if (raw[i - 1] == raw[i]) { + addOne(raw, i); + for (int j = i + 1; j < raw.length; j++) { + raw[j] = '0'; + } + process(raw); + return; + } + } + } + + // 99..... + // +1 + //100 + public static void addOne(char[] r, int i) { + boolean up = true; + while (up && r[i] == '9') { + r[i--] = '0'; + } + r[i]++; + } + + public static void main(String[] args) { + char[] test = new char[] { '0', '1', '2', '3' }; + + System.out.println(Integer.valueOf(String.valueOf(test))); + + int num1 = 55; + System.out.println(near(num1)); + + int num2 = 1765; + System.out.println(near(num2)); + + int num3 = 98; + System.out.println(near(num3)); + + int num4 = 44432; + System.out.println(near(num4)); + + int num5 = 3298; + System.out.println(near(num5)); + + int num6 = 9999998; + System.out.println(near(num6)); + } + +} diff --git a/算法周更班/class_2022_02_4_week/Code03_PartitionArrayForMaximumSum.java b/算法周更班/class_2022_02_4_week/Code03_PartitionArrayForMaximumSum.java new file mode 100644 index 0000000..43a70ac --- /dev/null +++ b/算法周更班/class_2022_02_4_week/Code03_PartitionArrayForMaximumSum.java @@ -0,0 +1,53 @@ +package class_2022_02_4_week; + +import java.util.Arrays; + +// 测试链接 : https://leetcode.com/problems/partition-array-for-maximum-sum/ +public class Code03_PartitionArrayForMaximumSum { + + public static int maxSumAfterPartitioning1(int[] arr, int k) { + int[] dp = new int[arr.length]; + Arrays.fill(dp, -1); + return process1(arr, k, arr.length - 1, dp); + } + + // 永远不变的固定参数 : arr, k + // 可变参数 : index + // 缓存 : dp + // process含义 : arr[0...index]最优划分搞出的最大和是多少,返回 + public static int process1(int[] arr, int k, int index, int[] dp) { + if (index == -1) { + return 0; + } + if (dp[index] != -1) { + return dp[index]; + } + int max = Integer.MIN_VALUE; + int ans = Integer.MIN_VALUE; + for (int i = index, j = 1; i >= 0 && j <= k; i--, j++) { + max = Math.max(max, arr[i]); + ans = Math.max(ans, process1(arr, k, i - 1, dp) + (index - i + 1) * max); + } + dp[index] = ans; + return ans; + } + + public static int maxSumAfterPartitioning2(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return 0; + } + int n = arr.length; + int[] dp = new int[n]; + dp[0] = arr[0]; + for (int i = 1; i < n; i++) { + dp[i] = arr[i] + dp[i - 1]; + int max = arr[i]; + for (int j = i - 1; j >= 0 && (i - j + 1) <= k; j--) { + max = Math.max(max, arr[j]); + dp[i] = Math.max(dp[i], max * (i - j + 1) + (j - 1 >= 0 ? dp[j - 1] : 0)); + } + } + return dp[n - 1]; + } + +} diff --git a/算法周更班/class_2022_02_4_week/Code04_NumberOfDescendingTriples.java b/算法周更班/class_2022_02_4_week/Code04_NumberOfDescendingTriples.java new file mode 100644 index 0000000..18f929c --- /dev/null +++ b/算法周更班/class_2022_02_4_week/Code04_NumberOfDescendingTriples.java @@ -0,0 +1,134 @@ +package class_2022_02_4_week; + +import java.util.Arrays; + +// 返回一个数组中,所有降序三元组的数量 +// 比如 : {5, 3, 4, 2, 1} +// 所有降序三元组为 : +// {5, 3, 2}、{5, 3, 1}、{5, 4, 2}、{5, 4, 1} +// {5, 2, 1}、{3, 2, 1}、{4, 2, 1} +// 所以返回数量7 +public class Code04_NumberOfDescendingTriples { + + // 暴力方法 + // 对数器 + public static int num1(int[] arr) { + if (arr == null || arr.length < 3) { + return 0; + } + return process(arr, 0, new int[3], 0); + } + + public static int process(int[] arr, int index, int[] path, int size) { + if (size == 3) { + return path[0] > path[1] && path[1] > path[2] ? 1 : 0; + } + int ans = 0; + if (index < arr.length) { + ans = process(arr, index + 1, path, size); + path[size] = arr[index]; + ans += process(arr, index + 1, path, size + 1); + } + return ans; + } + + // 正式方法 + // 时间复杂度O(N * logN) + // 利用index tree + public static int num2(int[] arr) { + if (arr == null || arr.length < 3) { + return 0; + } + int n = arr.length; + int[] sorted = Arrays.copyOf(arr, n); + Arrays.sort(sorted); + int max = Integer.MIN_VALUE; + for (int i = 0; i < n; i++) { + arr[i] = rank(sorted, arr[i]); + max = Math.max(max, arr[i]); + } + IndexTree it2 = new IndexTree(max); + IndexTree it3 = new IndexTree(max); + int ans = 0; + for (int i = n - 1; i >= 0; i--) { + ans += arr[i] == 1 ? 0 : it3.sum(arr[i] - 1); + it2.add(arr[i], 1); + it3.add(arr[i], arr[i] == 1 ? 0 : it2.sum(arr[i] - 1)); + } + return ans; + } + + // 返回>=num, 最左位置 + public static int rank(int[] sorted, int num) { + int l = 0; + int r = sorted.length - 1; + int m = 0; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (sorted[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans + 1; + } + + // 下标从1开始 + public static class IndexTree { + + private int[] tree; + private int N; + + // 0位置弃而不用 + public IndexTree(int size) { + N = size; + tree = new int[N + 1]; + } + + // 1~index 累加和是多少? + public int sum(int index) { + int ret = 0; + while (index > 0) { + ret += tree[index]; + index -= index & -index; + } + return ret; + } + + public void add(int index, int d) { + while (index <= N) { + tree[index] += d; + index += index & -index; + } + } + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * value) - (int) (Math.random() * value); + } + return arr; + } + + public static void main(String[] args) { + int len = 20; + int value = 100; + int testTimes = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int[] arr = randomArray(len, value); + int ans1 = num1(arr); + int ans2 = num2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_02_4_week/Code05_GroupsOfStrings.java b/算法周更班/class_2022_02_4_week/Code05_GroupsOfStrings.java new file mode 100644 index 0000000..9a77b32 --- /dev/null +++ b/算法周更班/class_2022_02_4_week/Code05_GroupsOfStrings.java @@ -0,0 +1,195 @@ +package class_2022_02_4_week; + +import java.util.HashMap; + +// 测试链接 : https://leetcode.com/problems/groups-of-strings/ +public class Code05_GroupsOfStrings { + + // 可能会超时,或者打败比例很低 + // 因为常数优化不到位 + public static int[] groupStrings1(String[] words) { + int n = words.length; + // 0 1 2 ... n-1 + UnionFind uf = new UnionFind(n); + int[] strs = new int[n]; + // abd -> 0..01011 7 + // 0..01011 key value 7 + HashMap stands = new HashMap<>(); + for (int i = 0; i < n; i++) { + int status = 0; + for (char c : words[i].toCharArray()) { + status |= 1 << (c - 'a'); + } + strs[i] = status; + if (stands.containsKey(status)) { + uf.union(stands.get(status), i); + } else { + stands.put(status, i); + } + } + for (int i = 0; i < n; i++) { + // 一个字符串,状态 + int status = strs[i]; + for (int j = 0; j < 26; j++) { + // 001101 + // a + // 001101 + // b + // 001111 + // c + // 001101 + // z + //.. + uf.union(i, stands.get(status | (1 << j))); + } + // 有的字符,减少一遍 + for (int j = 0; j < 26; j++) { + if ((status & (1 << j)) != 0) { + uf.union(i, stands.get(status ^ (1 << j))); + } + } + for (int has = 0; has < 26; has++) { + if ((status & (1 << has)) != 0) { + status ^= 1 << has; + for (int replace = 0; replace < 26; replace++) { + uf.union(i, stands.get(status | (1 << replace))); + } + status |= 1 << has; + } + } + } + return new int[] { uf.sets(), uf.maxSize() }; + } + + // 肯定通过 + // 打败比例达标 + // 优化了常数时间 + public static int[] groupStrings2(String[] words) { + int n = words.length; + UnionFind uf = new UnionFind(n); + int[] strs = new int[n]; + HashMap stands = new HashMap<>(); + for (int i = 0; i < n; i++) { + int status = 0; + for (char c : words[i].toCharArray()) { + status |= 1 << (c - 'a'); + } + strs[i] = status; + if (stands.containsKey(status)) { + uf.union(stands.get(status), i); + } else { + stands.put(status, i); + } + } + for (int i = 0; i < n; i++) { + int yes = strs[i]; + int no = (~yes) & ((1 << 26) - 1); + int tmpYes = yes; + int tmpNo = no; + int rightOneYes = 0; + int rightOneNo = 0; + + + // 0....0 0110011 + // + // 0....0 0110011 + // 0....0 0000001 -> 用 + + // 0....0 0110010 + // 0....0 0000010 -> 用 + + // 0....0 0110000 + + while (tmpYes != 0) { + rightOneYes = tmpYes & (-tmpYes); + uf.union(i, stands.get(yes ^ rightOneYes)); + tmpYes ^= rightOneYes; + } + + + + + + // tmpNo = 该去试试什么添加! + while(tmpNo != 0) { + rightOneNo = tmpNo & (-tmpNo); + uf.union(i, stands.get(yes | rightOneNo)); + tmpNo ^= rightOneNo; + } + tmpYes = yes; + while (tmpYes != 0) { + rightOneYes = tmpYes & (-tmpYes); + tmpNo = no; + while (tmpNo != 0) { + rightOneNo = tmpNo & (-tmpNo); + uf.union(i, stands.get((yes ^ rightOneYes) | rightOneNo)); + tmpNo ^= rightOneNo; + } + tmpYes ^= rightOneYes; + } + } + return new int[] { uf.sets(), uf.maxSize() }; + } + + public static class UnionFind { + private int[] parent; + private int[] size; + private int[] help; + + public UnionFind(int N) { + parent = new int[N]; + size = new int[N]; + help = new int[N]; + for (int i = 0; i < N; i++) { + parent[i] = i; + size[i] = 1; + } + } + + private int find(int i) { + int hi = 0; + while (i != parent[i]) { + help[hi++] = i; + i = parent[i]; + } + for (hi--; hi >= 0; hi--) { + parent[help[hi]] = i; + } + return i; + } + + public void union(Integer i, Integer j) { + if (i == null || j == null) { + return; + } + int f1 = find(i); + int f2 = find(j); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + size[f1] += size[f2]; + parent[f2] = f1; + } else { + size[f2] += size[f1]; + parent[f1] = f2; + } + } + } + + public int sets() { + int ans = 0; + for (int i = 0; i < parent.length; i++) { + ans += parent[i] == i ? 1 : 0; + } + return ans; + } + + public int maxSize() { + int ans = 0; + for (int s : size) { + ans = Math.max(ans, s); + } + return ans; + } + } + +} diff --git a/算法周更班/class_2022_03_1_week/Code01_StronglyConnectedComponents.java b/算法周更班/class_2022_03_1_week/Code01_StronglyConnectedComponents.java new file mode 100644 index 0000000..a5886d4 --- /dev/null +++ b/算法周更班/class_2022_03_1_week/Code01_StronglyConnectedComponents.java @@ -0,0 +1,105 @@ +package class_2022_03_1_week; + +import java.util.ArrayList; + +// tarjan算法求有向图的强连通分量 +public class Code01_StronglyConnectedComponents { + + public static class StronglyConnectedComponents { + public ArrayList> nexts; + public int n; + public int[] stack; + public int stackSize; + public int[] dfn; + public int[] low; + public int cnt; + public int[] scc; + public int sccn; + + // 请保证点的编号从1开始,不从0开始 + // 注意: + // 如果edges里有0、1、2...n这些点,那么容器edges的大小为n+1 + // 但是0点是弃而不用的,所以1..n才是有效的点,所以有效大小是n + public StronglyConnectedComponents(ArrayList> edges) { + nexts = edges; + init(); + scc(); + } + + private void init() { + n = nexts.size(); + stack = new int[n]; + stackSize = 0; + dfn = new int[n]; + low = new int[n]; + cnt = 0; + scc = new int[n]; + sccn = 0; + n--; + } + + private void scc() { + for (int i = 1; i <= n; i++) { + if (dfn[i] == 0) { + tarjan(i); + } + } + } + + // low[] + // dfn[] + // stack[] + // int stackSize + // boolean isStack[] + // int cnt; + // int sccn; + // scc[] + private void tarjan(int p) { + low[p] = dfn[p] = ++cnt; + stack[stackSize++] = p; + for (int q : nexts.get(p)) { + // q 当前p的每一个孩子 + if (dfn[q] == 0) { + tarjan(q); + } + // q 肯定遍历过 1) 遍历过,结算了!2)遍历过,没结算 + if (scc[q] == 0) { // scc[q]!=0 q已经属于某个集团了!不能用来更新 + low[p] = Math.min(low[p], low[q]); + } + } + if (low[p] == dfn[p]) { + sccn++; + int top = 0; + do { + top = stack[--stackSize]; + scc[top] = sccn; + } while (top != p); + } + } + + public int[] getScc() { + return scc; + } + + public int getSccn() { + return sccn; + } + + public ArrayList> getShortGraph() { + ArrayList> shortGraph = new ArrayList>(); + for (int i = 0; i <= sccn; i++) { + shortGraph.add(new ArrayList()); + } + for (int u = 1; u <= n; u++) { + for (int v : nexts.get(u)) { + if (scc[u] != scc[v]) { + shortGraph.get(scc[u]).add(scc[v]); + } + } + } + return shortGraph; + } + + } + +} diff --git a/算法周更班/class_2022_03_1_week/Code02_NetworkOfSchools.java b/算法周更班/class_2022_03_1_week/Code02_NetworkOfSchools.java new file mode 100644 index 0000000..4c280cc --- /dev/null +++ b/算法周更班/class_2022_03_1_week/Code02_NetworkOfSchools.java @@ -0,0 +1,148 @@ +package class_2022_03_1_week; + +// 强连通分量练习题目 +// N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输 +// 问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件 +// 问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后 +// 经过若干次传送,网络内所有的学校最终都能得到软件 +// 2 <= N <= 1000 +// 从题意中抽象出的算法模型, 给定一个有向图,求: +// 1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点 +// 2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点 +// 测试链接 : http://poj.org/problem?id=1236 +// 注册一下 -> 页面上点击"submit" -> 语言选择java +// 然后把如下代码粘贴进去, 把主类名改成"Main", 可以直接通过 +import java.util.ArrayList; +import java.util.Scanner; + +public class Code02_NetworkOfSchools { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + int n = sc.nextInt(); + ArrayList> edges = new ArrayList>(); + for (int i = 0; i <= n; i++) { + edges.add(new ArrayList()); + } + for (int from = 1; from <= n; from++) { + int to = 0; + while ((to = sc.nextInt()) != 0) { + edges.get(from).add(to); + } + } + StronglyConnectedComponents scc = new StronglyConnectedComponents(edges); + int sccn = scc.getSccn(); + int[] in = new int[sccn + 1]; + int[] out = new int[sccn + 1]; + ArrayList> dag = scc.getShortGraph(); + for (int i = 1; i <= sccn; i++) { + for (int j : dag.get(i)) { + out[i]++; + in[j]++; + } + } + int zeroIn = 0; + int zeroOut = 0; + for (int i = 1; i <= sccn; i++) { + if (in[i] == 0) { + zeroIn++; + } + if (out[i] == 0) { + zeroOut++; + } + } + System.out.println(zeroIn); + System.out.println(sccn == 1 ? 0 : Math.max(zeroIn, zeroOut)); + } + sc.close(); + } + + public static class StronglyConnectedComponents { + public ArrayList> nexts; + public int n; + public int[] stack; + public int stackSize; + public int[] dfn; + public int[] low; + public int cnt; + public int[] scc; + public int sccn; + + // 请保证点的编号从1开始,不从0开始 + // 注意: + // 如果edges里有0、1、2...n这些点,那么容器edges的大小为n+1 + // 但是0点是弃而不用的,所以1..n才是有效的点,所以有效大小是n + public StronglyConnectedComponents(ArrayList> edges) { + nexts = edges; + init(); + scc(); + } + + private void init() { + n = nexts.size(); + stack = new int[n]; + stackSize = 0; + dfn = new int[n]; + low = new int[n]; + cnt = 0; + scc = new int[n]; + sccn = 0; + n--; + } + + private void scc() { + for (int i = 1; i <= n; i++) { + if (dfn[i] == 0) { + tarjan(i); + } + } + } + + private void tarjan(int p) { + low[p] = dfn[p] = ++cnt; + stack[stackSize++] = p; + for (int q : nexts.get(p)) { + if (dfn[q] == 0) { + tarjan(q); + } + if (scc[q] == 0) { + low[p] = Math.min(low[p], low[q]); + } + } + if (low[p] == dfn[p]) { + sccn++; + int top = 0; + do { + top = stack[--stackSize]; + scc[top] = sccn; + } while (top != p); + } + } + + public int[] getScc() { + return scc; + } + + public int getSccn() { + return sccn; + } + + public ArrayList> getShortGraph() { + ArrayList> shortGraph = new ArrayList>(); + for (int i = 0; i <= sccn; i++) { + shortGraph.add(new ArrayList()); + } + for (int u = 1; u <= n; u++) { + for (int v : nexts.get(u)) { + if (scc[u] != scc[v]) { + shortGraph.get(scc[u]).add(scc[v]); + } + } + } + return shortGraph; + } + + } + +} diff --git a/算法周更班/class_2022_03_1_week/Code03_PopularCows.java b/算法周更班/class_2022_03_1_week/Code03_PopularCows.java new file mode 100644 index 0000000..1851176 --- /dev/null +++ b/算法周更班/class_2022_03_1_week/Code03_PopularCows.java @@ -0,0 +1,150 @@ +package class_2022_03_1_week; + +// 强连通分量练习题目 +// A -> B,表示A认为B是红人 +// A -> B -> C,表示A认为B是红人,B认为C是红人,规定“认为”关系有传递性,所以A也认为C是红人 +// 给定一张有向图,方式是给定M个有序对(A, B) +// (A, B)表示A认为B是红人,该关系具有传递性 +// 给定的有序对中可能包含(A, B)和(B, C),但不包含(A,C) +// 求被其他所有人认为是红人的总数。 +// 测试链接 : http://poj.org/problem?id=2186 +// 注册一下 -> 页面上点击"submit" -> 语言选择java +// 然后把如下代码粘贴进去, 把主类名改成"Main", 可以直接通过 +import java.util.ArrayList; +import java.util.Scanner; + +public class Code03_PopularCows { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + int n = sc.nextInt(); + int m = sc.nextInt(); + ArrayList> edges = new ArrayList>(); + for (int i = 0; i <= n; i++) { + edges.add(new ArrayList()); + } + for (int i = 0; i < m; i++) { + int from = sc.nextInt(); + int to = sc.nextInt(); + edges.get(from).add(to); + } + StronglyConnectedComponents connectedComponents = new StronglyConnectedComponents(edges); + int sccn = connectedComponents.getSccn(); + if (sccn == 1) { + System.out.println(n); + } else { + ArrayList> dag = connectedComponents.getShortGraph(); + int zeroOut = 0; + int outScc = 0; + for (int i = 1; i <= sccn; i++) { + if (dag.get(i).size() == 0) { + zeroOut++; + outScc = i; + } + } + if (zeroOut > 1) { + System.out.println(0); + } else { + int[] scc = connectedComponents.getScc(); + int ans = 0; + for (int i = 1; i <= n; i++) { + if (scc[i] == outScc) { + ans++; + } + } + System.out.println(ans); + } + } + } + sc.close(); + } + + public static class StronglyConnectedComponents { + public ArrayList> nexts; + public int n; + public int[] stack; + public int stackSize; + public int[] dfn; + public int[] low; + public int cnt; + public int[] scc; + public int sccn; + + // 请保证点的编号从1开始,不从0开始 + // 注意: + // 如果edges里有0、1、2...n这些点,那么容器edges的大小为n+1 + // 但是0点是弃而不用的,所以1..n才是有效的点,所以有效大小是n + public StronglyConnectedComponents(ArrayList> edges) { + nexts = edges; + init(); + scc(); + } + + private void init() { + n = nexts.size(); + stack = new int[n]; + stackSize = 0; + dfn = new int[n]; + low = new int[n]; + cnt = 0; + scc = new int[n]; + sccn = 0; + n--; + } + + private void scc() { + for (int i = 1; i <= n; i++) { + if (dfn[i] == 0) { + tarjan(i); + } + } + } + + private void tarjan(int p) { + low[p] = dfn[p] = ++cnt; + stack[stackSize++] = p; + for (int q : nexts.get(p)) { + if (dfn[q] == 0) { + tarjan(q); + } + if (scc[q] == 0) { + low[p] = Math.min(low[p], low[q]); + } + } + if (low[p] == dfn[p]) { + sccn++; + int top = 0; + do { + top = stack[--stackSize]; + scc[top] = sccn; + } while (top != p); + } + } + + public int[] getScc() { + return scc; + } + + public int getSccn() { + return sccn; + } + + public ArrayList> getShortGraph() { + ArrayList> shortGraph = new ArrayList>(); + for (int i = 0; i <= sccn; i++) { + shortGraph.add(new ArrayList()); + } + for (int u = 1; u <= n; u++) { + for (int v : nexts.get(u)) { + if (scc[u] != scc[v]) { + shortGraph.get(scc[u]).add(scc[v]); + } + } + } + return shortGraph; + } + + } + +} diff --git a/算法周更班/class_2022_03_1_week/Code04_IgniteMinBombs.java b/算法周更班/class_2022_03_1_week/Code04_IgniteMinBombs.java new file mode 100644 index 0000000..fab1a24 --- /dev/null +++ b/算法周更班/class_2022_03_1_week/Code04_IgniteMinBombs.java @@ -0,0 +1,328 @@ +package class_2022_03_1_week; + +import java.util.ArrayList; +import java.util.TreeMap; + +// 来自亚马逊 +// 帖子链接 : https://www.nowcoder.com/discuss/826182 +// 这道题是帖子里第2题 +// 在一个地图上有若干个炸弹,每个炸弹会呈现十字型引爆 +// 每个炸弹都有其当量值,这个值决定了这个炸弹的爆炸半径 +// 如果一个炸弹被引爆时,有其它炸弹在其爆炸半径内,那么其它炸弹也会爆炸 +// 请问使地图上所有炸弹爆炸所需的最少人为引爆次数。 +// 例如: +// 0,0,0,0,0 +// 0,0,0,1,0 +// 0,0,0,0,0 +// 上图中val为1的单元是一个炸弹,人为引爆后地图变成下面的样子: +// 0, 0, 0,-1, 0 +// 0, 0,-1,-1,-1 +// 0, 0, 0,-1, 0 +// 题目并没有给数据量,面经题目的通病 +public class Code04_IgniteMinBombs { + + // 暴力方法 + // 为了测试 + public static int minBombs1(int[][] map) { + int n = map.length; + int m = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + m += map[i][j] == 0 ? 0 : 1; + } + } + int[][] bombs = new int[m][2]; + m = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (map[i][j] != 0) { + bombs[m][0] = i; + bombs[m++][1] = j; + } + } + } + int[] arr = new int[m]; + for (int i = 0; i < m; i++) { + arr[i] = i; + } + return process1(arr, 0, bombs, map); + } + + public static int process1(int[] arr, int index, int[][] bombs, int[][] map) { + int ans = Integer.MAX_VALUE; + if (index == arr.length) { + ans = orderIgnite(arr, bombs, map); + } else { + for (int i = index; i < arr.length; i++) { + swap(arr, index, i); + ans = Math.min(ans, process1(arr, index + 1, bombs, map)); + swap(arr, index, i); + } + } + return ans; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + public static int orderIgnite(int[] arr, int[][] bombs, int[][] map) { + int[][] copy = copyMap(map); + int ans = 0; + for (int i : arr) { + int row = bombs[i][0]; + int col = bombs[i][1]; + if (copy[row][col] != -1) { + ans++; + burn(copy, row, col, copy[row][col]); + } + } + return ans; + } + + public static int[][] copyMap(int[][] map) { + int n = map.length; + int[][] ans = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + ans[i][j] = map[i][j]; + } + } + return ans; + } + + public static void burn(int[][] map, int i, int j, int v) { + map[i][j] = -1; + ArrayList queue = new ArrayList<>(); + for (int row = i - 1, cnt = 1; row >= 0 && cnt <= v; row--, cnt++) { + if (map[row][j] > 0) { + queue.add(new int[] { row, j, map[row][j] }); + } + map[row][j] = -1; + } + for (int row = i + 1, cnt = 1; row < map.length && cnt <= v; row++, cnt++) { + if (map[row][j] > 0) { + queue.add(new int[] { row, j, map[row][j] }); + } + map[row][j] = -1; + } + for (int col = j - 1, cnt = 1; col >= 0 && cnt <= v; col--, cnt++) { + if (map[i][col] > 0) { + queue.add(new int[] { i, col, map[i][col] }); + } + map[i][col] = -1; + } + for (int col = j + 1, cnt = 1; col < map.length && cnt <= v; col++, cnt++) { + if (map[i][col] > 0) { + queue.add(new int[] { i, col, map[i][col] }); + } + map[i][col] = -1; + } + for (int[] next : queue) { + burn(map, next[0], next[1], next[2]); + } + } + + // 正式方法 + // 用到有序表 + 强连通分量 + public static int minBombs2(int[][] map) { + int n = map.length; + ArrayList> rowTreeMaps = new ArrayList<>(); + ArrayList> colTreeMaps = new ArrayList<>(); + for (int i = 0; i < n; i++) { + rowTreeMaps.add(new TreeMap<>()); + colTreeMaps.add(new TreeMap<>()); + } + int m = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (map[i][j] != 0) { + m++; + rowTreeMaps.get(i).put(j, m); + colTreeMaps.get(j).put(i, m); + } + } + } + ArrayList> edges = new ArrayList<>(); + for (int i = 0; i <= m; i++) { + edges.add(new ArrayList<>()); + } + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (map[i][j] != 0) { + TreeMap rowTreeMap = rowTreeMaps.get(i); + TreeMap colTreeMap = colTreeMaps.get(j); + int from = rowTreeMap.get(j); + int col = j - 1; + while (rowTreeMap.floorKey(col) != null && j - rowTreeMap.floorKey(col) <= map[i][j]) { + col = rowTreeMap.floorKey(col); + edges.get(from).add(rowTreeMap.get(col)); + col--; + } + col = j + 1; + while (rowTreeMap.ceilingKey(col) != null && rowTreeMap.ceilingKey(col) - j <= map[i][j]) { + col = rowTreeMap.ceilingKey(col); + edges.get(from).add(rowTreeMap.get(col)); + col++; + } + int row = i - 1; + while (colTreeMap.floorKey(row) != null && i - colTreeMap.floorKey(row) <= map[i][j]) { + row = colTreeMap.floorKey(row); + edges.get(from).add(colTreeMap.get(row)); + row--; + } + row = i + 1; + while (colTreeMap.ceilingKey(row) != null && colTreeMap.ceilingKey(row) - i <= map[i][j]) { + row = colTreeMap.ceilingKey(row); + edges.get(from).add(colTreeMap.get(row)); + row++; + } + } + } + } + StronglyConnectedComponents scc = new StronglyConnectedComponents(edges); + int sccn = scc.getSccn(); + int[] in = new int[sccn + 1]; + int[] out = new int[sccn + 1]; + ArrayList> dag = scc.getShortGraph(); + for (int i = 1; i <= sccn; i++) { + for (int j : dag.get(i)) { + out[i]++; + in[j]++; + } + } + int zeroIn = 0; + for (int i = 1; i <= sccn; i++) { + if (in[i] == 0) { + zeroIn++; + } + } + return zeroIn; + } + + public static class StronglyConnectedComponents { + public ArrayList> nexts; + public int n; + public int[] stack; + public int stackSize; + public int[] dfn; + public int[] low; + public int cnt; + public int[] scc; + public int sccn; + + // 请保证点的编号从1开始,不从0开始 + // 注意: + // 如果edges里有0、1、2...n这些点,那么容器edges的大小为n+1 + // 但是0点是弃而不用的,所以1..n才是有效的点,所以有效大小是n + public StronglyConnectedComponents(ArrayList> edges) { + nexts = edges; + init(); + scc(); + } + + private void init() { + n = nexts.size(); + stack = new int[n]; + stackSize = 0; + dfn = new int[n]; + low = new int[n]; + cnt = 0; + scc = new int[n]; + sccn = 0; + n--; + } + + private void scc() { + for (int i = 1; i <= n; i++) { + if (dfn[i] == 0) { + tarjan(i); + } + } + } + + private void tarjan(int p) { + low[p] = dfn[p] = ++cnt; + stack[stackSize++] = p; + for (int q : nexts.get(p)) { + if (dfn[q] == 0) { + tarjan(q); + } + if (scc[q] == 0) { + low[p] = Math.min(low[p], low[q]); + } + } + if (low[p] == dfn[p]) { + sccn++; + int top = 0; + do { + top = stack[--stackSize]; + scc[top] = sccn; + } while (top != p); + } + } + + public int[] getScc() { + return scc; + } + + public int getSccn() { + return sccn; + } + + public ArrayList> getShortGraph() { + ArrayList> shortGraph = new ArrayList>(); + for (int i = 0; i <= sccn; i++) { + shortGraph.add(new ArrayList()); + } + for (int u = 1; u <= n; u++) { + for (int v : nexts.get(u)) { + if (scc[u] != scc[v]) { + shortGraph.get(scc[u]).add(scc[v]); + } + } + } + return shortGraph; + } + + } + + // 为了测试 + public static int[][] randomMatrix(int n, int m, int v) { + int[][] map = new int[n][n]; + for (int i = 0; i < m; i++) { + map[(int) (Math.random() * n)][(int) (Math.random() * n)] = (int) (Math.random() * v) + 1; + } + return map; + } + + // 为了测试 + public static void main(String[] args) { + int n = 8; + int m = 8; + int v = 5; + int testTime = 1000; + System.out.println("测试开始"); + for (int k = 0; k < testTime; k++) { + int[][] map = randomMatrix(n, m, v); + int ans1 = minBombs1(map); + int ans2 = minBombs2(map); + if (ans1 != ans2) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(map[i][j] + " "); + } + System.out.println(); + } + System.out.println(ans1); + System.out.println(ans2); + System.out.println("出错了!"); + } + + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code01_MeetingCheck.java b/算法周更班/class_2022_03_2_week/Code01_MeetingCheck.java new file mode 100644 index 0000000..0757332 --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code01_MeetingCheck.java @@ -0,0 +1,202 @@ +package class_2022_03_2_week; + +import java.util.Arrays; + +// 来自字节飞书团队 +// 在字节跳动,大家都使用飞书的日历功能进行会议室的预订,遇到会议高峰时期, +// 会议室就可能不够用,现在请你实现一个算法,判断预订会议时是否有空的会议室可用。 +// 为简化问题,这里忽略会议室的大小,认为所有的会议室都是等价的, +// 只要空闲就可以容纳任意的会议,并且: +// 1. 所有的会议预订都是当日预订当日的时段 +// 2. 会议时段是一个左闭右开的时间区间,精确到分钟 +// 3. 每个会议室刚开始都是空闲状态,同一时间一个会议室只能进行一场会议 +// 4. 会议一旦预订成功就会按时进行 +// 比如上午11点到中午12点的会议即[660, 720) +// 给定一个会议室总数m +// 一个预定事件由[a,b,c]代表 : +// a代表预定动作的发生时间,早来早得; b代表会议的召开时间; c代表会议的结束时间 +// 给定一个n*3的二维数组,即可表示所有预定事件 +// 返回一个长度为n的boolean类型的数组,表示每一个预定时间是否成功 +public class Code01_MeetingCheck { + + public static boolean[] reserveMeetings(int m, int[][] meetings) { + // 会议的总场次 + int n = meetings.length; + // 开头时间,结尾时间 + int[] ranks = new int[n << 1]; + for (int i = 0; i < n; i++) { + ranks[i] = meetings[i][1]; + ranks[i + n] = meetings[i][2] - 1; + } + Arrays.sort(ranks); + // 0 : [6, 100, 200] + // 1 : [4, 30, 300] + // 30,1 100,2 200,3 300,4 + // [0,6,2,3] + // [1,4,1,4] + // + // 0 T/F , 1, T/ 2, + + // [1,4,1,4] [0,6,2,3] .... + int[][] reMeetings = new int[n][4]; + int max = 0; + for (int i = 0; i < n; i++) { + reMeetings[i][0] = i; + reMeetings[i][1] = meetings[i][0]; + reMeetings[i][2] = rank(ranks, meetings[i][1]); + reMeetings[i][3] = rank(ranks, meetings[i][2] - 1); + max = Math.max(max, reMeetings[i][3]); + } + SegmentTree st = new SegmentTree(max); + Arrays.sort(reMeetings, (a, b) -> a[1] - b[1]); + boolean[] ans = new boolean[n]; + for (int[] meeting : reMeetings) { + if (st.queryMax(meeting[2], meeting[3]) < m) { + ans[meeting[0]] = true; + st.add(meeting[2], meeting[3], 1); + } + } + return ans; + } + + // 返回>=num, 最左位置 + public static int rank(int[] sorted, int num) { + int l = 0; + int r = sorted.length - 1; + int m = 0; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (sorted[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans + 1; + } + + public static class SegmentTree { + private int n; + private int[] max; + private int[] lazy; + + public SegmentTree(int maxSize) { + n = maxSize; + max = new int[n << 2]; + lazy = new int[n << 2]; + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + private void pushDown(int rt, int ln, int rn) { + if (lazy[rt] != 0) { + lazy[rt << 1] += lazy[rt]; + max[rt << 1] += lazy[rt]; + lazy[rt << 1 | 1] += lazy[rt]; + max[rt << 1 | 1] += lazy[rt]; + lazy[rt] = 0; + } + } + + public void add(int L, int R, int C) { + add(L, R, C, 1, n, 1); + } + + private void add(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + max[rt] += C; + lazy[rt] += C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + add(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + add(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int queryMax(int L, int R) { + return queryMax(L, R, 1, n, 1); + } + + private int queryMax(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return max[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans = Math.max(ans, queryMax(L, R, l, mid, rt << 1)); + } + if (R > mid) { + ans = Math.max(ans, queryMax(L, R, mid + 1, r, rt << 1 | 1)); + } + return ans; + } + + } + + // 为了测试线段树 + public static class Right { + public int[] arr; + + public Right(int maxSize) { + arr = new int[maxSize + 1]; + } + + public void add(int L, int R, int C) { + for (int i = L; i <= R; i++) { + arr[i] += C; + } + } + + public int queryMax(int L, int R) { + int ans = 0; + for (int i = L; i <= R; i++) { + ans = Math.max(ans, arr[i]); + } + return ans; + } + + } + + // 测试线段树的对数器 + public static void main(String[] args) { + int N = 50; + int V = 10; + int testTimes1 = 1000; + int testTimes2 = 1000; + System.out.println("测试线段树开始"); + for (int i = 0; i < testTimes1; i++) { + int n = (int) (Math.random() * N) + 1; + SegmentTree st = new SegmentTree(n); + Right right = new Right(n); + for (int j = 0; j < testTimes2; j++) { + int a = (int) (Math.random() * n) + 1; + int b = (int) (Math.random() * n) + 1; + int L = Math.min(a, b); + int R = Math.max(a, b); + if (Math.random() < 0.5) { + int C = (int) (Math.random() * V); + st.add(L, R, C); + right.add(L, R, C); + } else { + if (st.queryMax(L, R) != right.queryMax(L, R)) { + System.out.println("出错了!"); + } + } + } + } + System.out.println("测试线段树结束"); + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code02_StringCheck.java b/算法周更班/class_2022_03_2_week/Code02_StringCheck.java new file mode 100644 index 0000000..5524a47 --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code02_StringCheck.java @@ -0,0 +1,65 @@ +package class_2022_03_2_week; + +import java.util.Arrays; + +// 来自字节飞书团队 +// 小歪每次会给你两个字符串: +// 笔记s1和关键词s2,请你写一个函数 +// 判断s2的排列之一是否是s1的子串 +// 如果是,返回true +// 否则,返回false +public class Code02_StringCheck { + + public static boolean check1(String s1, String s2) { + if (s1.length() < s2.length()) { + return false; + } + char[] str2 = s2.toCharArray(); + Arrays.sort(str2); + s2 = String.valueOf(str2); + for (int L = 0; L < s1.length(); L++) { + for (int R = L; R < s1.length(); R++) { + char[] cur = s1.substring(L, R + 1).toCharArray(); + Arrays.sort(cur); + String curSort = String.valueOf(cur); + if (curSort.equals(s2)) { + return true; + } + } + } + return false; + } + + public static boolean check2(String s1, String s2) { + if (s1.length() < s2.length()) { + return false; + } + char[] str2 = s2.toCharArray(); + int[] count = new int[256]; + for (int i = 0; i < str2.length; i++) { + count[str2[i]]++; + } + int M = str2.length; + char[] st1 = s1.toCharArray(); + int inValidTimes = 0; + int R = 0; + for (; R < M; R++) { + if (count[st1[R]]-- <= 0) { + inValidTimes++; + } + } + for (; R < st1.length; R++) { + if (inValidTimes == 0) { + return true; + } + if (count[st1[R]]-- <= 0) { + inValidTimes++; + } + if (count[st1[R - M]]++ < 0) { + inValidTimes--; + } + } + return inValidTimes == 0; + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code03_AiFill.java b/算法周更班/class_2022_03_2_week/Code03_AiFill.java new file mode 100644 index 0000000..a71cb85 --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code03_AiFill.java @@ -0,0 +1,112 @@ +package class_2022_03_2_week; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.TreeSet; + +// 来自字节飞书团队 +// 语法补全功能,比如"as soon as possible" +// 当我们识别到"as soon as"时, 基本即可判定用户需要键入"possible" +// 设计一个统计词频的模型,用于这个功能 +// 类似(prefix, next word)这样的二元组 +// 比如一个上面的句子"as soon as possible" +// 有产生如下的二元组(as, soon, 1)、(as soon, as, 1)、(as soon as, possible, 1) +// 意思是这一个句子产生了如下的统计: +// 当前缀为"as",接下来的单词是"soon",有了1个期望点 +// 当前缀为"as soon",接下来的单词是"as",有了1个期望点 +// 当前缀为"as soon as",接下来的单词是"possible",有了1个期望点 +// 那么如果给你很多的句子,当然就可以产生很多的期望点,同一个前缀下,同一个next word的期望点可以累加 +// 现在给你n个句子,让你来建立统计 +// 然后给你m个句子,作为查询 +// 最后给你k,表示每个句子作为前缀的情况下,词频排在前k名的联想 +// 返回m个结果,每个结果最多k个单词 +public class Code03_AiFill { + + public static class TrieNode { + public String word; + public int times; + public HashMap nextNodes; + public TreeSet nextRanks; + + public TrieNode(String w) { + word = w; + times = 1; + nextNodes = new HashMap<>(); + nextRanks = new TreeSet<>((a, b) -> a.times != b.times ? (b.times - a.times) : a.word.compareTo(b.word)); + } + + } + + public static class AI { + public TrieNode root; + public int topk; + + public AI(List sentences, int k) { + root = new TrieNode(""); + topk = k; + for (String sentence : sentences) { + fill(sentence); + } + } + + public void fill(String sentence) { + TrieNode cur = root; + TrieNode next = null; + for (String word : sentence.split(" ")) { + if (!cur.nextNodes.containsKey(word)) { + next = new TrieNode(word); + cur.nextNodes.put(word, next); + cur.nextRanks.add(next); + } else { + next = cur.nextNodes.get(word); + cur.nextRanks.remove(next); + next.times++; + cur.nextRanks.add(next); + } + cur = next; + } + } + + public List suggest(String sentence) { + List ans = new ArrayList<>(); + TrieNode cur = root; + for (String word : sentence.split(" ")) { + if (!cur.nextNodes.containsKey(word)) { + return ans; + } else { + cur = cur.nextNodes.get(word); + } + } + for (TrieNode n : cur.nextRanks) { + ans.add(n.word); + if (ans.size() == topk) { + break; + } + } + return ans; + } + + } + + public static void main(String[] args) { + ArrayList sentences = new ArrayList<>(); + sentences.add("i think you are good"); + sentences.add("i think you are fine"); + sentences.add("i think you are good man"); + int k = 2; + AI ai = new AI(sentences, k); + for (String ans : ai.suggest("i think you are")) { + System.out.println(ans); + } + System.out.println("====="); + ai.fill("i think you are fucking good"); + ai.fill("i think you are fucking great"); + ai.fill("i think you are fucking genius"); + for (String ans : ai.suggest("i think you are")) { + System.out.println(ans); + } + System.out.println("====="); + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code04_SameTeams.java b/算法周更班/class_2022_03_2_week/Code04_SameTeams.java new file mode 100644 index 0000000..65ec1b0 --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code04_SameTeams.java @@ -0,0 +1,62 @@ +package class_2022_03_2_week; + +import java.util.ArrayList; +import java.util.HashMap; + +// 来自字节飞书团队 +// 假设数组a和数组b为两组信号 +// 1) length(b) <= length(a) +// 2) 对于任意0<=i indices; + public HashMap nexts; + + public TrieNode() { + indices = new ArrayList<>(); + nexts = new HashMap<>(); + } + + } + + public static int[] sameTeamsArray(int[][] bs, int[][] as) { + int m = bs.length; + TrieNode root = new TrieNode(); + TrieNode cur = null; + for (int i = 0; i < m; i++) { + int k = bs[i].length; + cur = root; + for (int j = 1; j < k; j++) { + int diff = bs[i][j] - bs[i][j - 1]; + if (!cur.nexts.containsKey(diff)) { + cur.nexts.put(diff, new TrieNode()); + } + cur = cur.nexts.get(diff); + } + cur.indices.add(i); + } + int[] ans = new int[m]; + int n = as.length; + for (int i = 0; i < n; i++) { + int k = as[i].length; + cur = root; + for (int j = 1; j < k; j++) { + int diff = as[i][j] - as[i][j - 1]; + if (!cur.nexts.containsKey(diff)) { + break; + } + cur = cur.nexts.get(diff); + for (int index : cur.indices) { + ans[index]++; + } + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code05_NumberOfDivisibleByM.java b/算法周更班/class_2022_03_2_week/Code05_NumberOfDivisibleByM.java new file mode 100644 index 0000000..03e500a --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code05_NumberOfDivisibleByM.java @@ -0,0 +1,69 @@ +package class_2022_03_2_week; + +// 来自微软 +// 给定一个数组arr,给定一个正数M +// 如果arr[i] + arr[j]可以被M整除,并且i < j,那么(i,j)叫做一个M整除对 +// 返回arr中M整除对的总数量 +public class Code05_NumberOfDivisibleByM { + + public static int num1(int[] arr, int m) { + int n = arr.length; + int ans = 0; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (Math.abs(arr[i] + arr[j]) % m == 0) { + ans++; + } + } + } + return ans; + } + + public static int num2(int[] arr, int m) { + int n = arr.length; + int[] cnts = new int[m]; + int ans = 0; + for (int i = n - 1; i >= 0; i--) { + int cur = (arr[i] % m + m) % m; + ans += cnts[(m - cur) % m]; + cnts[cur]++; + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) - (int) (Math.random() * v); + } + return arr; + } + + public static void main(String[] args) { + int len = 50; + int value = 50; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int m = (int) (Math.random() * value) + 1; + int ans1 = num1(arr, m); + int ans2 = num2(arr, m); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("m = " + m); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code06_JobMinDays.java b/算法周更班/class_2022_03_2_week/Code06_JobMinDays.java new file mode 100644 index 0000000..fc36ecc --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code06_JobMinDays.java @@ -0,0 +1,100 @@ +package class_2022_03_2_week; + +// 来自微软 +// 给定一个正数数组arr,长度为N,依次代表N个任务的难度,给定一个正数k +// 你只能从0任务开始,依次处理到N-1号任务结束,就是一定要从左往右处理任务 +// 只不过,难度差距绝对值不超过k的任务,可以在一天之内都完成 +// 返回完成所有任务的最少天数 +public class Code06_JobMinDays { + + public static int minDays1(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[n]; + dp[0] = 1; + for (int i = 1; i < n; i++) { + dp[i] = dp[i - 1] + 1; + int min = arr[i]; + int max = arr[i]; + for (int j = i - 1; j >= 0; j--) { + min = Math.min(min, arr[j]); + max = Math.max(max, arr[j]); + if (max - min <= k) { + dp[i] = Math.min(dp[i], 1 + (j - 1 >= 0 ? dp[j - 1] : 0)); + } else { + break; + } + } + } + return dp[n - 1]; + } + + public static int minDays2(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[n]; + int[] windowMax = new int[n]; + int[] windowMin = new int[n]; + int maxL = 0; + int maxR = 0; + int minL = 0; + int minR = 0; + int L = 0; + for (int i = 0; i < n; i++) { + while (maxL < maxR && arr[windowMax[maxR - 1]] <= arr[i]) { + maxR--; + } + windowMax[maxR++] = i; + while (minL < minR && arr[windowMin[minR - 1]] >= arr[i]) { + minR--; + } + windowMin[minR++] = i; + while (arr[windowMax[maxL]] - arr[windowMin[minL]] > k) { + if (windowMax[maxL] == L) { + maxL++; + } + if (windowMin[minL] == L) { + minL++; + } + L++; + } + dp[i] = 1 + (L - 1 >= 0 ? dp[L - 1] : 0); + } + return dp[n - 1]; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 50; + int value = 20; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int k = (int) (Math.random() * value); + int ans1 = minDays1(arr, k); + int ans2 = minDays2(arr, k); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("k = " + k); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code07_MinWaitingTime.java b/算法周更班/class_2022_03_2_week/Code07_MinWaitingTime.java new file mode 100644 index 0000000..90cbeed --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code07_MinWaitingTime.java @@ -0,0 +1,95 @@ +package class_2022_03_2_week; + +import java.util.PriorityQueue; + +// 来自谷歌 +// 给定一个数组arr,长度为n +// 表示n个服务员,每个人服务一个人的时间 +// 给定一个正数m,表示有m个人等位 +// 如果你是刚来的人,请问你需要等多久? +// 假设:m远远大于n,比如n<=1000, m <= 10的9次方,该怎么做? +public class Code07_MinWaitingTime { + + public static int minWaitingTime1(int[] arr, int m) { + if (arr == null || arr.length == 0) { + return -1; + } + PriorityQueue heap = new PriorityQueue<>((a, b) -> (a[0] - b[0])); + int n = arr.length; + for (int i = 0; i < n; i++) { + heap.add(new int[] { 0, arr[i] }); + } + for (int i = 0; i < m; i++) { + int[] cur = heap.poll(); + cur[0] += cur[1]; + heap.add(cur); + } + return heap.peek()[0]; + } + + public static int minWaitingTime2(int[] arr, int m) { + if (arr == null || arr.length == 0) { + return -1; + } + int best = Integer.MAX_VALUE; + for (int num : arr) { + best = Math.min(best, num); + } + int left = 0; + int right = best * m; + int mid = 0; + int near = 0; + while (left <= right) { + mid = (left + right) / 2; + int cover = 0; + for (int num : arr) { + cover += (mid / num) + 1; + } + if (cover >= m + 1) { + near = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + return near; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 50; + int value = 30; + int mMax = 3000; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int m = (int) (Math.random() * mMax); + int ans1 = minWaitingTime1(arr, m); + int ans2 = minWaitingTime2(arr, m); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("m : " + m); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_2_week/Code08_TimeNSpace1LowestCommonAncestor.java b/算法周更班/class_2022_03_2_week/Code08_TimeNSpace1LowestCommonAncestor.java new file mode 100644 index 0000000..f4e91aa --- /dev/null +++ b/算法周更班/class_2022_03_2_week/Code08_TimeNSpace1LowestCommonAncestor.java @@ -0,0 +1,111 @@ +package class_2022_03_2_week; + +// 如何时间复杂度O(N),额外空间复杂度O(1),解决最低公共祖先问题? +// 测试链接 : https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/ +public class Code08_TimeNSpace1LowestCommonAncestor { + + // 这个类不要提交 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + // 提交以下的方法 + // 该方法亮点在于:时间复杂度O(N),额外空间复杂度O(1) + public static TreeNode lowestCommonAncestor(TreeNode head, TreeNode o1, TreeNode o2) { + if (findFirst(o1.left, o1, o2) != null || findFirst(o1.right, o1, o2) != null) { + return o1; + } + if (findFirst(o2.left, o1, o2) != null || findFirst(o2.right, o1, o2) != null) { + return o2; + } + TreeNode leftAim = findFirst(head, o1, o2); + TreeNode cur = head; + TreeNode mostRight = null; + TreeNode ans = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + if (findLeftAim(cur.left, leftAim)) { + if (ans == null && findFirst(leftAim.right, o1, o2) != null) { + ans = leftAim; + } + leftAim = cur; + } + } + } + cur = cur.right; + } + return ans != null ? ans : (findFirst(leftAim.right, o1, o2) != null ? leftAim : head); + } + + public static boolean findLeftAim(TreeNode head, TreeNode leftAim) { + TreeNode tail = reverseEdge(head); + TreeNode cur = tail; + boolean ans = false; + while (cur != null) { + if (cur == leftAim) { + ans = true; + } + cur = cur.right; + } + reverseEdge(tail); + return ans; + } + + public static TreeNode reverseEdge(TreeNode from) { + TreeNode pre = null; + TreeNode next = null; + while (from != null) { + next = from.right; + from.right = pre; + pre = from; + from = next; + } + return pre; + } + + public static TreeNode findFirst(TreeNode head, TreeNode o1, TreeNode o2) { + if (head == null) { + return null; + } + TreeNode cur = head; + TreeNode mostRight = null; + TreeNode first = null; + while (cur != null) { + mostRight = cur.left; + if (mostRight != null) { + while (mostRight.right != null && mostRight.right != cur) { + mostRight = mostRight.right; + } + if (mostRight.right == null) { + if (first == null && (cur == o1 || cur == o2)) { + first = cur; + } + mostRight.right = cur; + cur = cur.left; + continue; + } else { + mostRight.right = null; + } + } else { + if (first == null && (cur == o1 || cur == o2)) { + first = cur; + } + } + cur = cur.right; + } + return first; + } + +} diff --git a/算法周更班/class_2022_03_3_week/Code01_LongestUncontinuousSet.java b/算法周更班/class_2022_03_3_week/Code01_LongestUncontinuousSet.java new file mode 100644 index 0000000..c32f6e2 --- /dev/null +++ b/算法周更班/class_2022_03_3_week/Code01_LongestUncontinuousSet.java @@ -0,0 +1,100 @@ +package class_2022_03_3_week; + +import java.util.Arrays; + +// 来自美团 +// 给定一个数组arr,你可以随意挑选其中的数字 +// 但是你挑选的数中,任何两个数a和b,必须Math.abs(a - b) > 1 +// 返回你最多能挑选几个数 +public class Code01_LongestUncontinuousSet { + + // 暴力方法 + // 为了验证 + public static int longestUncontinuous1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + Arrays.sort(arr); + return process1(arr, 0, new int[arr.length], 0); + } + + public static int process1(int[] arr, int i, int[] path, int j) { + if (i == arr.length) { + for (int k = 1; k < j; k++) { + if (path[k - 1] + 1 >= path[k]) { + return 0; + } + } + return j; + } else { + int p1 = process1(arr, i + 1, path, j); + path[j] = arr[i]; + int p2 = process1(arr, i + 1, path, j + 1); + return Math.max(p1, p2); + } + } + + // 最优解 + public static int longestUncontinuous2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + Arrays.sort(arr); + int n = arr.length; + int size = 1; + for (int i = 1; i < n; i++) { + if (arr[i] != arr[size - 1]) { + arr[size++] = arr[i]; + } + } + int[] dp = new int[size]; + dp[0] = 1; + int ans = 1; + for (int i = 1; i < size; i++) { + dp[i] = 1; + if (arr[i] - arr[i - 1] > 1) { + dp[i] = 1 + dp[i - 1]; + } + if (i - 2 >= 0 && arr[i] - arr[i - 2] > 1) { + dp[i] = Math.max(dp[i], 1 + dp[i - 2]); + } + ans = Math.max(ans, dp[i]); + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) - (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 10; + int value = 20; + int testTime = 2000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int ans1 = longestUncontinuous1(arr); + int ans2 = longestUncontinuous2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_3_week/Code02_CutDouFu.java b/算法周更班/class_2022_03_3_week/Code02_CutDouFu.java new file mode 100644 index 0000000..d0a695b --- /dev/null +++ b/算法周更班/class_2022_03_3_week/Code02_CutDouFu.java @@ -0,0 +1,54 @@ +package class_2022_03_3_week; + +import java.util.Arrays; + +// 来自美团 +// 有一块10000 * 10000 * 10000的立方体豆腐 +// 豆腐的前左下角放在(0,0,0)点,豆腐的后右上角放在(10000,10000,10000)点 +// 下面给出切法的数据结构 +// [a,b] +// a = 1,表示x = b处,一把无穷大的刀平行于yz面贯穿豆腐切过去 +// a = 2,表示y = b处,一把无穷大的刀平行于xz面贯穿豆腐切过去 +// a = 3,表示z = b处,一把无穷大的刀平行于xy面贯穿豆腐切过去 +// a = 1 or 2 or 3,0<=b<=10000 +// 给定一个n*2的二维数组,表示切了n刀 +// 返回豆腐中最大的一块体积是多少 +public class Code02_CutDouFu { + + public static long maxCut(int[][] cuts) { + if (cuts == null || cuts.length == 0) { + return 10000L * 10000L * 10000L; + } + // 0 类型 + // 1 切哪了 + Arrays.sort(cuts, (a, b) -> a[0] != b[0] ? (a[0] - b[0]) : (a[1] - b[1])); + int n = cuts.length; + int i = 0; + int xMaxDiff = 0; + int pre = 0; + while (i < n && cuts[i][0] == 1) { + xMaxDiff = Math.max(xMaxDiff, cuts[i][1] - pre); + pre = cuts[i][1]; + i++; + } + xMaxDiff = Math.max(xMaxDiff, 10000 - pre); + int yMaxDiff = 0; + pre = 0; + while (i < n && cuts[i][0] == 2) { + yMaxDiff = Math.max(yMaxDiff, cuts[i][1] - pre); + pre = cuts[i][1]; + i++; + } + yMaxDiff = Math.max(yMaxDiff, 10000 - pre); + int zMaxDiff = 0; + pre = 0; + while (i < n && cuts[i][0] == 3) { + zMaxDiff = Math.max(zMaxDiff, cuts[i][1] - pre); + pre = cuts[i][1]; + i++; + } + zMaxDiff = Math.max(zMaxDiff, 10000 - pre); + return (long) xMaxDiff * (long) yMaxDiff * (long) zMaxDiff; + } + +} diff --git a/算法周更班/class_2022_03_3_week/Code03_MaxSumOnReverseArray.java b/算法周更班/class_2022_03_3_week/Code03_MaxSumOnReverseArray.java new file mode 100644 index 0000000..4bb6815 --- /dev/null +++ b/算法周更班/class_2022_03_3_week/Code03_MaxSumOnReverseArray.java @@ -0,0 +1,105 @@ +package class_2022_03_3_week; + +// 来自美团 +// 最大子段和是 +// 一个经典问题,即对于一个数组找出其和最大的子数组。 +// 现在允许你在求解该问题之前翻转这个数組的连续一段 +// 如翻转(1,2,3,4,5,6)的第三个到第五个元素組成的子数组得到的是(1,2,5,4,3,6), +// 则翻转后该数组的最大子段和最大能达到多少? +// 来自字节 +// 几乎一样的题,来自字节笔试第4题 +// 给定两个数組values和numbers, +// values[i]表示i号宝石的单品价值 +// numbers[i]表示i号宝石的数量 +// i号宝石的总价值 = values[i] * numbers[i] +// 如果有一种魔法,可以翻转任何区间L...R的宝石,也就是改变L..R的宝石排列,变成逆序的 +// 求在允许用一次魔法的情况下,任取一段连续区间,能达到的最大价值 +// 这两个问法解法都几乎一样,区别无非是: +// 美团的: 可进行一次翻转情况下,子数组最大累加和 +// 字节的: 可进行一次翻转情况下,子数组最大价值和 +public class Code03_MaxSumOnReverseArray { + + public static int maxSumReverse1(int[] arr) { + int ans = Integer.MIN_VALUE; + for (int L = 0; L < arr.length; L++) { + for (int R = L; R < arr.length; R++) { + reverse(arr, L, R); + ans = Math.max(ans, maxSum(arr)); + reverse(arr, L, R); + } + } + return ans; + } + + public static void reverse(int[] arr, int L, int R) { + while (L < R) { + int tmp = arr[L]; + arr[L++] = arr[R]; + arr[R--] = tmp; + } + } + + public static int maxSum(int[] arr) { + int pre = arr[0]; + int max = arr[0]; + for (int i = 1; i < arr.length; i++) { + pre = Math.max(arr[i], arr[i] + pre); + max = Math.max(max, pre); + } + return max; + } + + public static int maxSumReverse2(int[] arr) { + int n = arr.length; + int[] prefix = new int[n]; + prefix[n - 1] = arr[n - 1]; + for (int i = n - 2; i >= 0; i--) { + prefix[i] = arr[i] + Math.max(0, prefix[i + 1]); + } + int ans = prefix[0]; + int suffix = arr[0]; + int maxSuffix = suffix; + for (int i = 1; i < n; i++) { + ans = Math.max(ans, maxSuffix + prefix[i]); + suffix = arr[i] + Math.max(0, suffix); + maxSuffix = Math.max(maxSuffix, suffix); + } + ans = Math.max(ans, maxSuffix); + return ans; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) - (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 50; + int value = 20; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int ans1 = maxSumReverse1(arr); + int ans2 = maxSumReverse2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_3_week/Code04_ArrangeAddGetMax.java b/算法周更班/class_2022_03_3_week/Code04_ArrangeAddGetMax.java new file mode 100644 index 0000000..f7b0de6 --- /dev/null +++ b/算法周更班/class_2022_03_3_week/Code04_ArrangeAddGetMax.java @@ -0,0 +1,150 @@ +package class_2022_03_3_week; + +import java.util.Arrays; + +// 来自美团 +// void add(int L, int R, int C)代表在arr[L...R]上每个数加C +// int get(int L, int R)代表查询arr[L...R]上的累加和 +// 假设你可以在所有操作开始之前,重新排列arr +// 请返回每一次get查询的结果都加在一起最大能是多少 +// 输入参数: +// int[] arr : 原始数组 +// int[][] ops,二维数组每一行解释如下: +// [a,b,c],如果数组有3个数,表示调用add(a,b,c) +// [a,b],如果数组有2个数,表示调用get(a,b) +// a和b表示arr范围,范围假设从1开始,不从0开始 +// 输出: +// 假设你可以在开始时重新排列arr,返回所有get操作返回值累计和最大是多少? +public class Code04_ArrangeAddGetMax { + + public static int maxGets(int[] arr, int[][] ops) { + int n = arr.length; + SegmentTree getTree = new SegmentTree(n); + for (int[] op : ops) { + if (op.length == 2) { + getTree.add(op[0], op[1], 1); + } + } + int[][] getCnts = new int[n][2]; + for (int i = 1, j = 0; i <= n; i++, j++) { + getCnts[j][0] = j; + getCnts[j][1] = getTree.get(i, i); + } + Arrays.sort(getCnts, (a, b) -> a[1] - b[1]); + Arrays.sort(arr); + int[] reArrange = new int[n]; + for (int i = 0; i < n; i++) { + reArrange[getCnts[i][0]] = arr[i]; + } + SegmentTree st = new SegmentTree(reArrange); + int ans = 0; + for (int[] op : ops) { + if (op.length == 3) { + st.add(op[0], op[1], op[2]); + } else { + ans += st.get(op[0], op[1]); + } + } + return ans; + } + + public static class SegmentTree { + private int n; + private int[] arr; + private int[] sum; + private int[] lazy; + + public SegmentTree(int size) { + n = size + 1; + sum = new int[n << 2]; + lazy = new int[n << 2]; + n--; + } + + public SegmentTree(int[] origin) { + n = origin.length + 1; + arr = new int[n]; + for (int i = 1; i < n; i++) { + arr[i] = origin[i - 1]; + } + sum = new int[n << 2]; + lazy = new int[n << 2]; + build(1, --n, 1); + } + + private void build(int l, int r, int rt) { + if (l == r) { + sum[rt] = arr[l]; + return; + } + int mid = (l + r) >> 1; + build(l, mid, rt << 1); + build(mid + 1, r, rt << 1 | 1); + pushUp(rt); + } + + private void pushUp(int rt) { + sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; + } + + private void pushDown(int rt, int ln, int rn) { + if (lazy[rt] != 0) { + lazy[rt << 1] += lazy[rt]; + sum[rt << 1] += lazy[rt] * ln; + lazy[rt << 1 | 1] += lazy[rt]; + sum[rt << 1 | 1] += lazy[rt] * rn; + lazy[rt] = 0; + } + } + + public void add(int L, int R, int C) { + add(L, R, C, 1, n, 1); + } + + private void add(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + sum[rt] += C * (r - l + 1); + lazy[rt] += C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + add(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + add(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int get(int L, int R) { + return query(L, R, 1, n, 1); + } + + private int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return sum[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans += query(L, R, l, mid, rt << 1); + } + if (R > mid) { + ans += query(L, R, mid + 1, r, rt << 1 | 1); + } + return ans; + } + + } + + public static void main(String[] args) { + int[] arr = { 1, 2, 3, 4, 5 }; + int[][] ops = { { 1, 3 }, { 2, 4 }, { 1, 5 } }; + System.out.println(maxGets(arr, ops)); + + } + +} diff --git a/算法周更班/class_2022_03_3_week/Code05_EatFish.java b/算法周更班/class_2022_03_3_week/Code05_EatFish.java new file mode 100644 index 0000000..7ab2b89 --- /dev/null +++ b/算法周更班/class_2022_03_3_week/Code05_EatFish.java @@ -0,0 +1,106 @@ +package class_2022_03_3_week; + +// 来自bilibili +// 现在有N条鱼,每条鱼的体积为Ai,从左到右排列,数组arr给出 +// 每一轮,左边的大鱼一定会吃掉右边比自己小的第一条鱼, +// 并且每条鱼吃比自己小的鱼的事件是同时发生的。 +// 返回多少轮之后,鱼的数量会稳定 +// 注意:6 6 3 3 +// 第一轮过后 : +// 对于两个6来说,右边比自己小的第一条鱼都是第1个3,所以只有这个3被吃掉, +// 数组变成 : 6 6 3(第2个3) +// 第二轮过后 : 6 6 +// 返回2 +public class Code05_EatFish { + + public static int minTurns1(int[] arr) { + int ans = 0; + for (;; ans++) { + int[] rest = eatRest(arr); + if (arr.length == rest.length) { + break; + } + arr = rest; + } + return ans; + } + + public static int[] eatRest(int[] arr) { + if (arr.length == 0) { + return new int[0]; + } + int n = arr.length; + boolean[] delete = new boolean[n]; + int len = n; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (arr[i] > arr[j]) { + if (!delete[j]) { + delete[j] = true; + len--; + } + break; + } + } + } + int[] rest = new int[len]; + for (int i = 0, j = 0; i < n; i++) { + if (!delete[i]) { + rest[j++] = arr[i]; + } + } + return rest; + } + + public static int minTurns2(int[] arr) { + int n = arr.length; + int[][] stack = new int[n][2]; + int stackSize = 0; + int ans = 0; + for (int i = n - 1; i >= 0; i--) { + int curAns = 0; + while (stackSize > 0 && stack[stackSize - 1][0] < arr[i]) { + curAns = Math.max(curAns + 1, stack[--stackSize][1]); + } + stack[stackSize][0] = arr[i]; + stack[stackSize++][1] = curAns; + ans = Math.max(ans, curAns); + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 50; + int value = 20; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int ans1 = minTurns1(arr); + int ans2 = minTurns2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_3_week/Code06_FinancialProduct.java b/算法周更班/class_2022_03_3_week/Code06_FinancialProduct.java new file mode 100644 index 0000000..05a6219 --- /dev/null +++ b/算法周更班/class_2022_03_3_week/Code06_FinancialProduct.java @@ -0,0 +1,51 @@ +package class_2022_03_3_week; + +import java.util.Arrays; + +// 来自银联编程比赛 +// 某公司计划推出一批投资项目。 product[i] = price 表示第 i 个理财项目的投资金额 price 。 +// 客户在按需投资时,需要遵循以下规则: +// 客户在首次对项目 product[i] 投资时,需要投入金额 price +// 对已完成首次投资的项目 product[i] 可继续追加投入, +// 但追加投入的金额需小于上一次对该项目的投入(追加投入为大于 0 的整数) +// 为控制市场稳定,每人交易次数不得大于 limit。(首次投资和追加投入均记作 1 次交易) +// 若对所有理财项目中最多进行 limit 次交易,使得投入金额总和最大,请返回这个最大值的总和。 +// 测试链接 : https://leetcode-cn.com/contest/cnunionpay-2022spring/problems/I4mOGz/ +public class Code06_FinancialProduct { + + public static long mod = 1000000007L; + + public int maxInvestment(int[] arr, int limit) { + Arrays.sort(arr); + int n = arr.length; + long ans = 0; + int r = n - 1; + int l = r; + while (limit > 0 && r != -1) { + while (l >= 0 && arr[l] == arr[r]) { + l--; + } + int big = arr[r]; + int small = l == -1 ? 0 : arr[l]; + int teams = n - l - 1; + int all = (big - small) * teams; + if (limit >= all) { + ans += get(big, small + 1, teams); + ans %= mod; + limit -= all; + } else { + int a = limit / teams; + ans += get(big, big - a + 1, teams) + (long) (big - a) * (long) (limit % teams); + ans %= mod; + limit = 0; + } + r = l; + } + return (int) (ans % mod); + } + + public static long get(long up, long down, long num) { + return num * ((up + down) * (up - down + 1) / 2); + } + +} diff --git a/算法周更班/class_2022_03_3_week/Code07_CoopDevelop.java b/算法周更班/class_2022_03_3_week/Code07_CoopDevelop.java new file mode 100644 index 0000000..d931fa6 --- /dev/null +++ b/算法周更班/class_2022_03_3_week/Code07_CoopDevelop.java @@ -0,0 +1,51 @@ +package class_2022_03_3_week; + +import java.util.HashMap; + +// 来自银联编程比赛 +// 为了不断提高用户使用的体验,开发团队正在对产品进行全方位的开发和优化。 +// 已知开发团队共有若干名成员,skills[i] 表示第 i 名开发人员掌握技能列表。 +// 如果两名成员各自拥有至少一门对方未拥有的技能,则这两名成员可以「合作开发」。 +// 请返回当前有多少对开发成员满足「合作开发」的条件。 +// 由于答案可能很大,请你返回答案对 10^9 + 7 取余的结果。 +// 测试链接 : https://leetcode-cn.com/contest/cnunionpay-2022spring/problems/lCh58I/ +public class Code07_CoopDevelop { + + public static long mod = 1000000007L; + + public static int coopDevelop(int[][] skills) { + int n = skills.length; + // key : 子集 + // value : 个数 + HashMap noFullSetsNums = new HashMap<>(); + for (int[] people : skills) { + fillNoFullMap(people, 0, 0, true, noFullSetsNums); + } + HashMap cntsNums = new HashMap<>(); + long minus = 0L; + for (int[] people : skills) { + long status = 0L; + for (int skill : people) { + status = (status << 10) | skill; + } + minus += noFullSetsNums.getOrDefault(status, 0L); + minus += cntsNums.getOrDefault(status, 0L); + cntsNums.put(status, cntsNums.getOrDefault(status, 0L) + 1); + } + long ans = (long) n * (long) (n - 1) / 2L; + return (int) ((ans - minus) % mod); + } + + public static void fillNoFullMap(int[] people, int i, long status, boolean full, + HashMap noFullSetsNums) { + if (i == people.length) { + if (!full) { + noFullSetsNums.put(status, noFullSetsNums.getOrDefault(status, 0L) + 1); + } + } else { + fillNoFullMap(people, i + 1, status, false, noFullSetsNums); + fillNoFullMap(people, i + 1, (status << 10) | people[i], full, noFullSetsNums); + } + } + +} diff --git a/算法周更班/class_2022_03_4_week/Code01_ArrangeJob.java b/算法周更班/class_2022_03_4_week/Code01_ArrangeJob.java new file mode 100644 index 0000000..8cd7e9b --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code01_ArrangeJob.java @@ -0,0 +1,66 @@ +package class_2022_03_4_week; + +import java.util.Arrays; +import java.util.PriorityQueue; + +// 来自学员的考试 +// 来自华为 +// 给定一个n*2的二维数组,表示有n个任务 +// 一个信息是任务能够开始做的时间,另一个信息是任务的结束期限,后者一定大于前者,且数值上都是正数 +// 你作为单线程的人,不能并行处理任务,但是每个任务都只需要一个单位时间完成 +// 你需要将所有任务的执行时间,位于开始做的时间和最后期限之间 +// 返回你能否做到这一点 +public class Code01_ArrangeJob { + + // 1 开 7 + // 5 闭 end没有用! + public static class TimePoint { + // 时间 + public int time; + public int end; + // add = true time 任务的添加时间 + // add = false time 任务的结束时间 + public boolean add; + + public TimePoint(int t, int e, boolean a) { + time = t; + end = e; + add = a; + } + + } + + public static boolean canDo(int[][] jobs) { + if (jobs == null || jobs.length < 2) { + return true; + } + int n = jobs.length; + TimePoint[] arr = new TimePoint[n << 1]; + for (int i = 0; i < n; i++) { + arr[i] = new TimePoint(jobs[i][0], jobs[i][1], true); + arr[i + n] = new TimePoint(jobs[i][1], jobs[i][1], false); + } + Arrays.sort(arr, (a, b) -> a.time - b.time); + PriorityQueue heap = new PriorityQueue<>(); + // 经过一个一个的时间点,遭遇事件:添加时间、检查时间 + for (int i = 0, lastTime = arr[0].time; i < arr.length; i++) { + if (arr[i].add) { + heap.add(arr[i].end); + } else { // 检查时间 + int curTime = arr[i].time; + for (int j = lastTime; j < curTime; j++) { + if (heap.isEmpty()) { + break; + } + heap.poll(); + } + if (heap.peek() <= curTime) { + return false; + } + lastTime = curTime; + } + } + return true; + } + +} diff --git a/算法周更班/class_2022_03_4_week/Code02_BuyGoodsHaveDiscount.java b/算法周更班/class_2022_03_4_week/Code02_BuyGoodsHaveDiscount.java new file mode 100644 index 0000000..5d768a8 --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code02_BuyGoodsHaveDiscount.java @@ -0,0 +1,89 @@ +package class_2022_03_4_week; + +// 来自字节内部训练营 +// 某公司游戏平台的夏季特惠开始了,你决定入手一些游戏。现在你一共有X元的预算。 +// 该平台上所有的 n 个游戏均有折扣,标号为 i 的游戏的原价a_i元,现价只要b_i元 +// 也就是说该游戏可以优惠 a_i - b_i,并且你购买该游戏能获得快乐值为 w_i +// 由于优惠的存在,你可能做出一些冲动消费导致最终买游戏的总费用超过预算, +// 只要满足 : 获得的总优惠金额不低于超过预算的总金额 +// 那在心理上就不会觉得吃亏。 +// 现在你希望在心理上不觉得吃亏的前提下,获得尽可能多的快乐值。 +// 测试链接 : https://leetcode-cn.com/problems/tJau2o/ +// 提交以下的code,将主类名字改成"Main" +// 可以直接通过 +import java.util.Scanner; + +public class Code02_BuyGoodsHaveDiscount { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + int n = sc.nextInt(); + int money = sc.nextInt(); + int[] costs = new int[n]; + long[] values = new long[n]; + int size = 0; + long ans = 0; + for (int i = 0; i < n; i++) { + // 打折前 + int pre = sc.nextInt(); + // 打折后 + int pos = sc.nextInt(); + // 满足度 + int happy = sc.nextInt(); + // 节省的钱(save) = 打折前(pre) - 打折后(pos) + int save = pre - pos; + // 带来的好处(well) = 节省的钱 - 打折后(pos) + int well = save - pos; + // 比如,一件"一定要买的商品": + // 预算 = 100,商品原价 = 10,打折后 = 3 + // 那么好处 = (10 - 3) - 3 = 4 + // 所以,这件商品把预算增加到了104,一定要买 + // 接下来,比如一件"需要考虑的商品",预算 = 104,商品原价 = 10,打折后 = 8 + // 那么好处 = (10 - 8) - 8 = -6 + // 这件商品,就花掉6元! + // 也就是说,以后花的不是打折后的值,是"坏处" + int cost = -well; + if (well >= 0) { + money += well; + ans += happy; + } else { + costs[size] = cost; + values[size++] = happy; + } + + } + long[][] dp = new long[size + 1][money + 1]; + for (int a = 0; a <= size; a++) { + for (int b = 0; b <= money; b++) { + dp[a][b] = -2; + } + } + ans += process(costs, values, size, 0, money, dp); + System.out.println(ans); + } + sc.close(); + } + + public static long process(int[] costs, long[] values, int size, int i, int money, long[][] dp) { + if (money < 0) { + return -1; + } + if (i == size) { + return 0; + } + if (dp[i][money] != -2) { + return dp[i][money]; + } + long p1 = process(costs, values, size, i + 1, money, dp); + long p2 = -1; + long next = process(costs, values, size, i + 1, money - costs[i], dp); + if (next != -1) { + p2 = values[i] + next; + } + long ans = Math.max(p1, p2); + dp[i][money] = ans; + return ans; + } + +} \ No newline at end of file diff --git a/算法周更班/class_2022_03_4_week/Code03_MinTowNumberSumABS.java b/算法周更班/class_2022_03_4_week/Code03_MinTowNumberSumABS.java new file mode 100644 index 0000000..1a46cf2 --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code03_MinTowNumberSumABS.java @@ -0,0 +1,159 @@ +package class_2022_03_4_week; + +import java.util.Arrays; + +// 来自学员问题 +// 给定一个数组arr,可能有正、有负、有0,无序 +// 只能挑选两个数字,想尽量让两个数字加起来的绝对值尽量小 +// 返回可能的最小的值 +public class Code03_MinTowNumberSumABS { + + public static int minSumABS1(int[] arr) { + if (arr == null || arr.length < 2) { + return -1; + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + ans = Math.min(ans, Math.abs(arr[i] + arr[j])); + } + } + return ans; + } + + public static int minSumABS2(int[] arr) { + if (arr == null || arr.length < 2) { + return -1; + } + Arrays.sort(arr); + int n = arr.length; + int split = -1; + for (int i = 0; i < n; i++) { + if (arr[i] >= 0) { + split = i; + break; + } + } + if (split == 0) { + return arr[0] + arr[1]; + } + if (split == -1) { + return -arr[n - 2] - arr[n - 1]; + } + int ans = Integer.MAX_VALUE; + if (split + 1 < n) { + ans = arr[split] + arr[split + 1]; + } + if (split - 2 >= 0) { + ans = Math.min(ans, -arr[split - 1] - arr[split - 2]); + } + for (int i = 0; i < split; i++) { + ans = Math.min(ans, Math.abs(arr[i] + near(arr, split, -arr[i]))); + } + return ans; + } + + // arr[start...]是有序的 + // 返回离num最近的数字 + public static int near(int[] arr, int start, int num) { + int l = start; + int r = arr.length - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = (l + r) / 2; + if (arr[m] <= num) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + if (ans == -1) { + return arr[start]; + } else { + if (ans == arr.length - 1) { + return arr[arr.length - 1]; + } else { + if (Math.abs(arr[ans] - num) <= Math.abs(arr[ans + 1] - num)) { + return arr[ans]; + } else { + return arr[ans + 1]; + } + } + } + } + + public static int minSumABS3(int[] arr) { + if (arr == null || arr.length < 2) { + return -1; + } + Arrays.sort(arr); + int n = arr.length; + int split = -1; + for (int i = 0; i < n; i++) { + if (arr[i] >= 0) { + split = i; + break; + } + } + if (split == 0) { + return arr[0] + arr[1]; + } + if (split == -1) { + return -arr[n - 2] - arr[n - 1]; + } + int ans = Integer.MAX_VALUE; + if (split + 1 < n) { + ans = arr[split] + arr[split + 1]; + } + if (split - 2 >= 0) { + ans = Math.min(ans, -arr[split - 1] - arr[split - 2]); + } + int r = n - 1; + for (int l = 0; l < split; l++) { + ans = Math.min(ans, Math.abs(arr[l] + arr[r])); + while (r - 1 >= split && Math.abs(arr[l] + arr[r]) >= Math.abs(arr[l] + arr[r - 1])) { + ans = Math.min(ans, Math.abs(arr[l] + arr[--r])); + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) - (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 50; + int value = 500; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int ans1 = minSumABS1(arr); + int ans2 = minSumABS2(arr); + int ans3 = minSumABS3(arr); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_4_week/Code04_JumpToTargets.java b/算法周更班/class_2022_03_4_week/Code04_JumpToTargets.java new file mode 100644 index 0000000..88a286e --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code04_JumpToTargets.java @@ -0,0 +1,45 @@ +package class_2022_03_4_week; + +// 来自字节 +// 一开始在0位置,每一次都可以向左或者向右跳 +// 第i次能向左或者向右跳严格的i步 +// 请问从0到x位置,至少跳几次可以到达 +// 字节考的问题其实就是这个问题 +// leetcode测试链接 : https://leetcode.com/problems/reach-a-number/ +public class Code04_JumpToTargets { + + public static int reachNumber(long target) { + if (target == 0) { + return 0; + } + target = Math.abs(target); + long l = 0; + long r = target; + long m = 0; + long near = 0; + while (l <= r) { + m = (l + r) / 2; + if (sum(m) >= target) { + near = m; + r = m - 1; + } else { + l = m + 1; + } + } + if (sum(near) == target) { + return (int)near; + } + if (((sum(near) - target) & 1) == 1) { + near++; + } + if (((sum(near) - target) & 1) == 1) { + near++; + } + return (int)near; + } + + public static long sum(long n) { + return (n * (n + 1)) / 2; + } + +} diff --git a/算法周更班/class_2022_03_4_week/Code05_HowManyWaysFromBottomToTop.java b/算法周更班/class_2022_03_4_week/Code05_HowManyWaysFromBottomToTop.java new file mode 100644 index 0000000..2816a0e --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code05_HowManyWaysFromBottomToTop.java @@ -0,0 +1,81 @@ +package class_2022_03_4_week; + +// 来自理想汽车 +// a -> b,代表a在食物链中被b捕食 +// 给定一个有向无环图,返回 +// 这个图中从最初级动物到最顶级捕食者的食物链有几条 +// 线上测试链接 : https://www.luogu.com.cn/problem/P4017 +// 以下代码都提交,提交时把主类名改成"Main"即可 +// 注意:洛谷测试平台对java提交非常不友好,空间限制可能会卡住,C++的提交就完全不会 +// 所以提交时如果显示失败,就多提交几次,一定是能成功的 +// 这道题本身就是用到拓扑排序,没什么特别的 +// 但是为了提交能通过,逼迫我在空间上做的优化值得好好说一下,可以推广到其他题目 +import java.util.Arrays; +import java.util.Scanner; + +public class Code05_HowManyWaysFromBottomToTop { + + public static int[] in = new int[5001]; + public static boolean[] out = new boolean[5001]; + public static int[] lines = new int[5001]; + public static int[] headEdge = new int[5001]; + public static int[] queue = new int[5001]; + public static int mod = 80112002; + public static int n = 0; + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + Arrays.fill(in, 0); + Arrays.fill(out, false); + Arrays.fill(lines, 0); + Arrays.fill(headEdge, 0); + n = sc.nextInt(); + int m = sc.nextInt(); + int[] preEdge = new int[m + 1]; + int[] edgesTo = new int[m + 1]; + for (int i = 1; i <= m; i++) { + int from = sc.nextInt(); + int to = sc.nextInt(); + edgesTo[i] = to; + preEdge[i] = headEdge[from]; + headEdge[from] = i; + out[from] = true; + in[to]++; + } + System.out.println(howManyWays(preEdge, edgesTo)); + } + sc.close(); + } + + public static int howManyWays(int[] preEdge, int[] edgesTo) { + int ql = 0; + int qr = 0; + for (int i = 1; i <= n; i++) { + if (in[i] == 0) { + queue[qr++] = i; + lines[i] = 1; + } + } + while (ql < qr) { + int cur = queue[ql++]; + int edge = headEdge[cur]; + while (edge != 0) { + int next = edgesTo[edge]; + lines[next] = (lines[next] + lines[cur]) % mod; + if (--in[next] == 0) { + queue[qr++] = next; + } + edge = preEdge[edge]; + } + } + int ans = 0; + for (int i = 1; i <= n; i++) { + if (!out[i]) { + ans = (ans + lines[i]) % mod; + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_03_4_week/Code06_LongestContinuousTrees.java b/算法周更班/class_2022_03_4_week/Code06_LongestContinuousTrees.java new file mode 100644 index 0000000..0e77cc9 --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code06_LongestContinuousTrees.java @@ -0,0 +1,21 @@ +package class_2022_03_4_week; + +// 来自学员问题 +// 给定一个数字n,表示一开始有编号1~n的树木,列成一条直线 +// 给定一个有序数组arr,表示现在哪些树已经没了,arr[i]一定在[1,n]范围 +// 给定一个数字m,表示你可以补种多少棵树 +// 返回补种之后,最长的连续树木,有多少棵 +public class Code06_LongestContinuousTrees { + + public static int longestTrees(int n, int m, int[] arr) { + int ans = 0; + int start = 1; + for (int i = 0, j = m; j < arr.length; i++, j++) { + ans = Math.max(ans, arr[j] - start); + start = arr[i] + 1; + } + ans = Math.max(ans, n - start + 1); + return ans; + } + +} diff --git a/算法周更班/class_2022_03_4_week/Code07_IrregularSudoku.java b/算法周更班/class_2022_03_4_week/Code07_IrregularSudoku.java new file mode 100644 index 0000000..0c2e7f4 --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code07_IrregularSudoku.java @@ -0,0 +1,127 @@ +package class_2022_03_4_week; + +// 来自网易 +// 不规则数独问题 +// 3*3填数独 +// 每一行要填1~3 +// 每一列要填1~3 +// 3*3的区域会拆分成不规则的三个集团区域 +// 每个集团区域3个格子 +// 每个集团的区域都一定是一个连在一起的整体,可能不规则 +// 每个集团内要填1~3 +// 如果只有一个解返回"Unique",如果有多个解返回"Multiple",如果没有解返回"No" +// 解析请看,大厂刷题班,28节,leetcode原题,数独那两个题 +// 本题就是改变一下桶的归属而已 +public class Code07_IrregularSudoku { + + // sudoku[i][j] == 0,代表这个位置没有数字,需要填 + // sudoku[i][j] != 0,代表这个位置有数字,不需要填 + // map[0] = {0,0}、{0,1}、{1,0} 代表0集团拥有的三个点 + // map[i] = {a,b}、{c,d}、{e,f} 代表i集团拥有的三个点 + public static String solution(int[][] sudoku, int[][][] map) { + boolean[][] row = new boolean[3][4]; + boolean[][] col = new boolean[3][4]; + boolean[][] bucket = new boolean[3][4]; + int[][] own = new int[3][3]; + for (int i = 0; i < 3; i++) { + for (int[] arr : map[i]) { + own[arr[0]][arr[1]] = i; + } + } + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (sudoku[i][j] != 0) { + row[i][sudoku[i][j]] = true; + col[j][sudoku[i][j]] = true; + bucket[own[i][j]][sudoku[i][j]] = true; + } + } + } + int ans = process(sudoku, 0, 0, row, col, bucket, own); + return ans == 0 ? "No" : (ans == 1 ? "Unique" : "Multiple"); + } + + public static int process(int[][] sudoku, int i, int j, boolean[][] row, boolean[][] col, boolean[][] bucket, + int[][] own) { + if (i == 3) { + return 1; + } + int nexti = j != 2 ? i : i + 1; + int nextj = j != 2 ? j + 1 : 0; + if (sudoku[i][j] != 0) { + return process(sudoku, nexti, nextj, row, col, bucket, own); + } else { + int ans = 0; + int bid = own[i][j]; + for (int num = 1; num <= 3; num++) { + if ((!row[i][num]) && (!col[j][num]) && (!bucket[bid][num])) { + row[i][num] = true; + col[j][num] = true; + bucket[bid][num] = true; + sudoku[i][j] = num; + ans += process(sudoku, nexti, nextj, row, col, bucket, own); + row[i][num] = false; + col[j][num] = false; + bucket[bid][num] = false; + sudoku[i][j] = 0; + if (ans > 1) { + return ans; + } + } + } + return ans; + } + } + + public static void main(String[] args) { + int[][] sudoku1 = { + { 0, 2, 0 }, + { 1, 0, 2 }, + { 0, 0, 0 } + }; + int[][][] map1 = { + { { 0, 0 }, { 0, 1 }, { 1, 0 } }, + { { 0, 2 }, { 1, 1 }, { 1, 2 } }, + { { 2, 0 }, { 2, 1 }, { 2, 2 } } + }; + System.out.println(solution(sudoku1, map1)); + + int[][] sudoku2 = { + { 0, 0, 3 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }; + int[][][] map2 = { + { { 0, 0 }, { 1, 0 }, { 1, 1 } }, + { { 0, 1 }, { 0, 2 }, { 1, 2 } }, + { { 2, 0 }, { 2, 1 }, { 2, 2 } } + }; + System.out.println(solution(sudoku2, map2)); + + int[][] sudoku3 = { + { 0, 0, 3 }, + { 1, 0, 0 }, + { 0, 0, 2 } + }; + int[][][] map3 = { + { { 0, 0 }, { 1, 0 }, { 1, 1 } }, + { { 0, 1 }, { 0, 2 }, { 1, 2 } }, + { { 2, 0 }, { 2, 1 }, { 2, 2 } } + }; + System.out.println(solution(sudoku3, map3)); + + int[][] sudoku4 = { + { 3, 0, 3 }, + { 1, 0, 0 }, + { 0, 0, 2 } + }; + int[][][] map4 = { + { { 0, 0 }, { 1, 0 }, { 1, 1 } }, + { { 0, 1 }, { 0, 2 }, { 1, 2 } }, + { { 2, 0 }, { 2, 1 }, { 2, 2 } } + }; + System.out.println(solution(sudoku4, map4)); + + } + +} diff --git a/算法周更班/class_2022_03_4_week/Code08_EggXtoY.java b/算法周更班/class_2022_03_4_week/Code08_EggXtoY.java new file mode 100644 index 0000000..424af25 --- /dev/null +++ b/算法周更班/class_2022_03_4_week/Code08_EggXtoY.java @@ -0,0 +1,82 @@ +package class_2022_03_4_week; + +// 来自学员问题 +// 大妈一开始手上有x个鸡蛋,她想让手上的鸡蛋数量变成y +// 操作1 : 从仓库里拿出1个鸡蛋到手上,x变成x+1个 +// 操作2 : 如果手上的鸡蛋数量是3的整数倍,大妈可以直接把三分之二的鸡蛋放回仓库,手里留下三分之一 +// 返回从x到y的最小操作次数 +// 1 <= x,y <= 10^18 +// 练一下,用平凡解做限制 +public class Code08_EggXtoY { + + // 彻底贪心! + public static int minTimes1(int x, int y) { + if (x <= y) { + return y - x; + } + // 0 0 + // 1 2 + // 2 1 + int mod = x % 3; + // 鸡蛋拿到3的整数倍,需要耗费的行动点数 + int need = mod == 0 ? 0 : (mod == 1 ? 2 : 1); + return need + 1 + minTimes1((x + 2) / 3, y); + } + + public static int minTimes2(int x, int y) { + if (x <= y) { + return y - x; + } + int limit = minTimes1(x, y); + int[][] dp = new int[x + limit + 1][limit + 1]; + return process(x, y, 0, limit, dp); + } + + // 当前鸡蛋数量cur,目标aim + // 之前已经用了多少行动点,pre + // limit : 一定行动点,超过limit,不需要尝试了! + public static int process(int cur, int aim, int pre, int limit, int[][] dp) { + if (pre > limit) { + return Integer.MAX_VALUE; + } + if (dp[cur][pre] != 0) { + return dp[cur][pre]; + } + int ans = 0; + if (cur == aim) { + ans = pre; + } else { + int p1 = process(cur + 1, aim, pre + 1, limit, dp); + int p2 = Integer.MAX_VALUE; + if (cur % 3 == 0) { + p2 = process(cur / 3, aim, pre + 1, limit, dp); + } + ans = Math.min(p1, p2); + } + dp[cur][pre] = ans; + return ans; + } + + public static void main(String[] args) { + int max = 3000; + int testTime = 500; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int x = (int) (Math.random() * max) + 1; + int y = (int) (Math.random() * max) + 1; + int ans1 = minTimes1(x, y); + int ans2 = minTimes2(x, y); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println("x = " + x); + System.out.println("y = " + y); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + + } + +} diff --git a/算法周更班/class_2022_03_5_week/Code01_KMAlgorithm.java b/算法周更班/class_2022_03_5_week/Code01_KMAlgorithm.java new file mode 100644 index 0000000..adc3ced --- /dev/null +++ b/算法周更班/class_2022_03_5_week/Code01_KMAlgorithm.java @@ -0,0 +1,166 @@ +// 注意本文件中,graph不是邻接矩阵的含义,而是一个二部图 +// 在长度为N的邻接矩阵matrix中,所有的点有N个,matrix[i][j]表示点i到点j的距离或者权重 +// 而在二部图graph中,所有的点有2*N个,行所对应的点有N个,列所对应的点有N个 +// 而且认为,行所对应的点之间是没有路径的,列所对应的点之间也是没有路径的! + +package class_2022_03_5_week; + +import java.util.Arrays; + +// km算法 +// O(N^3),最大匹配问题,最优的解! +public class Code01_KMAlgorithm { + + // 暴力解 + public static int right(int[][] graph) { + int N = graph.length; + int[] to = new int[N]; + for (int i = 0; i < N; i++) { + to[i] = 1; + } + return process(0, to, graph); + } + + public static int process(int from, int[] to, int[][] graph) { + if (from == graph.length) { + return 0; + } + int ans = 0; + for (int i = 0; i < to.length; i++) { + if (to[i] == 1) { + to[i] = 0; + ans = Math.max(ans, graph[from][i] + process(from + 1, to, graph)); + to[i] = 1; + } + } + return ans; + } + + public static int km(int[][] graph) { + int N = graph.length; + int[] match = new int[N]; + int[] lx = new int[N]; + int[] ly = new int[N]; + // dfs过程中,碰过的点! + boolean[] x = new boolean[N]; + boolean[] y = new boolean[N]; + // 降低的预期! + // 公主上,打一个,降低预期的值,只维持最小! + int[] slack = new int[N]; + int invalid = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + match[i] = -1; + lx[i] = -invalid; + for (int j = 0; j < N; j++) { + lx[i] = Math.max(lx[i], graph[i][j]); + } + ly[i] = 0; + } + for (int from = 0; from < N; from++) { + for (int i = 0; i < N; i++) { + slack[i] = invalid; + } + Arrays.fill(x, false); + Arrays.fill(y, false); + // dfs() : from王子,能不能不降预期,匹配成功! + // 能:dfs返回true! + // 不能:dfs返回false! + while (!dfs(from, x, y, lx, ly, match, slack, graph)) { + // 刚才的dfs,失败了! + // 需要拿到,公主的slack里面,预期下降幅度的最小值! + int d = invalid; + for (int i = 0; i < N; i++) { + if (!y[i] && slack[i] < d) { + d = slack[i]; + } + } + // 按照最小预期来调整预期 + for (int i = 0; i < N; i++) { + if (x[i]) { + lx[i] = lx[i] - d; + } + if (y[i]) { + ly[i] = ly[i] + d; + } + } + Arrays.fill(x, false); + Arrays.fill(y, false); + // 然后回到while里,再次尝试 + } + } + int ans = 0; + for (int i = 0; i < N; i++) { + ans += (lx[i] + ly[i]); + } + return ans; + } + + // from, 当前的王子 + // x,王子碰没碰过 + // y, 公主碰没碰过 + // lx,所有王子的预期 + // ly, 所有公主的预期 + // match,所有公主,之前的分配,之前的爷们! + // slack,连过,但没允许的公主,最小下降的幅度 + // map,报价,所有王子对公主的报价 + // 返回,from号王子,不降预期能不能配成! + public static boolean dfs(int from, boolean[] x, boolean[] y, int[] lx, int[] ly, int[] match, int[] slack, + int[][] map) { + int N = map.length; + x[from] = true; + for (int to = 0; to < N; to++) { + if (!y[to]) { // 只有没dfs过的公主,才会去尝试 + int d = lx[from] + ly[to] - map[from][to]; + if (d != 0) {// 如果当前的路不符合预期,更新公主的slack值 + slack[to] = Math.min(slack[to], d); + } else { // 如果当前的路符合预期,尝试直接拿下,或者抢夺让之前的安排倒腾去 + y[to] = true; + if (match[to] == -1 || dfs(match[to], x, y, lx, ly, match, slack, map)) { + match[to] = from; + return true; + } + } + } + } + return false; + } + + // 为了测试 + public static int[][] randomGraph(int N, int V) { + int[][] graph = new int[N][N]; + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + int num = (int) (Math.random() * V); + graph[i][j] = num; + graph[j][i] = num; + } + } + return graph; + } + + // 为了测试 + public static void main(String[] args) { + int N = 10; + int V = 20; + int testTime = 100; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[][] graph = randomGraph(N, V); + int ans1 = right(graph); + int ans2 = km(graph); + if (ans1 != ans2) { + System.out.println("Oops!"); + for (int r = 0; r < graph.length; r++) { + for (int c = 0; c < graph.length; c++) { + System.out.print(graph[r][c] + " "); + } + System.out.println(); + } + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_5_week/Code02_ToAllSpace.java b/算法周更班/class_2022_03_5_week/Code02_ToAllSpace.java new file mode 100644 index 0000000..00e499a --- /dev/null +++ b/算法周更班/class_2022_03_5_week/Code02_ToAllSpace.java @@ -0,0 +1,213 @@ +package class_2022_03_5_week; + +import java.util.Arrays; + +// 来自微软 +// 在N*N的正方形棋盘中,有N*N个棋子,那么每个格子正好可以拥有一个棋子 +// 但是现在有些棋子聚集到一个格子上了,比如: +// 2 0 3 +// 0 1 0 +// 3 0 0 +// 如上的二维数组代表,一共3*3个格子 +// 但是有些格子有2个棋子、有些有3个、有些有1个、有些没有 +// 请你用棋子移动的方式,让每个格子都有一个棋子 +// 每个棋子可以上、下、左、右移动,每移动一步算1的代价 +// 返回最小的代价 +public class Code02_ToAllSpace { + + // 暴力解 + // 作为对数器 + public static int minDistance1(int[][] map) { + int n = 0; + int m = 0; + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + n += Math.max(0, map[i][j] - 1); + m += map[i][j] == 0 ? 1 : 0; + } + } + if (n != m || n == 0) { + return 0; + } + int[][] nodes = new int[n][2]; + int[][] space = new int[m][2]; + n = 0; + m = 0; + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + for (int k = 2; k <= map[i][j]; k++) { + nodes[n][0] = i; + nodes[n++][1] = j; + } + if (map[i][j] == 0) { + space[m][0] = i; + space[m++][1] = j; + } + } + } + return process1(nodes, 0, space); + } + + public static int process1(int[][] nodes, int index, int[][] space) { + int ans = 0; + if (index == nodes.length) { + for (int i = 0; i < nodes.length; i++) { + ans += distance(nodes[i], space[i]); + } + } else { + ans = Integer.MAX_VALUE; + for (int i = index; i < nodes.length; i++) { + swap(nodes, index, i); + ans = Math.min(ans, process1(nodes, index + 1, space)); + swap(nodes, index, i); + } + } + return ans; + } + + public static void swap(int[][] nodes, int i, int j) { + int[] tmp = nodes[i]; + nodes[i] = nodes[j]; + nodes[j] = tmp; + } + + public static int distance(int[] a, int[] b) { + return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]); + } + + // 正式方法 + // KM算法 + public static int minDistance2(int[][] map) { + int n = 0; + int m = 0; + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + n += Math.max(0, map[i][j] - 1); + m += map[i][j] == 0 ? 1 : 0; + } + } + if (n != m || n == 0) { + return 0; + } + int[][] nodes = new int[n][2]; + int[][] space = new int[m][2]; + n = 0; + m = 0; + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[0].length; j++) { + for (int k = 2; k <= map[i][j]; k++) { + nodes[n][0] = i; + nodes[n++][1] = j; + } + if (map[i][j] == 0) { + space[m][0] = i; + space[m++][1] = j; + } + } + } + int[][] graph = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + graph[i][j] = -distance(nodes[i], space[j]); + } + } + return -km(graph); + } + + public static int km(int[][] graph) { + int N = graph.length; + int[] match = new int[N]; + int[] lx = new int[N]; + int[] ly = new int[N]; + boolean[] x = new boolean[N]; + boolean[] y = new boolean[N]; + int[] slack = new int[N]; + int invalid = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + match[i] = -1; + lx[i] = -invalid; + for (int j = 0; j < N; j++) { + lx[i] = Math.max(lx[i], graph[i][j]); + } + ly[i] = 0; + } + for (int from = 0; from < N; from++) { + for (int i = 0; i < N; i++) { + slack[i] = invalid; + } + Arrays.fill(x, false); + Arrays.fill(y, false); + while (!dfs(from, x, y, lx, ly, match, slack, graph)) { + int d = invalid; + for (int i = 0; i < N; i++) { + if (!y[i] && slack[i] < d) { + d = slack[i]; + } + } + for (int i = 0; i < N; i++) { + if (x[i]) { + lx[i] = lx[i] - d; + } + if (y[i]) { + ly[i] = ly[i] + d; + } + } + Arrays.fill(x, false); + Arrays.fill(y, false); + } + } + int ans = 0; + for (int i = 0; i < N; i++) { + ans += (lx[i] + ly[i]); + } + return ans; + } + + public static boolean dfs(int from, boolean[] x, boolean[] y, int[] lx, int[] ly, int[] match, int[] slack, + int[][] map) { + int N = map.length; + x[from] = true; + for (int to = 0; to < N; to++) { + if (!y[to]) { + int d = lx[from] + ly[to] - map[from][to]; + if (d != 0) { + slack[to] = Math.min(slack[to], d); + } else { + y[to] = true; + if (match[to] == -1 || dfs(match[to], x, y, lx, ly, match, slack, map)) { + match[to] = from; + return true; + } + } + } + } + return false; + } + + // 为了测试 + public static int[][] randomValidMatrix(int len) { + int[][] ans = new int[len][len]; + int all = len * len; + for (int i = 1; i <= all; i++) { + ans[(int) (Math.random() * len)][(int) (Math.random() * len)]++; + } + return ans; + } + + public static void main(String[] args) { + int len = 4; + int testTimes = 1000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int[][] map = randomValidMatrix(len); + int ans1 = minDistance1(map); + int ans2 = minDistance2(map); + if (ans1 != ans2) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_03_5_week/Code03_MaximumAndSumOfArray.java b/算法周更班/class_2022_03_5_week/Code03_MaximumAndSumOfArray.java new file mode 100644 index 0000000..433d74d --- /dev/null +++ b/算法周更班/class_2022_03_5_week/Code03_MaximumAndSumOfArray.java @@ -0,0 +1,90 @@ +package class_2022_03_5_week; + +import java.util.Arrays; + +// 测试链接 : https://leetcode.com/problems/maximum-and-sum-of-array/ +public class Code03_MaximumAndSumOfArray { + + public static int maximumANDSum(int[] arr, int m) { + m <<= 1; + int[][] graph = new int[m][m]; + for (int i = 0; i < arr.length; i++) { + for (int j = 0, num = 1; j < m; num++, j += 2) { + graph[i][j] = arr[i] & num; + graph[i][j + 1] = arr[i] & num; + } + } + return km(graph); + } + + public static int km(int[][] graph) { + int N = graph.length; + int[] match = new int[N]; + int[] lx = new int[N]; + int[] ly = new int[N]; + boolean[] x = new boolean[N]; + boolean[] y = new boolean[N]; + int[] slack = new int[N]; + int invalid = Integer.MAX_VALUE; + for (int i = 0; i < N; i++) { + match[i] = -1; + lx[i] = -invalid; + for (int j = 0; j < N; j++) { + lx[i] = Math.max(lx[i], graph[i][j]); + } + ly[i] = 0; + } + for (int from = 0; from < N; from++) { + for (int i = 0; i < N; i++) { + slack[i] = invalid; + } + Arrays.fill(x, false); + Arrays.fill(y, false); + while (!dfs(from, x, y, lx, ly, match, slack, graph)) { + int d = invalid; + for (int i = 0; i < N; i++) { + if (!y[i] && slack[i] < d) { + d = slack[i]; + } + } + for (int i = 0; i < N; i++) { + if (x[i]) { + lx[i] = lx[i] - d; + } + if (y[i]) { + ly[i] = ly[i] + d; + } + } + Arrays.fill(x, false); + Arrays.fill(y, false); + } + } + int ans = 0; + for (int i = 0; i < N; i++) { + ans += (lx[i] + ly[i]); + } + return ans; + } + + public static boolean dfs(int from, boolean[] x, boolean[] y, int[] lx, int[] ly, int[] match, int[] slack, + int[][] map) { + int N = map.length; + x[from] = true; + for (int to = 0; to < N; to++) { + if (!y[to]) { + int d = lx[from] + ly[to] - map[from][to]; + if (d != 0) { + slack[to] = Math.min(slack[to], d); + } else { + y[to] = true; + if (match[to] == -1 || dfs(match[to], x, y, lx, ly, match, slack, map)) { + match[to] = from; + return true; + } + } + } + } + return false; + } + +} diff --git a/算法周更班/class_2022_03_5_week/Code04_KillAllSameTime.java b/算法周更班/class_2022_03_5_week/Code04_KillAllSameTime.java new file mode 100644 index 0000000..051368c --- /dev/null +++ b/算法周更班/class_2022_03_5_week/Code04_KillAllSameTime.java @@ -0,0 +1,18 @@ +package class_2022_03_5_week; + +// 来自网易 +// 我军一起干掉敌人的最少移动数 +// km算法的又一个题 +// 给定一个矩阵int[][] matrix +// matrix[i][j] == -2,代表此处(i,j)有山脉,无法通行 +// matrix[i][j] == -1,代表此处(i,j)是一个敌军 +// matrix[i][j] == 0,代表此处(i,j)是空地,可以自由行动 +// matrix[i][j] > 0,代表此处(i,j)是一个我军,行动能力就是matrix[i][j] +// 我军只能上、下、左、右移动,只可以穿过同样是我军的地点和空地的地点,但是最多移动matrix[i][j]步 +// 任何一个我军都不能穿过山脉,任何一个我军可以来到敌军的位置,表示消灭了敌军,但是如果这么做了,这个我军就不能再移动了 +// 你可以任意决定所有我军的行动策略,每一步你都可以随意选择一个友军移动随意方向的,但是必须合法。 +// 如果你可以让所有我军在消耗完自身的行动能力之前,消灭所有的敌军,请返回总距离的最小值 +// 如果你就是无法消灭所有敌军,返回-1 +public class Code04_KillAllSameTime { + +} diff --git a/算法周更班/class_2022_04_1_week/Code01_FourNumbersMinusOne.java b/算法周更班/class_2022_04_1_week/Code01_FourNumbersMinusOne.java new file mode 100644 index 0000000..006bd9d --- /dev/null +++ b/算法周更班/class_2022_04_1_week/Code01_FourNumbersMinusOne.java @@ -0,0 +1,58 @@ +package class_2022_04_1_week; + +import java.util.Arrays; + +// 来自阿里笔试 +// 牛牛今年上幼儿园了,老师叫他学习减法 +// 老师给了他5个数字,他每次操作可以选择其中的4个数字减1 +// 减一之后的数字不能小于0,因为幼儿园的牛牛还没有接触过负数 +// 现在牛牛想知道,自己最多可以进行多少次这样的操作 +// 扩展问题来自leetcode 2141,掌握了这个题原始问题就非常简单了 +// leetcode测试链接 : https://leetcode.com/problems/maximum-running-time-of-n-computers/ +public class Code01_FourNumbersMinusOne { + + public static long maxRunTime(int n, int[] arr) { + Arrays.sort(arr); + int size = arr.length; + long[] sums = new long[size]; + sums[0] = arr[0]; + for (int i = 1; i < size; i++) { + sums[i] = sums[i - 1] + arr[i]; + } + long l = 0; + long m = 0; + long r = sums[size - 1] / n; + long ans = -1; + while (l <= r) { + m = (l + r) / 2; + if (ok(arr, sums, m, n)) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static boolean ok(int[] arr, long[] sum, long time, int num) { + int l = 0; + int m = 0; + int r = arr.length - 1; + int left = arr.length; + // >= time 最左 + while (l <= r) { + m = (l + r) / 2; + if (arr[m] >= time) { + left = m; + r = m - 1; + } else { + l = m + 1; + } + } + num -= arr.length - left; + long rest = left == 0 ? 0 : sum[left - 1]; + return time * (long) num <= rest; + } + +} diff --git a/算法周更班/class_2022_04_1_week/Code02_MaxOrSmallestSubarray.java b/算法周更班/class_2022_04_1_week/Code02_MaxOrSmallestSubarray.java new file mode 100644 index 0000000..702618b --- /dev/null +++ b/算法周更班/class_2022_04_1_week/Code02_MaxOrSmallestSubarray.java @@ -0,0 +1,116 @@ +package class_2022_04_1_week; + +// 来自学员问题 +// 找到非负数组中拥有"最大或的结果"的最短子数组 +public class Code02_MaxOrSmallestSubarray { + + public static int longest1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = 0; + for (int num : arr) { + max |= num; + } + if (max == 0) { + return 1; + } + int n = arr.length; + int ans = n; + for (int i = 0; i < n; i++) { + int cur = 0; + for (int j = i; j < n; j++) { + cur |= arr[j]; + if (cur == max) { + ans = Math.min(ans, j - i + 1); + break; + } + } + } + return ans; + } + + public static int longest2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = 0; + for (int num : arr) { + max |= num; + } + if (max == 0) { + return 1; + } + int n = arr.length; + int ans = n; + int[] counts = new int[32]; + int l = 0; + int cur = 0; + for (int r = 0; r < n; r++) { + cur = add(counts, arr[r]); + while (cur == max) { + cur = delete(counts, arr[l++]); + } + if (l > 0) { + cur = add(counts, arr[--l]); + } + if (cur == max) { + ans = Math.min(ans, r - l + 1); + } + } + return ans; + } + + public static int add(int[] counts, int num) { + int ans = 0; + for (int i = 0; i < 32; i++) { + counts[i] += (num & (1 << i)) != 0 ? 1 : 0; + ans |= (counts[i] > 0 ? 1 : 0) << i; + } + return ans; + } + + public static int delete(int[] counts, int num) { + int ans = 0; + for (int i = 0; i < 32; i++) { + counts[i] -= (num & (1 << i)) != 0 ? 1 : 0; + ans |= (counts[i] > 0 ? 1 : 0) << i; + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 50; + int value = 50000; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int ans1 = longest1(arr); + int ans2 = longest2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_1_week/Code03_ArrangeMeetingPosCancelPre.java b/算法周更班/class_2022_04_1_week/Code03_ArrangeMeetingPosCancelPre.java new file mode 100644 index 0000000..ea560b7 --- /dev/null +++ b/算法周更班/class_2022_04_1_week/Code03_ArrangeMeetingPosCancelPre.java @@ -0,0 +1,219 @@ +package class_2022_04_1_week; + +import java.util.ArrayList; +import java.util.Arrays; + +// 来自通维数码 +// 每个会议给定开始和结束时间 +// 后面的会议如果跟前面的会议有任何冲突,完全取消冲突的、之前的会议,安排当前的 +// 给定一个会议数组,返回安排的会议列表 +public class Code03_ArrangeMeetingPosCancelPre { + + // 比较暴力的解 + // 为了对数器来验证 + public static ArrayList arrange1(int[][] meetings) { + int max = 0; + for (int[] meeting : meetings) { + max = Math.max(max, meeting[1]); + } + boolean[] occupy = new boolean[max + 1]; + ArrayList ans = new ArrayList<>(); + for (int i = meetings.length - 1; i >= 0; i--) { + int[] cur = meetings[i]; + boolean add = true; + for (int j = cur[0]; j < cur[1]; j++) { + if (occupy[j]) { + add = false; + break; + } + } + if (add) { + ans.add(cur); + } + for (int j = cur[0]; j < cur[1]; j++) { + occupy[j] = true; + } + } + return ans; + } + + // 最优解 + // 会议有N个,时间复杂度O(N*logN) + public static ArrayList arrange2(int[][] meetings) { + int n = meetings.length; + // n << 1 -> n*2 + int[] rank = new int[n << 1]; + for (int i = 0; i < meetings.length; i++) { + rank[i] = meetings[i][0]; // 会议开头点 + rank[i + n] = meetings[i][1] - 1; // 会议的结束点 + } + Arrays.sort(rank); + // n*2 + SegmentTree st = new SegmentTree(n << 1); + // 哪些会议安排了,放入到ans里去! + ArrayList ans = new ArrayList<>(); + // 从右往左遍历,意味着,后出现的会议,先看看能不能安排 + for (int i = meetings.length - 1; i >= 0; i--) { + // cur 当前会议 + int[] cur = meetings[i]; + // cur[0] = 17万 -> 6 + int from = rank(rank, cur[0]); + // cur[1] = 90 -> 89 -> 2 + int to = rank(rank, cur[1] - 1); + if (st.sum(from, to) == 0) { + ans.add(cur); + } + st.add(from, to, 1); + } + return ans; + } + + public static int rank(int[] rank, int num) { + int l = 0; + int r = rank.length - 1; + int m = 0; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (rank[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans + 1; + } + + public static class SegmentTree { + private int n; + private int[] sum; + private int[] lazy; + + public SegmentTree(int size) { + n = size + 1; + sum = new int[n << 2]; + lazy = new int[n << 2]; + n--; + } + + private void pushUp(int rt) { + sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; + } + + private void pushDown(int rt, int ln, int rn) { + if (lazy[rt] != 0) { + lazy[rt << 1] += lazy[rt]; + sum[rt << 1] += lazy[rt] * ln; + lazy[rt << 1 | 1] += lazy[rt]; + sum[rt << 1 | 1] += lazy[rt] * rn; + lazy[rt] = 0; + } + } + + public void add(int L, int R, int C) { + add(L, R, C, 1, n, 1); + } + + private void add(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + sum[rt] += C * (r - l + 1); + lazy[rt] += C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + add(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + add(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + public int sum(int L, int R) { + return query(L, R, 1, n, 1); + } + + private int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return sum[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans += query(L, R, l, mid, rt << 1); + } + if (R > mid) { + ans += query(L, R, mid + 1, r, rt << 1 | 1); + } + return ans; + } + + } + + // 为了测试 + public static int[][] randomMeeting(int len, int time) { + int[][] meetings = new int[len][2]; + for (int i = 0; i < len; i++) { + int a = (int) (Math.random() * (time + 1)); + int b = (int) (Math.random() * (time + 1)); + if (a == b) { + b++; + } + meetings[i][0] = Math.min(a, b); + meetings[i][1] = Math.max(a, b); + } + return meetings; + } + + // 为了测试 + public static int[][] copyMeetings(int[][] meetings) { + int len = meetings.length; + int[][] ans = new int[len][2]; + for (int i = 0; i < len; i++) { + ans[i][0] = meetings[i][0]; + ans[i][1] = meetings[i][1]; + } + return ans; + } + + // 为了测试 + public static boolean equal(ArrayList arr1, ArrayList arr2) { + if (arr1.size() != arr2.size()) { + return false; + } + for (int i = 0; i < arr1.size(); i++) { + int[] a = arr1.get(i); + int[] b = arr2.get(i); + if (a[0] != b[0] || a[1] != b[1]) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + int n = 100; + int t = 5000; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n) + 1; + int[][] meetings1 = randomMeeting(len, t); + int[][] meetings2 = copyMeetings(meetings1); + ArrayList ans1 = arrange1(meetings1); + ArrayList ans2 = arrange2(meetings2); + if (!equal(ans1, ans2)) { + System.out.println("出错了!"); + System.out.println(ans1.size()); + System.out.println(ans2.size()); + System.out.println("===="); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_1_week/Code04_MaxScoreMoveInBoard.java b/算法周更班/class_2022_04_1_week/Code04_MaxScoreMoveInBoard.java new file mode 100644 index 0000000..a7a4935 --- /dev/null +++ b/算法周更班/class_2022_04_1_week/Code04_MaxScoreMoveInBoard.java @@ -0,0 +1,88 @@ +package class_2022_04_1_week; + +// 来自小红书 +// 小红书第一题: +// 薯队长从北向南穿过一片红薯地(南北长M,东西宽N),红薯地被划分为1x1的方格, +// 他可以从北边的任何一个格子出发,到达南边的任何一个格子, +// 但每一步只能走到东南、正南、西南方向的三个格子之一, +// 而且不能跨出红薯地,他可以获得经过的格子上的所有红薯,请问他可以获得最多的红薯个数。 +public class Code04_MaxScoreMoveInBoard { + +// // 目前来到(row, col)的位置 +// // 只能往不越界的三个方向移动:西南、正南、东南 +// // 一旦遇到最南格子,停 +// // 返回这个过程中,最大的收成 +// public static int f(int[][] board, int row, int col) { +// if (col < 0 || col == board[0].length) { +// return 0; +// } +// if (row == board.length - 1) { +// return board[row][col]; +// } +// int p1 = f(board, row + 1, col - 1); +// int p2 = f(board, row + 1, col); +// int p3 = f(board, row + 1, col + 1); +// return board[row][col] + Math.max(p1, Math.max(p2, p3)); +// } + + public static int maxScore(int[][] map) { + int ans = 0; + for (int col = 0; col < map[0].length; col++) { + ans = Math.max(ans, process(map, 0, col)); + } + return ans; + } + + public static int process(int[][] map, int row, int col) { + if (col < 0 || col == map[0].length) { + return -1; + } + if (row == map.length - 1) { + return map[row][col]; + } + int cur = map[row][col]; + int next1 = process(map, row + 1, col - 1); + int next2 = process(map, row + 1, col); + int next3 = process(map, row + 1, col + 1); + return cur + Math.max(Math.max(next1, next2), next3); + } + + public static int maxScore2(int[][] map) { + int ans = 0; + int n = map.length; + int m = map[0].length; + int[][] dp = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + dp[i][j] = -2; // -2表示,这个格子没算过! + } + } + for (int col = 0; col < map[0].length; col++) { + ans = Math.max(ans, process2(map, 0, col, dp)); + } + return ans; + } + + public static int process2(int[][] map, int row, int col, int[][] dp) { + if (col < 0 || col == map[0].length) { + return -1; + } + if (dp[row][col] != -2) { + return dp[row][col]; + } + // 继续算! + int ans = 0; + if (row == map.length - 1) { + ans = map[row][col]; + } else { + int cur = map[row][col]; + int next1 = process2(map, row + 1, col - 1, dp); + int next2 = process2(map, row + 1, col, dp); + int next3 = process2(map, row + 1, col + 1, dp); + ans = cur + Math.max(Math.max(next1, next2), next3); + } + dp[row][col] = ans; + return ans; + } + +} diff --git a/算法周更班/class_2022_04_1_week/Code05_PickKnumbersNearTowNumberMaxDiff.java b/算法周更班/class_2022_04_1_week/Code05_PickKnumbersNearTowNumberMaxDiff.java new file mode 100644 index 0000000..519ea65 --- /dev/null +++ b/算法周更班/class_2022_04_1_week/Code05_PickKnumbersNearTowNumberMaxDiff.java @@ -0,0 +1,57 @@ +package class_2022_04_1_week; + +import java.util.Arrays; + +// 小红书第二题: +// 薯队长最近在参加了一个活动,主办方提供了N个礼物以供挑选, +// 每个礼物有一个价值,范围在0 ~ 10^9之间, +// 薯队长可以从中挑选k个礼物 +// 返回:其中价值最接近的两件礼物之间相差值尽可能大的结果 +// 我们课上讲 +// +// 小红书第三题: +// 薯队长最近在玩一个游戏,这个游戏桌上会有一排不同颜色的方块, +// 每次薯队长可以选择一个方块,便可以消除这个方块以及和他左右相临的 +// 若干的颜色相同的方块,而每次消除的方块越多,得分越高。 +// 具体来说,桌上有以个方块排成一排 (1 <= N <= 200), +// 每个方块有一个颜色,用1~N之间的一个整数表示,相同的数宇代表相同的颜色, +// 每次消除的时候,会把连续的K个相同颜色的方块消除,并得到K*K的分数, +// 直到所有方块都消除。显然,不同的消除顺序得分不同,薯队长希望您能告诉他,这个游戏最多能得到多少分 +// 体系学习班,代码46节,视频在47节,消箱子原题,RemoveBoxes +public class Code05_PickKnumbersNearTowNumberMaxDiff { + + public static int maxNear(int[] arr, int k) { + if (arr.length < k) { + return -1; + } + Arrays.sort(arr); + int n = arr.length; + int l = 0; + int r = arr[n - 1] - arr[0]; + int m = 0; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (yeah(arr, k, m)) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static boolean yeah(int[] arr, int k, int limit) { + int last = arr[0]; + int pick = 1; + for (int i = 1; i < arr.length; i++) { + if (arr[i] - last >= limit) { + pick++; + last = arr[i]; + } + } + return pick >= k; + } + +} diff --git a/算法周更班/class_2022_04_1_week/Code06_TopMinSubsquenceSum.java b/算法周更班/class_2022_04_1_week/Code06_TopMinSubsquenceSum.java new file mode 100644 index 0000000..67ad8c5 --- /dev/null +++ b/算法周更班/class_2022_04_1_week/Code06_TopMinSubsquenceSum.java @@ -0,0 +1,114 @@ +package class_2022_04_1_week; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.PriorityQueue; + +// 给定一个数组arr,含有n个数字,都是非负数 +// 给定一个正数k +// 返回所有子序列中,累加和最小的前k个子序列累加和 +// 假设K不大,怎么算最快? +public class Code06_TopMinSubsquenceSum { + + public static int[] topMinSum1(int[] arr, int k) { + ArrayList allAns = new ArrayList<>(); + process(arr, 0, 0, allAns); + allAns.sort((a, b) -> a.compareTo(b)); + int[] ans = new int[k]; + for (int i = 0; i < k; i++) { + ans[i] = allAns.get(i); + } + return ans; + } + + public static void process(int[] arr, int index, int sum, ArrayList ans) { + if (index == arr.length) { + ans.add(sum); + } else { + process(arr, index + 1, sum, ans); + process(arr, index + 1, sum + arr[index], ans); + } + } + + public static int[] topMinSum2(int[] arr, int k) { + Arrays.sort(arr); + // (最右的下标,集合的累加和) + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[1] - b[1]); + heap.add(new int[] { 0, arr[0] }); + int[] ans = new int[k]; + // ans[0] = 0 + // 0 1 2 k-1 + // k个! + for (int i = 1; i < k; i++) { + int[] cur = heap.poll(); + // (7, 100) + // 左 :8, 100 - arr[7] + arr[8] + // 右 :8, 100 + arr[8] + int last = cur[0]; + int sum = cur[1]; + ans[i] = sum; + if (last + 1 < arr.length) { + heap.add(new int[] { last + 1, sum - arr[last] + arr[last + 1] }); + heap.add(new int[] { last + 1, sum + arr[last + 1] }); + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value); + } + return arr; + } + + // 为了测试 + public static boolean equals(int[] ans1, int[] ans2) { + if (ans1.length != ans2.length) { + return false; + } + for (int i = 0; i < ans1.length; i++) { + if (ans1[i] != ans2[i]) { + return false; + } + } + return true; + } + + // 为了测试 + public static void main(String[] args) { + int n = 10; + int v = 40; + int testTime = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n) + 1; + int[] arr = randomArray(len, v); + int k = (int) (Math.random() * ((1 << len) - 1)) + 1; + int[] ans1 = topMinSum1(arr, k); + int[] ans2 = topMinSum2(arr, k); + if (!equals(ans1, ans2)) { + System.out.println("出错了!"); + System.out.print("arr : "); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("k : " + k); + for (int num : ans1) { + System.out.print(num + " "); + } + System.out.println(); + for (int num : ans2) { + System.out.print(num + " "); + } + System.out.println(); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_1_week/Code07_TopMaxSubsquenceSum.java b/算法周更班/class_2022_04_1_week/Code07_TopMaxSubsquenceSum.java new file mode 100644 index 0000000..0312656 --- /dev/null +++ b/算法周更班/class_2022_04_1_week/Code07_TopMaxSubsquenceSum.java @@ -0,0 +1,124 @@ +package class_2022_04_1_week; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.PriorityQueue; + +// 来自Amazon +// 给定一个数组arr,含有n个数字,可能有正、有负、有0 +// 给定一个正数k +// 返回所有子序列中,累加和最大的前k个子序列累加和 +// 假设K不大,怎么算最快? +public class Code07_TopMaxSubsquenceSum { + + public static int[] topMaxSum1(int[] arr, int k) { + ArrayList allAns = new ArrayList<>(); + process(arr, 0, 0, allAns); + allAns.sort((a, b) -> a.compareTo(b)); + int[] ans = new int[k]; + for (int i = allAns.size() - 1, j = 0; j < k; i--, j++) { + ans[j] = allAns.get(i); + } + return ans; + } + + public static void process(int[] arr, int index, int sum, ArrayList ans) { + if (index == arr.length) { + ans.add(sum); + } else { + process(arr, index + 1, sum, ans); + process(arr, index + 1, sum + arr[index], ans); + } + } + + public static int[] topMaxSum2(int[] arr, int k) { + int sum = 0; + for (int i = 0; i < arr.length; i++) { + if (arr[i] >= 0) { + sum += arr[i]; + } else { + arr[i] = -arr[i]; + } + } + int[] ans = topMinSum(arr, k); + for (int i = 0; i < ans.length; i++) { + ans[i] = sum - ans[i]; + } + return ans; + } + + public static int[] topMinSum(int[] arr, int k) { + Arrays.sort(arr); + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[1] - b[1]); + heap.add(new int[] { 0, arr[0] }); + int[] ans = new int[k]; + for (int i = 1; i < k; i++) { + int[] cur = heap.poll(); + int last = cur[0]; + int sum = cur[1]; + ans[i] = sum; + if (last + 1 < arr.length) { + heap.add(new int[] { last + 1, sum - arr[last] + arr[last + 1] }); + heap.add(new int[] { last + 1, sum + arr[last + 1] }); + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value) + 1; + } + return arr; + } + + // 为了测试 + public static boolean equals(int[] ans1, int[] ans2) { + if (ans1.length != ans2.length) { + return false; + } + for (int i = 0; i < ans1.length; i++) { + if (ans1[i] != ans2[i]) { + return false; + } + } + return true; + } + + // 为了测试 + public static void main(String[] args) { + int n = 10; + int v = 40; + int testTime = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n) + 1; + int[] arr = randomArray(len, v); + int k = (int) (Math.random() * ((1 << len) - 1)) + 1; + int[] ans1 = topMaxSum1(arr, k); + int[] ans2 = topMaxSum2(arr, k); + if (!equals(ans1, ans2)) { + System.out.println("出错了!"); + System.out.print("arr : "); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("k : " + k); + for (int num : ans1) { + System.out.print(num + " "); + } + System.out.println(); + for (int num : ans2) { + System.out.print(num + " "); + } + System.out.println(); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_2_week/Code01_SumOfValuesAboutPrimes.java b/算法周更班/class_2022_04_2_week/Code01_SumOfValuesAboutPrimes.java new file mode 100644 index 0000000..93a9468 --- /dev/null +++ b/算法周更班/class_2022_04_2_week/Code01_SumOfValuesAboutPrimes.java @@ -0,0 +1,114 @@ +package class_2022_04_2_week; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; + +// 来自携程 +// 给出n个数字,你可以任选其中一些数字相乘,相乘之后得到的新数字x +// x的价值是x的不同质因子的数量 +// 返回所有选择数字的方案中,得到的x的价值之和 +public class Code01_SumOfValuesAboutPrimes { + + // 工具! + // 返回num质数因子列表(去重) + // 时间复杂度,根号(num) + public static ArrayList primes(long num) { + ArrayList ans = new ArrayList<>(); + for (long i = 2; i * i <= num && num > 1; i++) { + if (num % i == 0) { + ans.add(i); + while (num % i == 0) { + num /= i; + } + } + } + if (num != 1) { + ans.add(num); + } + return ans; + } + + public static long sumOfValues1(int[] arr) { + return process1(arr, 0, 1); + } + + public static long process1(int[] arr, int index, long pre) { + if (index == arr.length) { + return (long) primes(pre).size(); + } + long p1 = process1(arr, index + 1, pre); + long p2 = process1(arr, index + 1, pre * (long) arr[index]); + return p1 + p2; + } + + public static long sumOfValues2(int[] arr) { + // key : 某个质数因子 + // value : 有多少个数含有这个因子 + HashMap cntMap = new HashMap<>(); + for (int num : arr) { + for (long factor : primes(num)) { + cntMap.put(factor, cntMap.getOrDefault(factor, 0L) + 1L); + } + } + int n = arr.length; + long ans = 0; + // count :含有这个因子的数,有多少个 + // others : 不含有这个因子的数,有多少个 + for (long count : cntMap.values()) { + long others = n - count; + ans += (power(2, count) - 1) * power(2, others); + } + return ans; + } + + public static long power(long num, long n) { + if (n == 0L) { + return 1L; + } + long ans = 1L; + while (n > 0) { + if ((n & 1) != 0) { + ans *= num; + } + num *= num; + n >>= 1; + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int n = 10; + int v = 20; + int testTime = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(n, v); + long ans1 = sumOfValues1(arr); + long ans2 = sumOfValues2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + BigInteger all = new BigInteger("1"); + for (int num : arr) { + all = all.multiply(new BigInteger(String.valueOf(num))); + } + System.out.println("所有数都乘起来 : " + all.toString()); + System.out.println("长整型最大范围 : " + Long.MAX_VALUE); + System.out.println("可以看看上面的打印,看看是不是因为溢出了"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_2_week/Code02_MinDistanceFromLeftUpToRightDown.java b/算法周更班/class_2022_04_2_week/Code02_MinDistanceFromLeftUpToRightDown.java new file mode 100644 index 0000000..12c6b77 --- /dev/null +++ b/算法周更班/class_2022_04_2_week/Code02_MinDistanceFromLeftUpToRightDown.java @@ -0,0 +1,125 @@ +package class_2022_04_2_week; + +import java.util.PriorityQueue; + +// 来自网易 +// 3.27笔试 +// 一个二维矩阵,上面只有 0 和 1,只能上下左右移动 +// 如果移动前后的元素值相同,则耗费 1 ,否则耗费 2。 +// 问从左上到右下的最小耗费 +public class Code02_MinDistanceFromLeftUpToRightDown { + + // 一个错误的贪心 + // 网上帖子最流行的解答,看似对,其实不行 + public static int bestWalk1(int[][] map) { + int n = map.length; + int m = map[0].length; + int[][] dp = new int[n][m]; + for (int i = 1; i < m; i++) { + dp[0][i] = dp[0][i - 1] + (map[0][i - 1] == map[0][i] ? 1 : 2); + } + for (int i = 1; i < n; i++) { + dp[i][0] = dp[i - 1][0] + (map[i - 1][0] == map[i][0] ? 1 : 2); + } + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + dp[i][j] = dp[i - 1][j] + (map[i - 1][j] == map[i][j] ? 1 : 2); + dp[i][j] = Math.min(dp[i][j], dp[i][j - 1] + (map[i][j - 1] == map[i][j] ? 1 : 2)); + } + } + return dp[n - 1][m - 1]; + } + + // 正确的解法 + // Dijskra + public static int bestWalk2(int[][] map) { + int n = map.length; + int m = map[0].length; + // 小根堆:[代价,行,列] + // 根据代价,谁代价小,谁放在堆的上面 + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + // poped[i][j] == true 已经弹出过了!不要再处理,直接忽略! + // poped[i][j] == false 之间(i,j)没弹出过!要处理 + boolean[][] poped = new boolean[n][m]; + heap.add(new int[] { 0, 0, 0 }); + int ans = 0; + while (!heap.isEmpty()) { + // 当前弹出了,[代价,行,列],当前位置 + int[] cur = heap.poll(); + int dis = cur[0]; + int row = cur[1]; + int col = cur[2]; + if (poped[row][col]) { + continue; + } + // 第一次弹出! + poped[row][col] = true; + if (row == n - 1 && col == m - 1) { + ans = dis; + break; + } + add(dis, row - 1, col, map[row][col], n, m, map, poped, heap); + add(dis, row + 1, col, map[row][col], n, m, map, poped, heap); + add(dis, row, col - 1, map[row][col], n, m, map, poped, heap); + add(dis, row, col + 1, map[row][col], n, m, map, poped, heap); + } + return ans; + } + + // preDistance : 之前的距离 + // int row, int col : 当前要加入的是什么位置 + // preValue : 前一个格子是什么值, + // int n, int m :边界,固定参数 + // map: 每一个格子的值,都在map里 + // boolean[][] poped : 当前位置如果是弹出过的位置,要忽略! + // PriorityQueue heap : 小根堆 + public static void add(int preDistance, + int row, int col, int preValue, int n, int m, + int[][] map, boolean[][] poped, + PriorityQueue heap) { + if (row >= 0 && row < n && col >= 0 && col < m + && !poped[row][col]) { + heap.add(new int[] { + preDistance + (map[row][col] == preValue ? 1 : 2), + row, col }); + } + } + + // 为了测试 + public static int[][] randomMatrix(int n, int m) { + int[][] ans = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + ans[i][j] = (int) (Math.random() * 2); + } + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int n = 100; + int m = 100; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[][] map = randomMatrix(n, m); + int ans1 = bestWalk1(map); + int ans2 = bestWalk2(map); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int[] arr : map) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + } + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_2_week/Code03_MaxSumDividedBy7.java b/算法周更班/class_2022_04_2_week/Code03_MaxSumDividedBy7.java new file mode 100644 index 0000000..0412aab --- /dev/null +++ b/算法周更班/class_2022_04_2_week/Code03_MaxSumDividedBy7.java @@ -0,0 +1,82 @@ +package class_2022_04_2_week; + +// 来自美团 +// 3.26笔试 +// 给定一个非负数组,任意选择数字,使累加和最大且为7的倍数,返回最大累加和 +// n比较大,10的5次方 +public class Code03_MaxSumDividedBy7 { + + public static int maxSum1(int[] arr) { + return process1(arr, 0, 0); + } + + public static int process1(int[] arr, int index, int pre) { + if (index == arr.length) { + return pre % 7 == 0 ? pre : 0; + } + int p1 = process1(arr, index + 1, pre); + int p2 = process1(arr, index + 1, pre + arr[index]); + return Math.max(p1, p2); + } + + public static int maxSum2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int n = arr.length; + int[][] dp = new int[n][7]; + for (int i = 0; i < n; i++) { + for (int j = 1; j < 7; j++) { + dp[i][j] = -1; + } + } + dp[0][arr[0] % 7] = arr[0]; + for (int i = 1; i < n; i++) { + // 当前arr[i] % 7 的余数 + int curMod = arr[i] % 7; + for (int j = 0; j < 7; j++) { + dp[i][j] = dp[i - 1][j]; + int findMod = (7 - curMod + j) % 7; + if (dp[i - 1][findMod] != -1) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][findMod] + arr[i]); + } + } + } + return dp[n - 1][0] == -1 ? 0 : dp[n - 1][0]; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 12; + int value = 30; + int testTime = 2000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int ans1 = maxSum1(arr); + int ans2 = maxSum2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_2_week/Code04_AllJobFinishTime.java b/算法周更班/class_2022_04_2_week/Code04_AllJobFinishTime.java new file mode 100644 index 0000000..3e8ca27 --- /dev/null +++ b/算法周更班/class_2022_04_2_week/Code04_AllJobFinishTime.java @@ -0,0 +1,48 @@ +package class_2022_04_2_week; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; + +// 来自美团 +// 3.26笔试 +// 给定一个正数n, 表示有0~n-1号任务 +// 给定一个长度为n的数组time,time[i]表示i号任务做完的时间 +// 给定一个二维数组matrix +// matrix[j] = {a, b} 代表:a任务想要开始,依赖b任务的完成 +// 只要能并行的任务都可以并行,但是任何任务只有依赖的任务完成,才能开始 +// 返回一个长度为n的数组ans,表示每个任务完成的时间 +// 输入可以保证没有循环依赖 +public class Code04_AllJobFinishTime { + + public static int[] finishTime(int n, int[] time, int[][] matrix) { + ArrayList> nexts = new ArrayList<>(); + for (int i = 0; i < n; i++) { + nexts.add(new ArrayList<>()); + } + int[] in = new int[n]; + for (int[] line : matrix) { + nexts.get(line[1]).add(line[0]); + in[line[0]]++; + } + Queue zeroInQueue = new LinkedList<>(); + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + if (in[i] == 0) { + zeroInQueue.add(i); + } + } + while (!zeroInQueue.isEmpty()) { + int cur = zeroInQueue.poll(); + ans[cur] += time[cur]; + for (int next : nexts.get(cur)) { + ans[next] = Math.max(ans[next], ans[cur]); + if (--in[next] == 0) { + zeroInQueue.add(next); + } + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_04_2_week/Code05_TowLongestSubarraySame01Number.java b/算法周更班/class_2022_04_2_week/Code05_TowLongestSubarraySame01Number.java new file mode 100644 index 0000000..1941890 --- /dev/null +++ b/算法周更班/class_2022_04_2_week/Code05_TowLongestSubarraySame01Number.java @@ -0,0 +1,95 @@ +package class_2022_04_2_week; + +import java.util.HashMap; + +// 来自百度 +// 给出一个长度为n的01串,现在请你找到两个区间, +// 使得这两个区间中,1的个数相等,0的个数也相等 +// 这两个区间可以相交,但是不可以完全重叠,即两个区间的左右端点不可以完全一样 +// 现在请你找到两个最长的区间,满足以上要求。 +public class Code05_TowLongestSubarraySame01Number { + + public static int longest1(int[] arr) { + HashMap> map = new HashMap<>(); + for (int i = 0; i < arr.length; i++) { + int zero = 0; + int one = 0; + for (int j = i; j < arr.length; j++) { + zero += arr[j] == 0 ? 1 : 0; + one += arr[j] == 1 ? 1 : 0; + map.putIfAbsent(zero, new HashMap<>()); + map.get(zero).put(one, map.get(zero).getOrDefault(one, 0) + 1); + } + } + int ans = 0; + for (int zeros : map.keySet()) { + for (int ones : map.get(zeros).keySet()) { + int num = map.get(zeros).get(ones); + if (num > 1) { + ans = Math.max(ans, zeros + ones); + } + } + } + return ans; + } + + public static int longest2(int[] arr) { + int leftZero = -1; + int rightZero = -1; + int leftOne = -1; + int rightOne = -1; + for (int i = 0; i < arr.length; i++) { + if (arr[i] == 0) { + leftZero = i; + break; + } + } + for (int i = 0; i < arr.length; i++) { + if (arr[i] == 1) { + leftOne = i; + break; + } + } + for (int i = arr.length - 1; i >= 0; i--) { + if (arr[i] == 0) { + rightZero = i; + break; + } + } + for (int i = arr.length - 1; i >= 0; i--) { + if (arr[i] == 1) { + rightOne = i; + break; + } + } + int p1 = rightZero - leftZero; + int p2 = rightOne - leftOne; + return Math.max(p1, p2); + } + + public static int[] randomArray(int len) { + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * 2); + } + return ans; + } + + public static void main(String[] args) { + int n = 500; + int testTime = 200; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * n) + 2; + int[] arr = randomArray(size); + int ans1 = longest1(arr); + int ans2 = longest2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_2_week/Code06_PerfectPairNumber.java b/算法周更班/class_2022_04_2_week/Code06_PerfectPairNumber.java new file mode 100644 index 0000000..32d3212 --- /dev/null +++ b/算法周更班/class_2022_04_2_week/Code06_PerfectPairNumber.java @@ -0,0 +1,53 @@ +package class_2022_04_2_week; + +// 来自阿里 +// x = { a, b, c, d } +// y = { e, f, g, h } +// x、y两个小数组长度都是4 +// 如果有: a + e = b + f = c + g = d + h +// 那么说x和y是一个完美对 +// 题目给定N个小数组,每个小数组长度都是K +// 返回这N个小数组中,有多少完美对 +// 本题测试链接 : https://www.nowcoder.com/practice/f5a3b5ab02ed4202a8b54dfb76ad035e +// 提交如下代码,把主类名改成Main +// 可以直接通过 +import java.util.HashMap; +import java.util.Scanner; + +public class Code06_PerfectPairNumber { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + int n = sc.nextInt(); + int m = sc.nextInt(); + int[][] matrix = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + matrix[i][j] = sc.nextInt(); + } + } + long ans = perfectPairs(matrix); + System.out.println(ans); + } + sc.close(); + } + + public static long perfectPairs(int[][] matrix) { + long ans = 0; + // key : 字符串 特征,差值特征 : "_5_-2_6_9" + HashMap counts = new HashMap<>(); + for (int[] arr : matrix) { + StringBuilder self = new StringBuilder(); + StringBuilder minus = new StringBuilder(); + for (int i = 1; i < arr.length; i++) { + self.append("_" + (arr[i] - arr[i - 1])); + minus.append("_" + (arr[i - 1] - arr[i])); + } + ans += counts.getOrDefault(minus.toString(), 0); + counts.put(self.toString(), counts.getOrDefault(self.toString(), 0) + 1); + } + return ans; + } + +} \ No newline at end of file diff --git a/算法周更班/class_2022_04_2_week/Code07_MaxMoneyMostMin.java b/算法周更班/class_2022_04_2_week/Code07_MaxMoneyMostMin.java new file mode 100644 index 0000000..61b6ed6 --- /dev/null +++ b/算法周更班/class_2022_04_2_week/Code07_MaxMoneyMostMin.java @@ -0,0 +1,236 @@ +package class_2022_04_2_week; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; + +// 来自快手 +// 某公司年会上,大家要玩一食发奖金游戏,一共有n个员工, +// 每个员工都有建设积分和捣乱积分 +// 他们需要排成一队,在队伍最前面的一定是老板,老板也有建设积分和捣乱积分 +// 排好队后,所有员工都会获得各自的奖金, +// 该员工奖金 = 排在他前面所有人的建设积分乘积 / 该员工自己的捣乱积分,向下取整 +// 为了公平(放屁),老板希望 : 让获得奖金最高的员工,所获得的奖金尽可能少 +// 所以想请你帮他重新排一下队伍,返回奖金最高的员工获得的、尽可能少的奖金数额 +// 快手考试的时候,给定的数据量,全排列的代码也能过的! +// 1 <= n <= 1000, 1<= 积分 <= 10000; +public class Code07_MaxMoneyMostMin { + + // 暴力方法 + // 为了验证 + // a : 老板的贡献积分 + // b : 老板的捣乱积分 + // value[i] : i号员工的贡献积分 + // trouble[i] : i号员工的捣乱积分 + // 返回 : 奖金最高的员工获得的、尽可能少的奖金数额 + public static long mostMin1(int a, int b, int[] value, int[] trouble) { + return process1(a, value, trouble, 0); + } + + public static long process1(int boss, int[] value, int[] trouble, int index) { + if (index == value.length) { + long valueAll = boss; + long ans = 0; + for (int i = 0; i < value.length; i++) { + ans = Math.max(ans, valueAll / trouble[i]); + valueAll *= value[i]; + } + return ans; + } else { + long ans = Long.MAX_VALUE; + for (int i = index; i < value.length; i++) { + swap(value, trouble, i, index); + ans = Math.min(ans, process1(boss, value, trouble, index + 1)); + swap(value, trouble, i, index); + } + return ans; + } + } + + public static void swap(int[] value, int[] trouble, int i, int j) { + int tmp = value[i]; + value[i] = value[j]; + value[j] = tmp; + tmp = trouble[i]; + trouble[i] = trouble[j]; + trouble[j] = tmp; + } + + // 正式方法 + // 所有员工数量为N + // 假设所有员工建设积分乘起来为M + // 时间复杂度O(N * logN * logM) + public static long mostMin2(int a, int b, int[] value, int[] trouble) { + int n = value.length; + long l = 0; + long r = 0; + long valueAll = a; + long[][] staff = new long[n][2]; + for (int i = 0; i < n; i++) { + r = Math.max(r, valueAll / trouble[i]); + valueAll *= value[i]; + staff[i][0] = (long) value[i] * (long) trouble[i]; + staff[i][1] = value[i]; + } + Arrays.sort(staff, (x, y) -> (y[0] >= x[0] ? 1 : -1)); + long m = 0; + long ans = 0; + while (l <= r) { + m = l + ((r - l) >> 1); + if (yeah(valueAll, staff, m)) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + // staff长度为N,时间复杂度O(N * logN) + public static boolean yeah(long all, long[][] staff, long limit) { + int n = staff.length; + SegmentTree st = new SegmentTree(n); + HashMap> map = new HashMap<>(); + for (int i = 0, index = 1; i < n; i++, index++) { + int value = (int) staff[i][1]; + st.update(index, value); + if (!map.containsKey(value)) { + map.put(value, new LinkedList<>()); + } + map.get(value).addLast(index); + } + for (int k = 0; k < n; k++) { + int right = boundary(staff, all, limit); + if (right == 0) { + return false; + } + int max = st.max(right); + if (max == 0) { + return false; + } + int index = map.get(max).pollFirst(); + st.update(index, 0); + all /= max; + } + return true; + } + + public static int boundary(long[][] staff, long all, long limit) { + int l = 0; + int r = staff.length - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = l + ((r - l) >> 1); + if (all / staff[m][0] <= limit) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans + 1; + } + + public static class SegmentTree { + private int n; + private int[] max; + private int[] update; + + public SegmentTree(int maxSize) { + n = maxSize + 1; + max = new int[n << 2]; + update = new int[n << 2]; + Arrays.fill(update, -1); + } + + public void update(int index, int c) { + update(index, index, c, 1, n, 1); + } + + public int max(int right) { + return max(1, right, 1, n, 1); + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + private void pushDown(int rt, int ln, int rn) { + if (update[rt] != -1) { + update[rt << 1] = update[rt]; + max[rt << 1] = update[rt]; + update[rt << 1 | 1] = update[rt]; + max[rt << 1 | 1] = update[rt]; + update[rt] = -1; + } + } + + private void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + max[rt] = C; + update[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + private int max(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return max[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans = Math.max(ans, max(L, R, l, mid, rt << 1)); + } + if (R > mid) { + ans = Math.max(ans, max(L, R, mid + 1, r, rt << 1 | 1)); + } + return ans; + } + + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int n = 9; + int v = 50; + int testTime = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int a = (int) (Math.random() * v) + 1; + int b = (int) (Math.random() * v) + 1; + int len = (int) (Math.random() * n); + int[] value = randomArray(len, v); + int[] trouble = randomArray(len, v); + long ans1 = mostMin1(a, b, value, trouble); + long ans2 = mostMin2(a, b, value, trouble); + if (ans1 != ans2) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_3_week/Code01_MaxOneNumbers.java b/算法周更班/class_2022_04_3_week/Code01_MaxOneNumbers.java new file mode 100644 index 0000000..6348b83 --- /dev/null +++ b/算法周更班/class_2022_04_3_week/Code01_MaxOneNumbers.java @@ -0,0 +1,79 @@ +package class_2022_04_3_week; + +// 小红书 +// 3.13 笔试 +// 数组里有0和1,一定要翻转一个区间,翻转:0变1,1变0 +// 请问翻转后可以使得1的个数最多是多少? +public class Code01_MaxOneNumbers { + + public static int maxOneNumbers1(int[] arr) { + int ans = 0; + for (int l = 0; l < arr.length; l++) { + for (int r = l; r < arr.length; r++) { + reverse(arr, l, r); + ans = Math.max(ans, oneNumbers(arr)); + reverse(arr, l, r); + } + } + return ans; + } + + public static void reverse(int[] arr, int l, int r) { + for (int i = l; i <= r; i++) { + arr[i] ^= 1; + } + } + + public static int oneNumbers(int[] arr) { + int ans = 0; + for (int num : arr) { + ans += num; + } + return ans; + } + + public static int maxOneNumbers2(int[] arr) { + int ans = 0; + for (int num : arr) { + ans += num; + } + for (int i = 0; i < arr.length; i++) { + arr[i] = arr[i] == 0 ? 1 : -1; + } + int max = Integer.MIN_VALUE; + int cur = 0; + for (int i = 0; i < arr.length; i++) { + cur += arr[i]; + max = Math.max(max, cur); + cur = cur < 0 ? 0 : cur; + } + return ans + max; + } + + // 为了测试 + public static int[] randomArray(int n) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * 2); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int N = 100; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr = randomArray(n); + int ans1 = maxOneNumbers1(arr); + int ans2 = maxOneNumbers2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_3_week/Code02_RMQ.java b/算法周更班/class_2022_04_3_week/Code02_RMQ.java new file mode 100644 index 0000000..e6d5c5c --- /dev/null +++ b/算法周更班/class_2022_04_3_week/Code02_RMQ.java @@ -0,0 +1,118 @@ +package class_2022_04_3_week; + +// 小红书 +// 3.13 笔试 +// 给定一个数组,想随时查询任何范围上的最大值 +// 如果只是根据初始数组建立、并且以后没有修改, +// 那么RMQ方法比线段树方法好实现,时间复杂度O(N*logN),额外空间复杂度O(N*logN) +public class Code02_RMQ { + + public static class RMQ { + public int[][] max; + + // 下标一定要从1开始,没有道理!就是约定俗成! + public RMQ(int[] arr) { + // 长度! + int n = arr.length; + // 2的几次方,可以拿下n + int k = power2(n); + // n*logn + max = new int[n + 1][k + 1]; + for (int i = 1; i <= n; i++) { + // i 0:从下标i开始,往下连续的2的0次方个数,中,最大值 + // 1...1个 + // 2...1个 + // 3...1个 + max[i][0] = arr[i - 1]; + } + for (int j = 1; (1 << j) <= n; j++) { + // i...连续的、2的1次方个数,这个范围,最大值 + // i...连续的、2的2次方个数,这个范围,最大值 + // i...连续的、2的3次方个数,这个范围,最大值 + for (int i = 1; i + (1 << j) - 1 <= n; i++) { + // max[10][3] + // 下标10开始,连续的8个数,最大值是多少 + // 1) max[10][2] + // 2) max[14][2] + max[i][j] = Math.max( + max[i][j - 1], + max[i + (1 << (j - 1))][j - 1]); + } + } + } + + public int max(int l, int r) { + // l...r -> r - l + 1 -> 2的哪个次方最接近它! + int k = power2(r - l + 1); + return Math.max(max[l][k], max[r - (1 << k) + 1][k]); + } + + private int power2(int m) { + int ans = 0; + while ((1 << ans) <= (m >> 1)) { + ans++; + } + return ans; + } + + } + + // 为了测试 + public static class Right { + public int[][] max; + + public Right(int[] arr) { + int n = arr.length; + max = new int[n + 1][n + 1]; + for (int l = 1; l <= n; l++) { + max[l][l] = arr[l - 1]; + for (int r = l + 1; r <= n; r++) { + max[l][r] = Math.max(max[l][r - 1], arr[r - 1]); + } + } + } + + public int max(int l, int r) { + return max[l][r]; + } + + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int N = 150; + int V = 200; + int testTimeOut = 20000; + int testTimeIn = 200; + System.out.println("测试开始"); + for (int i = 0; i < testTimeOut; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr = randomArray(n, V); + int m = arr.length; + RMQ rmq = new RMQ(arr); + Right right = new Right(arr); + for (int j = 0; j < testTimeIn; j++) { + int a = (int) (Math.random() * m) + 1; + int b = (int) (Math.random() * m) + 1; + int l = Math.min(a, b); + int r = Math.max(a, b); + int ans1 = rmq.max(l, r); + int ans2 = right.max(l, r); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_3_week/Code03_ValidSortedArrayWays.java b/算法周更班/class_2022_04_3_week/Code03_ValidSortedArrayWays.java new file mode 100644 index 0000000..309219f --- /dev/null +++ b/算法周更班/class_2022_04_3_week/Code03_ValidSortedArrayWays.java @@ -0,0 +1,125 @@ +package class_2022_04_3_week; + +import java.util.Arrays; + +// 来自腾讯音乐 +// 原本数组中都是大于0、小于等于k的数字,是一个单调不减的数组 +// 其中可能有相等的数字,总体趋势是递增的 +// 但是其中有些位置的数被替换成了0,我们需要求出所有的把0替换的方案数量: +// 1)填充的每一个数可以大于等于前一个数,小于等于后一个数 +// 2)填充的每一个数不能大于k +public class Code03_ValidSortedArrayWays { + + // 动态规划 + public static long ways1(int[] nums, int k) { + int n = nums.length; + // dp[i][j] : 一共i个格子,随意填,但是不能降序,j种数可以选 + long[][] dp = new long[n + 1][k + 1]; + for (int i = 1; i <= n; i++) { + dp[i][1] = 1; + } + for (int i = 1; i <= k; i++) { + dp[1][i] = i; + } + for (int i = 2; i <= n; i++) { + for (int j = 2; j <= k; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + long res = 1; + for (int i = 0, j = 0; i < nums.length; i++) { + if (nums[i] == 0) { + j = i + 1; + while (j < nums.length && nums[j] == 0) { + j++; + } + int leftValue = i - 1 >= 0 ? nums[i - 1] : 1; + int rightValue = j < nums.length ? nums[j] : k; + res *= dp[j - i][rightValue - leftValue + 1]; + i = j; + } + } + return res; + } + + // 数学方法 + // a ~ b范围的数字随便选,可以选重复的数,一共选m个 + // 选出有序序列的方案数:C ( m, b - a + m ) + public static long ways2(int[] nums, int k) { + long res = 1; + for (int i = 0, j = 0; i < nums.length; i++) { + if (nums[i] == 0) { + j = i + 1; + while (j < nums.length && nums[j] == 0) { + j++; + } + int leftValue = i - 1 >= 0 ? nums[i - 1] : 1; + int rightValue = j < nums.length ? nums[j] : k; + int numbers = j - i; + res *= c(rightValue - leftValue + numbers, numbers); + i = j; + } + } + return res; + } + + // 从一共a个数里,选b个数,方法数是多少 + public static long c(int a, int b) { + if (a == b) { + return 1; + } + long x = 1; + long y = 1; + for (int i = b + 1, j = 1; i <= a; i++, j++) { + x *= i; + y *= j; + long gcd = gcd(x, y); + x /= gcd; + y /= gcd; + } + return x / y; + } + + public static long gcd(long m, long n) { + return n == 0 ? m : gcd(n, m % n); + } + + // 为了测试 + public static int[] randomArray(int n, int k) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * k) + 1; + } + Arrays.sort(ans); + for (int i = 0; i < n; i++) { + ans[i] = Math.random() < 0.5 ? 0 : ans[i]; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 20; + int K = 30; + int testTimes = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int k = (int) (Math.random() * K) + 1; + int[] arr = randomArray(n, k); + long ans1 = ways1(arr, k); + long ans2 = ways2(arr, k); + if (ans1 != ans2) { + System.out.println("出错了"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_3_week/Code04_SumEvenSubNumber.java b/算法周更班/class_2022_04_3_week/Code04_SumEvenSubNumber.java new file mode 100644 index 0000000..b30658b --- /dev/null +++ b/算法周更班/class_2022_04_3_week/Code04_SumEvenSubNumber.java @@ -0,0 +1,84 @@ +package class_2022_04_3_week; + +// 来自学员问题 +// 总长度为n的数组中,所有长度为k的子序列里,有多少子序列的和为偶数 +public class Code04_SumEvenSubNumber { + + public static int number1(int[] arr, int k) { + if (arr == null || arr.length == 0 || k < 1 || k > arr.length) { + return 0; + } + return process1(arr, 0, k, 0); + } + + public static int process1(int[] arr, int index, int rest, int sum) { + if (index == arr.length) { + return rest == 0 && (sum & 1) == 0 ? 1 : 0; + } else { + return process1(arr, index + 1, rest, sum) + process1(arr, index + 1, rest - 1, sum + arr[index]); + } + } + + public static int number2(int[] arr, int k) { + if (arr == null || arr.length == 0 || k < 1 || k > arr.length) { + return 0; + } + int n = arr.length; + // even[i][j] : 在前i个数的范围上(0...i-1),一定选j个数,加起来是偶数的子序列个数 + // odd[i][j] : 在前i个数的范围上(0...i-1),一定选j个数,加起来是奇数的子序列个数 + int[][] even = new int[n + 1][k + 1]; + int[][] odd = new int[n + 1][k + 1]; + for (int i = 0; i <= n; i++) { + // even[0][0] = 1; + // even[1][0] = 1; + // even[2][0] = 1; + // even[n][0] = 1; + even[i][0] = 1; + } + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= Math.min(i, k); j++) { + even[i][j] = even[i - 1][j]; + odd[i][j] = odd[i - 1][j]; + even[i][j] += (arr[i - 1] & 1) == 0 ? even[i - 1][j - 1] : odd[i - 1][j - 1]; + odd[i][j] += (arr[i - 1] & 1) == 0 ? odd[i - 1][j - 1] : even[i - 1][j - 1]; + } + } + return even[n][k]; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * v); + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 20; + int V = 30; + int testTimes = 3000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr = randomArray(n, V); + int k = (int) (Math.random() * n) + 1; + int ans1 = number1(arr, k); + int ans2 = number2(arr, k); + if (ans1 != ans2) { + System.out.println("出错了"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(k); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_04_3_week/Code05_ModKSubstringNumbers.java b/算法周更班/class_2022_04_3_week/Code05_ModKSubstringNumbers.java new file mode 100644 index 0000000..c69cec2 --- /dev/null +++ b/算法周更班/class_2022_04_3_week/Code05_ModKSubstringNumbers.java @@ -0,0 +1,83 @@ +package class_2022_04_3_week; + +// 来自微众 +// 4.11笔试 +// 给定n位长的数字字符串和正数k,求该子符串能被k整除的子串个数 +// (n<=1000,k<=100) +public class Code05_ModKSubstringNumbers { + + // 暴力方法 + // 为了验证 + public static int modWays1(String s, int k) { + int n = s.length(); + int ans = 0; + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + if (Long.valueOf(s.substring(i, j + 1)) % k == 0) { + ans++; + } + } + } + return ans; + } + + // 正式方法 + // 时间复杂度O(N * k) + public static int modWays2(String s, int k) { + int[] cur = new int[k]; + // 帮忙迁移 + int[] next = new int[k]; + // 0...i 整体余几? + int mod = 0; + // 答案:统计有多少子串的值%k == 0 + int ans = 0; + for (char cha : s.toCharArray()) { + for (int i = 0; i < k; i++) { + // i -> 10个 + // (i * 10) % k + next[(i * 10) % k] += cur[i]; + cur[i] = 0; + } + int[] tmp = cur; + cur = next; + next = tmp; + mod = (mod * 10 + (cha - '0')) % k; + ans += (mod == 0 ? 1 : 0) + cur[mod]; + cur[mod]++; + } + return ans; + } + + // 为了测试 + public static String randomNumber(int n) { + char[] ans = new char[n]; + for (int i = 0; i < n; i++) { + ans[i] = (char) ((int) (Math.random() * 10) + '0'); + } + return String.valueOf(ans); + } + + // 为了测试 + public static void main(String[] args) { + int N = 18; + int K = 20; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + String str = randomNumber((int) (Math.random() * N) + 1); + int k = (int) (Math.random() * K) + 1; + int ans1 = modWays1(str, k); + int ans2 = modWays2(str, k); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(str); + System.out.println(k); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_1_week/Code01_JumMinSameValue.java b/算法周更班/class_2022_05_1_week/Code01_JumMinSameValue.java new file mode 100644 index 0000000..8274de1 --- /dev/null +++ b/算法周更班/class_2022_05_1_week/Code01_JumMinSameValue.java @@ -0,0 +1,79 @@ +package class_2022_05_1_week; + +import java.util.ArrayList; +import java.util.HashMap; + +// 来自蔚来汽车 +// 给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。 +// 每一步,你可以从下标 i 跳到下标 i + 1 、i - 1 或者 j : +// i + 1 需满足:i + 1 < arr.length +// i - 1 需满足:i - 1 >= 0 +// j 需满足:arr[i] == arr[j] 且 i != j +// 请你返回到达数组最后一个元素的下标处所需的 最少操作次数 。 +// 注意:任何时候你都不能跳到数组外面。 +// leetcode测试链接 : https://leetcode-cn.com/problems/jump-game-iv/ +public class Code01_JumMinSameValue { + + public static int minJumps(int[] arr) { + int n = arr.length; + // 为了找某个值,有哪些位置,能快一些 + // key : 某个值9, + // value : 列表:0,7,19 + HashMap> valueIndex = new HashMap<>(); + for (int i = 0; i < n; i++) { + if (!valueIndex.containsKey(arr[i])) { + valueIndex.put(arr[i], new ArrayList<>()); + } + valueIndex.get(arr[i]).add(i); + } + // i会有哪些展开:左,右,i通过自己的值,能蹦到哪些位置上去 + // 宽度优先遍历,遍历过的位置,不希望重复处理 + // visited[i] == false:i位置,之前没来过,可以处理 + // visited[i] == true : i位置,之前来过,可以跳过 + boolean[] visited = new boolean[n]; + int[] queue = new int[n]; + int l = 0; + int r = 0; + // 0位置加到队列里去 + queue[r++] = 0; + visited[0] = true; + int jump = 0; + // 宽度优先遍历 + // 一次,遍历一整层! + // 该技巧,多次出现! + while (l != r) { // 队列里还有东西的意思! + // 此时的r记录! + // 0 1 2 | 3 4 5 6 7 8 + // 当前层的终止位置 + int tmp = r; + for (; l < tmp; l++) { // 遍历当前层! + int cur = queue[l]; + if (cur == n - 1) { + return jump; + } + if (cur + 1 < n && !visited[cur + 1]) { + visited[cur + 1] = true; + queue[r++] = cur + 1; + } + // cur > 0 cur - 1 >=0 + if (cur > 0 && !visited[cur - 1]) { + visited[cur - 1] = true; + queue[r++] = cur - 1; + } + // i -> 9 + // 值同样为9的那些位置,也能去 + for (int next : valueIndex.get(arr[cur])) { + if (!visited[next]) { + visited[next] = true; + queue[r++] = next; + } + } + // 重要优化! + valueIndex.get(arr[cur]).clear(); + } + jump++; + } + return -1; + } + +} diff --git a/算法周更班/class_2022_05_1_week/Code02_WhoWin21Balls.java b/算法周更班/class_2022_05_1_week/Code02_WhoWin21Balls.java new file mode 100644 index 0000000..6be690a --- /dev/null +++ b/算法周更班/class_2022_05_1_week/Code02_WhoWin21Balls.java @@ -0,0 +1,160 @@ +package class_2022_05_1_week; + +// 来自微众 +// 人工智能岗 +// 一开始有21个球,甲和乙轮流拿球,甲先、乙后 +// 每个人在自己的回合,一定要拿不超过3个球,不能不拿 +// 最终谁的总球数为偶数,谁赢 +// 请问谁有必胜策略 +public class Code02_WhoWin21Balls { + + // balls = 21 + // ball是奇数 + public static String win(int balls) { + return process(0, balls, 0, 0); + } + + // 憋递归! + // turn 谁的回合! + // turn == 0 甲回合 + // turn == 1 乙回合 + // rest剩余球的数量 + // 之前,jiaBalls、yiBalls告诉你! + // 当前,根据turn,知道是谁的回合! + // 当前,还剩多少球,rest + // 返回:谁会赢! + public static String process(int turn, int rest, int jia, int yi) { + if (rest == 0) { + return (jia & 1) == 0 ? "甲" : "乙"; + } + // rest > 0, 还剩下球! + if (turn == 0) { // 甲的回合! + // 甲,自己赢!甲赢! + for (int pick = 1; pick <= Math.min(rest, 3); pick++) { + // pick 甲当前做的选择 + if (process(1, rest - pick, jia + pick, yi).equals("甲")) { + return "甲"; + } + } + return "乙"; + } else { + for (int pick = 1; pick <= Math.min(rest, 3); pick++) { + // pick 甲当前做的选择 + if (process(0, rest - pick, jia, yi + pick).equals("乙")) { + return "乙"; + } + } + return "甲"; + } + } + + // 我们补充一下设定,假设一开始的球数量不是21,是任意的正数 + // 如果最终两个人拿的都是偶数,认为无人获胜,平局 + // 如果最终两个人拿的都是奇数,认为无人获胜,平局 + // rest代表目前剩下多少球 + // cur == 0 代表目前是甲行动 + // cur == 1 代表目前是乙行动 + // first == 0 代表目前甲所选的球数,加起来是偶数 + // first == 1 代表目前甲所选的球数,加起来是奇数 + // second == 0 代表目前乙所选的球数,加起来是偶数 + // second == 1 代表目前乙所选的球数,加起来是奇数 + // 返回选完了rest个球,谁会赢,只会返回"甲"、"乙"、"平" + // win1方法,就是彻底暴力的做所有尝试,并且返回最终的胜利者 + // 在甲的回合,甲会尝试所有的可能,以保证自己会赢,如果自己怎么都不会赢,那也要尽量平局,如果这个也不行,只能对方赢 + // 在乙的回合,乙会尝试所有的可能,以保证自己会赢,如果自己怎么都不会赢,那也要尽量平局,如果这个也不行,只能对方赢 + // 算法和数据结构体系学习班,视频39章节,牛羊吃草问题,就是类似这种递归 + public static String win1(int rest, int cur, int first, int second) { + if (rest == 0) { + if (first == 0 && second == 1) { + return "甲"; + } + if (first == 1 && second == 0) { + return "乙"; + } + return "平"; + } + if (cur == 0) { // 甲行动 + String bestAns = "乙"; + for (int pick = 1; pick <= Math.min(3, rest); pick++) { + String curAns = win1(rest - pick, 1, first ^ (pick & 1), second); + if (curAns.equals("甲")) { + bestAns = "甲"; + break; + } + if (curAns.equals("平")) { + bestAns = "平"; + } + } + return bestAns; + } else { // 乙行动 + String bestAns = "甲"; + for (int pick = 1; pick <= Math.min(3, rest); pick++) { + String curAns = win1(rest - pick, 0, first, second ^ (pick & 1)); + if (curAns.equals("乙")) { + bestAns = "乙"; + break; + } + if (curAns.equals("平")) { + bestAns = "平"; + } + } + return bestAns; + } + } + + // 下面的win2方法,仅仅是把win1方法,做了记忆化搜索 + // 变成了动态规划 + public static String[][][][] dp = new String[5000][2][2][2]; + + public static String win2(int rest, int cur, int first, int second) { + if (rest == 0) { + if (first == 0 && second == 1) { + return "甲"; + } + if (first == 1 && second == 0) { + return "乙"; + } + return "平"; + } + if (dp[rest][cur][first][second] != null) { + return dp[rest][cur][first][second]; + } + if (cur == 0) { // 甲行动 + String bestAns = "乙"; + for (int pick = 1; pick <= Math.min(3, rest); pick++) { + String curAns = win2(rest - pick, 1, first ^ (pick & 1), second); + if (curAns.equals("甲")) { + bestAns = "甲"; + break; + } + if (curAns.equals("平")) { + bestAns = "平"; + } + } + dp[rest][cur][first][second] = bestAns; + return bestAns; + } else { // 乙行动 + String bestAns = "甲"; + for (int pick = 1; pick <= Math.min(3, rest); pick++) { + String curAns = win2(rest - pick, 0, first, second ^ (pick & 1)); + if (curAns.equals("乙")) { + bestAns = "乙"; + break; + } + if (curAns.equals("平")) { + bestAns = "平"; + } + } + dp[rest][cur][first][second] = bestAns; + return bestAns; + } + } + + // 为了测试 + public static void main(String[] args) { + for (int balls = 1; balls <= 500; balls += 2) { + System.out.println("球数为 " + balls + " 时 , 赢的是 " + win(balls)); + } + } + +} \ No newline at end of file diff --git a/算法周更班/class_2022_05_1_week/Code03_FindDuplicateOnlyOne.java b/算法周更班/class_2022_05_1_week/Code03_FindDuplicateOnlyOne.java new file mode 100644 index 0000000..ecbc931 --- /dev/null +++ b/算法周更班/class_2022_05_1_week/Code03_FindDuplicateOnlyOne.java @@ -0,0 +1,114 @@ +package class_2022_05_1_week; + +import java.util.Arrays; +import java.util.HashSet; + +// 来自学员问题,真实大厂面试题 +// 1、2、3...n-1、n、n、n+1、n+2... +// 在这个序列中,只有一个数字有重复(n) +// 这个序列是无序的,找到重复数字n +// 这个序列是有序的,找到重复数字n +public class Code03_FindDuplicateOnlyOne { + + // 为了测试 + // 绝对正确,但是直接遍历+哈希表,没有得分的方法 + public static int right(int[] arr) { + HashSet set = new HashSet<>(); + for (int num : arr) { + if (set.contains(num)) { + return num; + } + set.add(num); + } + return -1; + } + + // 符合题目要求的、无序数组,找重复数 + // 时间复杂度O(N),额外空间复杂度O(1) + public static int findDuplicate(int[] arr) { + if (arr == null || arr.length < 2) { + return -1; + } + int slow = arr[0]; + int fast = arr[arr[0]]; + while (slow != fast) { + slow = arr[slow]; + fast = arr[arr[fast]]; + } + // slow == fast + fast = 0; + while (slow != fast) { + fast = arr[fast]; + slow = arr[slow]; + } + // 再相遇!一个结论 + return slow; + } + + // 符合题目要求的、有序数组,找重复数 + // 时间复杂度O(logN),额外空间复杂度O(1) + public static int findDuplicateSorted(int[] arr) { + if (arr == null || arr.length < 2) { + return -1; + } + int l = 0; + int r = arr.length - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = (l + r) / 2; + if ((m - 1 >= 0 && arr[m - 1] == arr[m]) || (m + 1 < arr.length && arr[m + 1] == arr[m])) { + ans = arr[m]; + break; + } + if (m - l == arr[m] - arr[l]) { + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int n) { + int[] ans = new int[n + 1]; + for (int i = 0; i < n; i++) { + ans[i] = i + 1; + } + ans[n] = (int) (Math.random() * n) + 1; + for (int i = n; i > 0; i--) { + int j = (int) (Math.random() * (i + 1)); + int tmp = ans[i]; + ans[i] = ans[j]; + ans[j] = tmp; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 10; + int testTime = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray((int) (Math.random() * N) + 1); + if (right(arr) != findDuplicate(arr)) { + System.out.println("未排序情况出错!"); + } + Arrays.sort(arr); + if (right(arr) != findDuplicateSorted(arr)) { + System.out.println("排序情况出错!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(right(arr)); + System.out.println(findDuplicateSorted(arr)); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_1_week/Code04_SumOfQuadraticSum.java b/算法周更班/class_2022_05_1_week/Code04_SumOfQuadraticSum.java new file mode 100644 index 0000000..0af7de4 --- /dev/null +++ b/算法周更班/class_2022_05_1_week/Code04_SumOfQuadraticSum.java @@ -0,0 +1,113 @@ +package class_2022_05_1_week; + +// 来自学员问题,蓝桥杯练习题 +// f(i) : i的所有因子,每个因子都平方之后,累加起来 +// 比如f(10) = 1平方 + 2平方 + 5平方 + 10平方 = 1 + 4 + 25 + 100 = 130 +// 给定一个数n,求f(1) + f(2) + .. + f(n) +// n <= 10的9次方 +// O(n)的方法都会超时!低于它的! +// O(根号N)的方法,就过了,一个思路 +// O(log N)的方法, +public class Code04_SumOfQuadraticSum { + + // 暴力方法 + public static long sum1(long n) { + int[] cnt = new int[(int) n + 1]; + for (int num = 1; num <= n; num++) { + for (int j = 1; j <= num; j++) { + if (num % j == 0) { + cnt[j]++; + } + } + } + long ans = 0; + for (long i = 1; i <= n; i++) { + ans += i * i * (long) (cnt[(int) i]); + } + return ans; + } + + // 正式方法 + // 时间复杂度O(开平方根N + 开平方根N * logN) + public static long sum2(long n) { + // 100 -> 10 + // 200 -> 14 + long sqrt = (long) Math.pow((double) n, 0.5); + long ans = 0; + for (long i = 1; i <= sqrt; i++) { + ans += i * i * (n / i); + } + // 后半段 + // 给你一个个数,二分出几个因子,处在这个个数上! + // 由最大个数(根号N), 开始二分 + for (long k = n / (sqrt + 1); k >= 1; k--) { + ans += sumOfLimitNumber(n, k); + } + return ans; + } + + // 平方和公式n(n+1)(2n+1)/6 + public static long sumOfLimitNumber(long v, long n) { + long r = cover(v, n); + long l = cover(v, n + 1); + return ((r * (r + 1) * ((r << 1) + 1) + - l * (l + 1) * ((l << 1) + 1)) * n) + / 6; + } + + public static long cover(long v, long n) { + long l = 1; + long r = v; + long m = 0; + long ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (m * n <= v) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + // 实验 + // 解法来自观察 + // 打表(暴力) + // f(1) + ... + f(n) + public static void test(int n) { + int[] cnt = new int[n + 1]; + for (int num = 1; num <= n; num++) { + for (int j = 1; j <= num; j++) { + if (num % j == 0) { + cnt[j]++; + } + } + } + for (int i = 1; i <= n; i++) { + System.out.println("因子 : " + i + ", 个数 : " + cnt[i]); + } + } + + public static void main(String[] args) { + +// test(100); + + System.out.println("测试开始"); + for (long i = 1; i < 1000; i++) { + if (sum1(i) != sum2(i)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + + long n = 50000000000L; // 5 * 10的10次方 + long start = System.currentTimeMillis(); + sum2(n); + long end = System.currentTimeMillis(); + System.out.println("大样本测试,n = " + n); + System.out.println("运行时间 : " + (end - start) + " ms"); + } + +} diff --git a/算法周更班/class_2022_05_1_week/Code05_PalindromeStringNoLessKLenNoOverlapingMaxParts.java b/算法周更班/class_2022_05_1_week/Code05_PalindromeStringNoLessKLenNoOverlapingMaxParts.java new file mode 100644 index 0000000..31f67a2 --- /dev/null +++ b/算法周更班/class_2022_05_1_week/Code05_PalindromeStringNoLessKLenNoOverlapingMaxParts.java @@ -0,0 +1,131 @@ +package class_2022_05_1_week; + +// 来自optiver +// 给定一个字符串str,和一个正数k +// 你可以随意的划分str成多个子串, +// 目的是找到在某一种划分方案中,有尽可能多的回文子串,长度>=k,并且没有重合 +// 返回有几个回文子串 +public class Code05_PalindromeStringNoLessKLenNoOverlapingMaxParts { + + // 暴力尝试 + // 为了测试 + // 可以改成动态规划,但不是最优解 + public static int max1(String s, int k) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + return process1(str, 0, k); + } + + public static int process1(char[] str, int index, int k) { + if (str.length - index < k) { + return 0; + } + int ans = process1(str, index + 1, k); + for (int i = index + k - 1; i < str.length; i++) { + if (isPalindrome(str, index, i)) { + ans = Math.max(ans, 1 + process1(str, i + 1, k)); + } + } + return ans; + } + + public static boolean isPalindrome(char[] str, int L, int R) { + while (L < R) { + if (str[L++] != str[R--]) { + return false; + } + } + return true; + } + + // 最优解 + // 时间复杂度O(N) + public static int max2(String s, int k) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = manacherString(s); + int[] p = new int[str.length]; + int ans = 0; + int next = 0; + // k == 5 回文串长度要 >= 5 + // next == 0 + // 0.... 8 第一块! + // next -> 9 + // 9.....17 第二块! + // next -> 18 + // 18....23 第三块 + // next一直到最后! + while ((next = manacherFind(str, p, next, k)) != -1) { + next = str[next] == '#' ? next : (next + 1); + ans++; + } + return ans; + } + + public static char[] manacherString(String s) { + char[] str = s.toCharArray(); + char[] ans = new char[s.length() * 2 + 1]; + int index = 0; + for (int i = 0; i != ans.length; i++) { + ans[i] = (i & 1) == 0 ? '#' : str[index++]; + } + return ans; + } + + // s[l...]字符串只在这个范围上,且s[l]一定是'#' + // 从下标l开始,之前都不算,一旦有某个中心回文半径>k,马上返回右边界 + public static int manacherFind(char[] s, int[] p, int l, int k) { + int c = l - 1; + int r = l - 1; + int n = s.length; + for (int i = l; i < s.length; i++) { + p[i] = r > i ? Math.min(p[2 * c - i], r - i) : 1; + while (i + p[i] < n && i - p[i] > l - 1 && s[i + p[i]] == s[i - p[i]]) { + if (++p[i] > k) { + return i + k; + } + } + if (i + p[i] > r) { + r = i + p[i]; + c = i; + } + } + return -1; + } + + // 为了测试 + public static String randomString(int n, int r) { + char[] str = new char[(int) (Math.random() * n)]; + for (int i = 0; i < str.length; i++) { + str[i] = (char) ((int) (Math.random() * r) + 'a'); + } + return String.valueOf(str); + } + + // 为了测试 + public static void main(String[] args) { + int n = 20; + int r = 3; + int testTime = 50000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + String str = randomString(n, r); + int k = (int) (Math.random() * str.length()) + 1; + int ans1 = max1(str, k); + int ans2 = max2(str, k); + if (ans1 != ans2) { + System.out.println(str); + System.out.println(k); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_2_week/Code01_TwoObjectMaxValue.java b/算法周更班/class_2022_05_2_week/Code01_TwoObjectMaxValue.java new file mode 100644 index 0000000..be8c0f8 --- /dev/null +++ b/算法周更班/class_2022_05_2_week/Code01_TwoObjectMaxValue.java @@ -0,0 +1,155 @@ +package class_2022_05_2_week; + +import java.util.Arrays; + +// 来自字节 +// 5.6笔试 +// 给定N件物品,每个物品有重量(w[i])、有价值(v[i]) +// 只能最多选两件商品,重量不超过bag,返回价值最大能是多少? +// N <= 10^5, w[i] <= 10^5, v[i] <= 10^5, bag <= 10^5 +// 本题的关键点:什么数据范围都很大,唯独只需要最多选两件商品,这个可以利用一下 +public class Code01_TwoObjectMaxValue { + + // 暴力方法 + // 为了验证而写的方法 + public static int max1(int[] w, int[] v, int bag) { + return process1(w, v, 0, 2, bag); + } + + public static int process1(int[] w, int[] v, int index, int restNumber, int restWeight) { + if (restNumber < 0 || restWeight < 0) { + return -1; + } + if (index == w.length) { + return 0; + } + int p1 = process1(w, v, index + 1, restNumber, restWeight); + int p2 = -1; + int next = process1(w, v, index + 1, restNumber - 1, restWeight - w[index]); + if (next != -1) { + p2 = v[index] + next; + } + return Math.max(p1, p2); + } + + // 正式方法 + // 时间复杂度O(N * logN) + public static int max2(int[] w, int[] v, int bag) { + int n = w.length; + int[][] arr = new int[n][2]; + for (int i = 0; i < n; i++) { + arr[i][0] = w[i]; + arr[i][1] = v[i]; + } + // O(N * logN) + Arrays.sort(arr, (a, b) -> (a[0] - b[0])); + // 重量从轻到重,依次标号1、2、3、4.... + // 价值依次被构建成了RMQ结构 + // O(N * logN) + RMQ rmq = new RMQ(arr); + int ans = 0; + // N * logN + for (int i = 0, j = 1; i < n && arr[i][0] <= bag; i++, j++) { + // 当前来到0号货物,RMQ结构1号 + // 当前来到i号货物,RMQ结构i+1号 + // 查询重量的边界,重量 边界 <= bag - 当前货物的重量 + // 货物数组中,找到 <= 边界,最右的位置i + // RMQ,位置 i + 1 + int right = right(arr, bag - arr[i][0]) + 1; + int rest = 0; + // j == i + 1,当前的货物,在RMQ里的下标 + if (right == j) { + rest = rmq.max(1, right - 1); + } else if (right < j) { + rest = rmq.max(1, right); + } else { // right > j + rest = Math.max(rmq.max(1, j - 1), rmq.max(j + 1, right)); + } + ans = Math.max(ans, arr[i][1] + rest); + } + return ans; + } + + public static int right(int[][] arr, int limit) { + int l = 0; + int r = arr.length - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = (l + r) / 2; + if (arr[m][0] <= limit) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static class RMQ { + public int[][] max; + + public RMQ(int[][] arr) { + int n = arr.length; + int k = power2(n); + max = new int[n + 1][k + 1]; + for (int i = 1; i <= n; i++) { + max[i][0] = arr[i - 1][1]; + } + for (int j = 1; (1 << j) <= n; j++) { + for (int i = 1; i + (1 << j) - 1 <= n; i++) { + max[i][j] = Math.max(max[i][j - 1], max[i + (1 << (j - 1))][j - 1]); + } + } + } + + public int max(int l, int r) { + if (r < l) { + return 0; + } + int k = power2(r - l + 1); + return Math.max(max[l][k], max[r - (1 << k) + 1][k]); + } + + private int power2(int m) { + int ans = 0; + while ((1 << ans) <= (m >> 1)) { + ans++; + } + return ans; + } + + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int N = 12; + int V = 20; + int testTimes = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int[] w = randomArray(n, V); + int[] v = randomArray(n, V); + int bag = (int) (Math.random() * V * 3); + int ans1 = max1(w, v, bag); + int ans2 = max2(w, v, bag); + if (ans1 != ans2) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_2_week/Code02_ModifyOneNumberModXWays.java b/算法周更班/class_2022_05_2_week/Code02_ModifyOneNumberModXWays.java new file mode 100644 index 0000000..ec32159 --- /dev/null +++ b/算法周更班/class_2022_05_2_week/Code02_ModifyOneNumberModXWays.java @@ -0,0 +1,84 @@ +package class_2022_05_2_week; + +// 来自网易 +// 小红拿到了一个长度为N的数组arr,她准备只进行一次修改 +// 可以将数组中任意一个数arr[i],修改为不大于P的正数(修改后的数必须和原数不同) +// 并使得所有数之和为X的倍数 +// 小红想知道,一共有多少种不同的修改方案 +// 1 <= N, X <= 10^5 +// 1 <= arr[i], P <= 10^9 +public class Code02_ModifyOneNumberModXWays { + + public static int ways1(int[] arr, int p, int x) { + long sum = 0; + for (int num : arr) { + sum += num; + } + int ans = 0; + for (int num : arr) { + sum -= num; + for (int v = 1; v <= p; v++) { + if (v != num) { + if ((sum + v) % x == 0) { + ans++; + } + } + } + sum += num; + } + return ans; + } + + public static int ways2(int[] arr, int p, int x) { + long sum = 0; + for (int num : arr) { + sum += num; + } + int ans = 0; + for (int num : arr) { + ans += cnt(p, x, num, (x - (int) ((sum - num) % x)) % x); + } + return ans; + } + + // 当前数字num + // 1~p以内,不能是num的情况下,% x == mod的数字有几个 + // O(1) + public static int cnt(int p, int x, int num, int mod) { + // p/x 至少有几个 + // (p % x) >= mod ? 1 : 0 + // 在不考虑变出来的数,是不是num的情况下,算一下有几个数,符合要求 + int ans = (p / x) + ((p % x) >= mod ? 1 : 0) - (mod == 0 ? 1 : 0); + // 不能等于num! + return ans - ((num <= p && num % x == mod) ? 1 : 0); + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * v) + 1; + } + return ans; + } + + public static void main(String[] args) { + int len = 100; + int value = 100; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * len) + 1; + int[] arr = randomArray(n, value); + int p = (int) (Math.random() * value) + 1; + int x = (int) (Math.random() * value) + 1; + int ans1 = ways1(arr, p, x); + int ans2 = ways2(arr, p, x); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_2_week/Code03_SortedSubsequenceMaxSum.java b/算法周更班/class_2022_05_2_week/Code03_SortedSubsequenceMaxSum.java new file mode 100644 index 0000000..967b8ef --- /dev/null +++ b/算法周更班/class_2022_05_2_week/Code03_SortedSubsequenceMaxSum.java @@ -0,0 +1,162 @@ +package class_2022_05_2_week; + +import java.util.Arrays; + +// 来自字节 +// 一共有n个人,从左到右排列,依次编号0~n-1 +// h[i]是第i个人的身高 +// v[i]是第i个人的分数 +// 要求从左到右选出一个子序列,在这个子序列中的人,从左到右身高是不下降的 +// 返回所有符合要求的子序列中,分数最大累加和是多大 +// n <= 10的5次方, 1 <= h[i] <= 10的9次方, 1 <= v[i] <= 10的9次方 +public class Code03_SortedSubsequenceMaxSum { + + // 为了测试 + // 绝对正确的暴力方法 + public static int right(int[] h, int[] v) { + return process(h, v, 0, 0); + } + + public static int process(int[] h, int[] v, int index, int preValue) { + if (index == h.length) { + return 0; + } + int p1 = process(h, v, index + 1, preValue); + int p2 = h[index] >= preValue ? (v[index] + process(h, v, index + 1, h[index])) : 0; + return Math.max(p1, p2); + } + + // 正式方法 + // 时间复杂度O(N * logN) + public static int maxSum(int[] h, int[] v) { + int n = h.length; + int[] rank = new int[n]; + for (int i = 0; i < n; i++) { + rank[i] = h[i]; + } + Arrays.sort(rank); + SegmentTree st = new SegmentTree(n); + for (int i = 0; i < n; i++) { + int height = rank(rank, h[i]); + // 1~height max + st.update(height, st.max(height) + v[i]); + } + return st.max(n); + } + + // [150, 152, 160, 175] 160 + // 1 2 3 4 + // 3 + public static int rank(int[] rank, int num) { + int l = 0; + int r = rank.length - 1; + int m = 0; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (rank[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans + 1; + } + + public static class SegmentTree { + private int n; + private int[] max; + private int[] update; + + public SegmentTree(int maxSize) { + n = maxSize + 1; + max = new int[n << 2]; + update = new int[n << 2]; + Arrays.fill(update, -1); + } + + public void update(int index, int c) { + update(index, index, c, 1, n, 1); + } + + public int max(int right) { + return max(1, right, 1, n, 1); + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + private void pushDown(int rt, int ln, int rn) { + if (update[rt] != -1) { + update[rt << 1] = update[rt]; + max[rt << 1] = update[rt]; + update[rt << 1 | 1] = update[rt]; + max[rt << 1 | 1] = update[rt]; + update[rt] = -1; + } + } + + private void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + max[rt] = C; + update[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + private int max(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return max[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans = Math.max(ans, max(L, R, l, mid, rt << 1)); + } + if (R > mid) { + ans = Math.max(ans, max(L, R, mid + 1, r, rt << 1 | 1)); + } + return ans; + } + + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * v) + 1; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 30; + int V = 100; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int[] h = randomArray(n, V); + int[] v = randomArray(n, V); + if (right(h, v) != maxSum(h, v)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_2_week/Code04_OneEdgeMagicMinPathSum.java b/算法周更班/class_2022_05_2_week/Code04_OneEdgeMagicMinPathSum.java new file mode 100644 index 0000000..c2e88d5 --- /dev/null +++ b/算法周更班/class_2022_05_2_week/Code04_OneEdgeMagicMinPathSum.java @@ -0,0 +1,173 @@ +package class_2022_05_2_week; + +import java.util.ArrayList; +import java.util.PriorityQueue; + +// 来自网易 +// 给出一个有n个点,m条有向边的图 +// 你可以施展魔法,把有向边,变成无向边 +// 比如A到B的有向边,权重为7。施展魔法之后,A和B通过该边到达彼此的代价都是7。 +// 求,允许施展一次魔法的情况下,1到n的最短路,如果不能到达,输出-1。 +// n为点数, 每条边用(a,b,v)表示,含义是a到b的这条边,权值为v +// 点的数量 <= 10^5,边的数量 <= 2 * 10^5,1 <= 边的权值 <= 10^6 +public class Code04_OneEdgeMagicMinPathSum { + + // 为了测试 + // 相对暴力的解 + // 尝试每条有向边,都变一次无向边,然后跑一次dijkstra算法 + // 那么其中一定有最好的答案 + public static int min1(int n, int[][] roads) { + int ans = Integer.MAX_VALUE; + for (int i = 0; i < roads.length; i++) { + ArrayList> graph = new ArrayList<>(); + for (int j = 0; j <= n; j++) { + graph.add(new ArrayList<>()); + } + graph.get(roads[i][1]).add(new int[] { roads[i][0], roads[i][2] }); + for (int[] r : roads) { + graph.get(r[0]).add(new int[] { r[1], r[2] }); + } + ans = Math.min(ans, dijkstra1(n, graph)); + } + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + public static int dijkstra1(int n, ArrayList> graph) { + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[1] - b[1]); + boolean[] visited = new boolean[n + 1]; + heap.add(new int[] { 1, 0 }); + int ans = Integer.MAX_VALUE; + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + if (cur[0] == n) { + ans = cur[1]; + break; + } + if (visited[cur[0]]) { + continue; + } + visited[cur[0]] = true; + for (int[] edge : graph.get(cur[0])) { + int to = edge[0]; + int weight = edge[1]; + if (!visited[to]) { + heap.add(new int[] { to, cur[1] + weight }); + } + } + } + return ans; + } + + // 最优解 + // 时间复杂度O(N * logN) + // N <= 2 * 10^5 + public static int min2(int n, int[][] roads) { + ArrayList> graph = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + graph.add(new ArrayList<>()); + } + for (int[] r : roads) { + graph.get(r[0]).add(new int[] { 0, r[1], r[2] }); + graph.get(r[1]).add(new int[] { 1, r[0], r[2] }); + } + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[2] - b[2]); + boolean[][] visited = new boolean[2][n + 1]; + // a -> 0,a 1,a + // boolean[] visted = new boolean[n+1] + // visted[i] == true 去过了!从队列里弹出来过了!以后别碰了! + // visted[i] == false 没去过!第一次从队列里弹出来!当前要处理! + // 0,1,0 -> 之前没有走过魔法路,当前来到1号出发点,代价是0 + heap.add(new int[] { 0, 1, 0 }); + int ans = Integer.MAX_VALUE; + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + if (visited[cur[0]][cur[1]]) { + continue; + } + visited[cur[0]][cur[1]] = true; + if (cur[1] == n) { + ans = Math.min(ans, cur[2]); + if (visited[0][n] && visited[1][n]) { + break; + } + } + for (int[] edge : graph.get(cur[1])) { + // 当前来到cur + // 之前有没有走过魔法路径:cur[0] == 0 ,没走过!cur[0] = 1, 走过了 + // 当前来到的点是啥,cur[1],点编号! + // 之前的总代价是啥?cur[2] + // cur,往下,能走的,所有的路在哪? + // 当前的路,叫edge + // 当前的路,是不是魔法路!edge[0] = 0 , 不是魔法路 + // edge[0] == 1,是魔法路 + // cur[0] + edge[0] == 0 + // 路 :0 5 20 + // 当前路,不是魔法路,去往的点是5号点,该路权重是20 + // 路 :1 7 13 + // 当前路,是魔法路,去往的点是7号点,该路权重是13 + if (cur[0] + edge[0] == 0) { + if (!visited[0][edge[1]]) { + heap.add(new int[] { 0, edge[1], cur[2] + edge[2] }); + } + } + // cur[0] + edge[0] == 1 + // 0 1 + // 1 0 + if (cur[0] + edge[0] == 1) { + if (!visited[1][edge[1]]) { + heap.add(new int[] { 1, edge[1], cur[2] + edge[2] }); + } + } + // 1 1 == 2 + } + } + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + // 为了测试 + public static int[][] randomRoads(int n, int v) { + int m = (int) (Math.random() * (n * (n - 1) / 2)) + 1; + int[][] roads = new int[m][3]; + for (int i = 0; i < m; i++) { + roads[i][0] = (int) (Math.random() * n) + 1; + roads[i][1] = (int) (Math.random() * n) + 1; + roads[i][2] = (int) (Math.random() * v) + 1; + } + return roads; + } + + // 为了测试 + public static void main(String[] args) { + int N = 20; + int V = 30; + int testTime = 20000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int[][] roads = randomRoads(n, V); + if (min1(n, roads) != min2(n, roads)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + + // 点的数量10^5 + int n = 100000; + // 边的数量2 * 10^5 + int m = 200000; + // 时间复杂度很明显和边的权重是没关系的 + // 所以这里设置小一点,防止出现溢出的解 + int v = 100; + int[][] roads = new int[m][3]; + for (int i = 0; i < m; i++) { + roads[i][0] = (int) (Math.random() * n) + 1; + roads[i][1] = (int) (Math.random() * n) + 1; + roads[i][2] = (int) (Math.random() * v) + 1; + } + long start = System.currentTimeMillis(); + System.out.println("运行结果 : " + min2(n, roads)); + long end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒) : " + (end - start)); + } + +} diff --git a/算法周更班/class_2022_05_2_week/Code05_RedAndWhiteSquares.java b/算法周更班/class_2022_05_2_week/Code05_RedAndWhiteSquares.java new file mode 100644 index 0000000..040b33c --- /dev/null +++ b/算法周更班/class_2022_05_2_week/Code05_RedAndWhiteSquares.java @@ -0,0 +1,211 @@ +package class_2022_05_2_week; + +// 来自网易 +// 小红拿到了一个大立方体,该大立方体由1*1*1的小方块拼成,初始每个小方块都是白色。 +// 小红可以每次选择一个小方块染成红色 +// 每次小红可能选择同一个小方块重复染色 +// 每次染色以后,你需要帮小红回答出当前的白色连通块数 +// 如果两个小方块共用同一个面,且颜色相同,则它们是连通的 +// 给定n、m、h,表示大立方体的长、宽、高 +// 给定k次操作,每一次操作用(a, b, c)表示在大立方体的该位置进行染色 +// 返回长度为k的数组,表示每一次操作后,白色方块的连通块数 +// n * m * h <= 10 ^ 5,k <= 10 ^ 5 +public class Code05_RedAndWhiteSquares { + + // 暴力方法 + // 时间复杂度(k * n * m * h); + public static int[] blocks1(int n, int m, int h, int[][] ops) { + int k = ops.length; + int[][][] cube = new int[n][m][h]; + int value = 1; + int[] ans = new int[k]; + for (int i = 0; i < k; i++) { + cube[ops[i][0]][ops[i][1]][ops[i][2]] = -1; + for (int x = 0; x < n; x++) { + for (int y = 0; y < m; y++) { + for (int z = 0; z < h; z++) { + if (cube[x][y][z] != -1 && cube[x][y][z] != value) { + ans[i]++; + infect(cube, x, y, z, value); + } + } + } + } + value++; + } + return ans; + } + + public static void infect(int[][][] cube, int a, int b, int c, int change) { + if (a < 0 || a == cube.length || b < 0 || b == cube[0].length || c < 0 || c == cube[0][0].length + || cube[a][b][c] == -1 || cube[a][b][c] == change) { + return; + } + cube[a][b][c] = change; + infect(cube, a - 1, b, c, change); + infect(cube, a + 1, b, c, change); + infect(cube, a, b - 1, c, change); + infect(cube, a, b + 1, c, change); + infect(cube, a, b, c - 1, change); + infect(cube, a, b, c + 1, change); + } + + // 最优解 + // O(k + n * m * h) + public static int[] blocks2(int n, int m, int h, int[][] ops) { + int k = ops.length; + int[][][] red = new int[n][m][h]; + for (int[] op : ops) { + red[op[0]][op[1]][op[2]]++; + } + UnionFind uf = new UnionFind(n, m, h, red); + int[] ans = new int[k]; + for (int i = k - 1; i >= 0; i--) { + ans[i] = uf.sets; + int x = ops[i][0]; + int y = ops[i][1]; + int z = ops[i][2]; + if (--red[x][y][z] == 0) { + // x, y ,z 这个格子,变白,建立自己的小集合 + // 然后6个方向,集合该合并合并 + uf.finger(x, y, z); + } + } + return ans; + } + + public static class UnionFind { + public int n; + public int m; + public int h; + public int[] father; + public int[] size; + public int[] help; + public int sets; + + public UnionFind(int a, int b, int c, int[][][] red) { + n = a; + m = b; + h = c; + int len = n * m * h; + father = new int[len]; + size = new int[len]; + help = new int[len]; + for (int x = 0; x < n; x++) { + for (int y = 0; y < m; y++) { + for (int z = 0; z < h; z++) { + if (red[x][y][z] == 0) { + finger(x, y, z); + } + } + } + } + } + + public void finger(int x, int y, int z) { + // x,y,z + // 一维数值 + int i = index(x, y, z); + father[i] = i; + size[i] = 1; + sets++; + union(i, x - 1, y, z); + union(i, x + 1, y, z); + union(i, x, y - 1, z); + union(i, x, y + 1, z); + union(i, x, y, z - 1); + union(i, x, y, z + 1); + } + + private int index(int x, int y, int z) { + return z * n * m + y * n + x; + } + + private void union(int i, int x, int y, int z) { + if (x < 0 || x == n || y < 0 || y == m || z < 0 || z == h) { + return; + } + int j = index(x, y, z); + if (size[j] == 0) { + return; + } + i = find(i); + j = find(j); + if (i != j) { + if (size[i] >= size[j]) { + father[j] = i; + size[i] += size[j]; + } else { + father[i] = j; + size[j] += size[i]; + } + sets--; + } + } + + private int find(int i) { + int s = 0; + while (i != father[i]) { + help[s++] = i; + i = father[i]; + } + while (s > 0) { + father[help[--s]] = i; + } + return i; + } + + } + + // 为了测试 + public static int[][] randomOps(int n, int m, int h) { + int size = (int) (Math.random() * (n * m * h)) + 1; + int[][] ans = new int[size][3]; + for (int i = 0; i < size; i++) { + ans[i][0] = (int) (Math.random() * n); + ans[i][1] = (int) (Math.random() * m); + ans[i][2] = (int) (Math.random() * h); + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int size = 10; + int testTime = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * size) + 1; + int m = (int) (Math.random() * size) + 1; + int h = (int) (Math.random() * size) + 1; + int[][] ops = randomOps(n, m, h); + int[] ans1 = blocks1(n, m, h, ops); + int[] ans2 = blocks2(n, m, h, ops); + for (int j = 0; j < ops.length; j++) { + if (ans1[j] != ans2[j]) { + System.out.println("出错了!"); + } + } + } + System.out.println("测试结束"); + + // 立方体达到10^6规模 + int n = 100; + int m = 100; + int h = 100; + int len = n * m * h; + // 操作条数达到10^6规模 + int[][] ops = new int[len][3]; + for (int i = 0; i < len; i++) { + ops[i][0] = (int) (Math.random() * n); + ops[i][1] = (int) (Math.random() * m); + ops[i][2] = (int) (Math.random() * h); + } + long start = System.currentTimeMillis(); + blocks2(n, m, h, ops); + long end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒) : " + (end - start)); + + } + +} diff --git a/算法周更班/class_2022_05_3_week/Code01_MaxNumberUnderLimit.java b/算法周更班/class_2022_05_3_week/Code01_MaxNumberUnderLimit.java new file mode 100644 index 0000000..924ac09 --- /dev/null +++ b/算法周更班/class_2022_05_3_week/Code01_MaxNumberUnderLimit.java @@ -0,0 +1,188 @@ +package class_2022_05_3_week; + +import java.util.Arrays; + +// 来自字节 +// 输入: +// 去重数组arr,里面的数只包含0~9 +// limit,一个数字 +// 返回: +// 要求比limit小的情况下,能够用arr拼出来的最大数字 +public class Code01_MaxNumberUnderLimit { + + public static int tmp = 0; + + // 暴力尝试的方法 + public static int maxNumber1(int[] arr, int limit) { + tmp = 0; + Arrays.sort(arr); + limit--; + int offset = 1; + while (offset <= limit / 10) { + offset *= 10; + } + process1(arr, 0, offset, limit); + if (tmp == 0) { + int rest = 0; + offset /= 10; + while (offset > 0) { + rest += arr[arr.length - 1] * offset; + offset /= 10; + } + return rest; + } + return tmp; + } + + public static void process1(int[] arr, int num, int offset, int limit) { + if (offset == 0) { + if (num <= limit) { + tmp = Math.max(tmp, num); + } + } else { + for (int cur : arr) { + process1(arr, num * 10 + cur, offset / 10, limit); + } + } + } + + // 正式方法 + public static int maxNumber2(int[] arr, int limit) { + // arr里面是不重复的数字,且只包含0~9 + Arrays.sort(arr); + limit--; + // <= limit 且最大的数字 + // 68886 + // 10000 + // 为了取数而设计的! + // 457 + // 100 + int offset = 1; + while (offset <= limit / 10) { + offset *= 10; + } + int ans = process2(arr, limit, offset); + if (ans != -1) { + return ans; + } else { + offset /= 10; + int rest = 0; + while (offset > 0) { + rest += arr[arr.length - 1] * offset; + offset /= 10; + } + return rest; + } + } + + // 可以选哪些数字,都在arr里,arr是有序的,[3,6,8,9] + // limit : <= limit 且尽量的大! 68886 + // offset : 10000 + // 1000 + // 100 + // offset 下标用! + public static int process2(int[] arr, int limit, int offset) { + // 之前的数字和limit完全一样,且limit所有数字都一样 + if (offset == 0) { + return limit; + } + // 当前的数字 + // 68886 + // 10000 + // 6 + int cur = (limit / offset) % 10; + // 6 arr中 <=6 最近的! + int near = near(arr, cur); + if (near == -1) { + return -1; + } else if (arr[near] == cur) { // 找出来的数字,真的和当前数字cur一样! + int ans = process2(arr, limit, offset / 10); + if (ans != -1) { + return ans; + } else if (near > 0) { // 虽然后续没成功,但是我自己还能下降!还能选更小的数字 + near--; + return (limit / (offset * 10)) * offset * 10 + (arr[near] * offset) + rest(arr, offset / 10); + } else { // 后续没成功,我自己也不能再下降了!宣告失败,往上返回! + return -1; + } + } else { // arr[near] < cur + return (limit / (offset * 10)) * offset * 10 + (arr[near] * offset) + rest(arr, offset / 10); + } + } + + // 比如offset = 100 + // 一共3位数 + // 那么就把arr中最大的数字x,拼成xxx,返回 + // 比如offset = 10000 + // 一共5位数 + // 那么就把arr中最大的数字x,拼成xxxxx,返回 + public static int rest(int[] arr, int offset) { + int rest = 0; + while (offset > 0) { + rest += arr[arr.length - 1] * offset; + offset /= 10; + } + return rest; + } + + // 在有序数组arr中,找到<=num,且最大的数字,在arr中的位置返回 + // 如果所有数字都大于num,返回-1 + // [3,6,9] num = 4 3 + // [5,7,9] num = 4 -1 + public static int near(int[] arr, int num) { + int l = 0; + int r = arr.length - 1; + int m = 0; + int near = -1; + while (l <= r) { + m = (l + r) / 2; + if (arr[m] <= num) { + near = m; + l = m + 1; + } else { + r = m - 1; + } + } + return near; + } + + // 为了测试 + public static int[] randomArray() { + int[] arr = new int[(int) (Math.random() * 10) + 1]; + boolean[] cnt = new boolean[10]; + for (int i = 0; i < arr.length; i++) { + do { + arr[i] = (int) (Math.random() * 10); + } while (cnt[arr[i]]); + cnt[arr[i]] = true; + } + return arr; + } + + public static void main(String[] args) { + int max = 3000; + int testTime = 100; + System.out.println("测试开始"); + for (int i = 0; i < max; i++) { + int[] arr = randomArray(); + for (int j = 0; j < testTime; j++) { + int ans1 = maxNumber1(arr, i); + int ans2 = maxNumber2(arr, i); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println("数组为 :"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("数字为 :" + i); + System.out.println(ans1); + System.out.println(ans2); + } + } + } + System.out.println("测试结束"); + + } + +} diff --git a/算法周更班/class_2022_05_3_week/Code02_RemoveNumbersNotIncreasingAll.java b/算法周更班/class_2022_05_3_week/Code02_RemoveNumbersNotIncreasingAll.java new file mode 100644 index 0000000..9c09f2f --- /dev/null +++ b/算法周更班/class_2022_05_3_week/Code02_RemoveNumbersNotIncreasingAll.java @@ -0,0 +1,162 @@ +package class_2022_05_3_week; + +// 来自京东 +// 4.2笔试 +// 给定一个数组arr,长度为N,arr中所有的值都在1~K范围上 +// 你可以删除数字,目的是让arr的最长递增子序列长度小于K +// 返回至少删除几个数字能达到目的 +// N <= 10^4,K <= 10^2 +public class Code02_RemoveNumbersNotIncreasingAll { + + // 暴力方法 + // 为了验证 + public static int minRemove1(int[] arr, int k) { + return process1(arr, 0, new int[arr.length], 0, k); + } + + public static int process1(int[] arr, int index, int[] path, int size, int k) { + if (index == arr.length) { + return lengthOfLIS(path, size) < k ? (arr.length - size) : Integer.MAX_VALUE; + } else { + int p1 = process1(arr, index + 1, path, size, k); + path[size] = arr[index]; + int p2 = process1(arr, index + 1, path, size + 1, k); + return Math.min(p1, p2); + } + } + + public static int lengthOfLIS(int[] arr, int size) { + if (size == 0) { + return 0; + } + int[] ends = new int[size]; + ends[0] = arr[0]; + int right = 0; + int l = 0; + int r = 0; + int m = 0; + int max = 1; + for (int i = 1; i < size; i++) { + l = 0; + r = right; + while (l <= r) { + m = (l + r) / 2; + if (arr[i] > ends[m]) { + l = m + 1; + } else { + r = m - 1; + } + } + right = Math.max(right, l); + ends[l] = arr[i]; + max = Math.max(max, l + 1); + } + return max; + } + + // arr[0...index-1]上,选择了一些数字,之前的决定! + // len长度了!len = 3 : 1 2 3 + // arr[index....]是能够决定的,之前的,已经不能再决定了 + // 返回:让最终保留的数字,凑不足k长度的情况下,至少要删几个! + public static int zuo(int[] arr, int index, int len, int k) { + if (len == k) { + return Integer.MAX_VALUE; + } + // 凑的(1...len)还不到(1...k) + if (index == arr.length) { + return 0; + } + // 没凑到 < k, 有数字! + int cur = arr[index]; + // 可能性1:保留 + // 可能性2:删除 + // 1...3 3 + if (len >= cur || len + 1 < cur) { + return zuo(arr, index + 1, len, k); + } + // 1..3 4 + // len + 1 == cur + // 可能性1:保留 + int p1 = zuo(arr, index + 1, len + 1, k); + // 可能性2:删除 + int p2 = Integer.MAX_VALUE; + int next2 = zuo(arr, index + 1, len, k); + if(next2 != Integer.MAX_VALUE) { + p2 = 1 + next2; + } + return Math.min(p1, p2); + } + + // 正式方法 + // 时间复杂度O(N*K) + public static int minRemove2(int[] arr, int k) { + int n = arr.length; + int[][] dp = new int[n][k]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < k; j++) { + dp[i][j] = -1; + } + } + return process2(arr, k, 0, 0, dp); + } + + public static int process2(int[] arr, int k, int index, int range, int[][] dp) { + if (range == k) { + return Integer.MAX_VALUE; + } + if (index == arr.length) { + return 0; + } + if (dp[index][range] != -1) { + return dp[index][range]; + } + int ans = 0; + if (arr[index] == range + 1) { + int p1 = process2(arr, k, index + 1, range, dp); + p1 += p1 != Integer.MAX_VALUE ? 1 : 0; + int p2 = process2(arr, k, index + 1, range + 1, dp); + ans = Math.min(p1, p2); + } else { + ans = process2(arr, k, index + 1, range, dp); + } + dp[index][range] = ans; + return ans; + } + + // 为了验证 + public static int[] randomArray(int len, int k) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * k) + 1; + } + return arr; + } + + // 为了验证 + public static void main(String[] args) { + int N = 15; + int K = 6; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N) + 1; + int k = (int) (Math.random() * K) + 1; + int[] arr = randomArray(len, k); + int ans1 = minRemove1(arr, k); + int ans2 = minRemove2(arr, k); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("k : " + k); + System.out.println("ans1 : " + ans1); + System.out.println("ans2 : " + ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_3_week/Code03_NumberOfCannon.java b/算法周更班/class_2022_05_3_week/Code03_NumberOfCannon.java new file mode 100644 index 0000000..6e482c1 --- /dev/null +++ b/算法周更班/class_2022_05_3_week/Code03_NumberOfCannon.java @@ -0,0 +1,97 @@ +package class_2022_05_3_week; + +import java.util.TreeMap; +import java.util.TreeSet; + +// 来自学员问题 +// 给定一个数组arr,表示从早到晚,依次会出现的导弹的高度 +// 大炮打导弹的时候,如果一旦大炮定了某个高度去打,那么这个大炮每次打的高度都必须下降一点 +// 1) 如果只有一个大炮,返回最多能拦截多少导弹 +// 2) 如果所有的导弹都必须拦截,返回最少的大炮数量 +public class Code03_NumberOfCannon { + + public static int numOfCannon(int[] arr) { + // key : 某个大炮打的结尾数值 + // value : 有多少个大炮有同样的结尾数值 + // 比如: + // 一共有A、B、C三个大炮 + // 如果A大炮此时打的高度是17,B大炮此时打的高度是7,C大炮此时打的高度是13 + // 那么在表中: + // 7, 1 + // 13, 1 + // 17, 1 + // 如果A大炮此时打的高度是13,B大炮此时打的高度是7,C大炮此时打的高度是13 + // 那么在表中: + // 7, 1 + // 13, 2 + TreeMap ends = new TreeMap<>(); + for (int num : arr) { + if (ends.ceilingKey(num + 1) == null) { + ends.put(Integer.MAX_VALUE, 1); + } + int ceilKey = ends.ceilingKey(num + 1); + if (ends.get(ceilKey) > 1) { + ends.put(ceilKey, ends.get(ceilKey) - 1); + } else { + ends.remove(ceilKey); + } + ends.put(num, ends.getOrDefault(num, 0) + 1); + } + int ans = 0; + for (int value : ends.values()) { + ans += value; + } + return ans; + } + + public static void main(String[] args) { + + // 有序表来说 + // add + // remove + // ceiling + // <= floor + // O(logN)! + TreeSet set = new TreeSet<>(); + + set.add(17); + set.add(20); + set.add(25); + + // >= 23 + System.out.println(set.ceiling(26)); + + // 有序表是去重的,key去重 + // A :99 + // B : 99 + // C : 99 + + TreeMap map = new TreeMap<>(); + map.put(99, 3); + // 76 + + if (map.ceilingKey(76) == null) { + // 没有大炮可以打76 + // 新开一门大炮,打76 + // 这个新跑,只能打75~ + map.put(75, map.getOrDefault(75, 0) + 1); + } else { // 之前有大炮可以打76,不需要新开一门炮! + int key = map.ceilingKey(76); + // 99 -1 75 +1 + + if(map.get(key) > 1) { + map.put(key, map.get(key) - 1); + }else { + map.remove(key); + } + + map.put(75, map.getOrDefault(75, 0) + 1); + } + + +// +// int[] arr = { 15, 7, 14, 6, 5, 13, 5, 10, 9 }; +// System.out.println(numOfCannon(arr)); + } + +} diff --git a/算法周更班/class_2022_05_3_week/Code04_MinJumpUsePre.java b/算法周更班/class_2022_05_3_week/Code04_MinJumpUsePre.java new file mode 100644 index 0000000..692109d --- /dev/null +++ b/算法周更班/class_2022_05_3_week/Code04_MinJumpUsePre.java @@ -0,0 +1,154 @@ +package class_2022_05_3_week; + +import java.util.Arrays; + +// 来自学员问题 +// 为了给刷题的同学一些奖励,力扣团队引入了一个弹簧游戏机 +// 游戏机由 N 个特殊弹簧排成一排,编号为 0 到 N-1 +// 初始有一个小球在编号 0 的弹簧处。若小球在编号为 i 的弹簧处 +// 通过按动弹簧,可以选择把小球向右弹射 jump[i] 的距离,或者向左弹射到任意左侧弹簧的位置 +// 也就是说,在编号为 i 弹簧处按动弹簧, +// 小球可以弹向 0 到 i-1 中任意弹簧或者 i+jump[i] 的弹簧(若 i+jump[i]>=N ,则表示小球弹出了机器) +// 小球位于编号 0 处的弹簧时不能再向左弹。 +// 为了获得奖励,你需要将小球弹出机器。 +// 请求出最少需要按动多少次弹簧,可以将小球从编号 0 弹簧弹出整个机器,即向右越过编号 N-1 的弹簧。 +// 测试链接 : https://leetcode-cn.com/problems/zui-xiao-tiao-yue-ci-shu/ +public class Code04_MinJumpUsePre { + + // 宽度优先遍历 + // N*logN + public int minJump(int[] jump) { + int n = jump.length; + int[] queue = new int[n]; + int l = 0; + int r = 0; + queue[r++] = 0; + IndexTree it = new IndexTree(n); + // 1...n初始化的时候 每个位置填上1 + for (int i = 1; i < n; i++) { + it.add(i, 1); + } + int step = 0; + while (l != r) { // 队列里面还有东西 + // tmp记录了当前层的终止位置! + int tmp = r; + // 当前层的所有节点,都去遍历! + for (; l < tmp; l++) { + int cur = queue[l]; + int forward = cur + jump[cur]; + if (forward >= n) { + return step + 1; + } + if (it.value(forward) != 0) { + queue[r++] = forward; + it.add(forward, -1); + } + // cur + // 1....cur-1 cur + while (it.sum(cur - 1) != 0) { + int find = find(it, cur - 1); + it.add(find, -1); + queue[r++] = find; + } + } + step++; + } + return -1; + } + + public static int find(IndexTree it, int right) { + int left = 0; + int mid = 0; + int find = 0; + while (left <= right) { + mid = (left + right) / 2; + if (it.sum(mid) > 0) { + find = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + return find; + } + + public static class IndexTree { + + private int[] tree; + private int N; + + public IndexTree(int size) { + N = size; + tree = new int[N + 1]; + } + + public int value(int index) { + if (index == 0) { + return sum(0); + } else { + return sum(index) - sum(index - 1); + } + } + + public int sum(int i) { + int index = i + 1; + int ret = 0; + while (index > 0) { + ret += tree[index]; + index -= index & -index; + } + return ret; + } + + public void add(int i, int d) { + int index = i + 1; + while (index <= N) { + tree[index] += d; + index += index & -index; + } + } + + } + + // 感谢黄汀同学 + // 弄出了时间复杂度O(N)的过程 + // 和大厂刷题班,第10节,jump game类似 + public int minJump2(int[] jump) { + int N = jump.length; + int ans = N; + int next = jump[0]; + if (next >= N) { + return 1; + } + if (next + jump[next] >= N) { + return 2; + } + // dp[i] : 来到i位置,最少跳几步? + int[] dp = new int[N + 1]; + Arrays.fill(dp, N); + // dis[i] : <= i步的情况下,最远能跳到哪? + int[] dis = new int[N]; + // 如果从0开始向前跳,<=1步的情况下,最远当然能到next + dis[1] = next; + // 如果从0开始向前跳,<=2步的情况下,最远可能比next + jump[next]要远, + // 这里先设置,以后可能更新 + dis[2] = next + jump[next]; + dp[next + jump[next]] = 2; + int step = 1; + for (int i = 1; i < N; i++) { + if (i > dis[step]) { + step++; + } + dp[i] = Math.min(dp[i], step + 1); + next = i + jump[i]; + if (next >= N) { + ans = Math.min(ans, dp[i] + 1); + } else if (dp[next] > dp[i] + 1) { + dp[next] = dp[i] + 1; + dis[dp[next]] = Math.max(dis[dp[next]], next); + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_05_4_week/Code01_SomeDPFromVT.java b/算法周更班/class_2022_05_4_week/Code01_SomeDPFromVT.java new file mode 100644 index 0000000..7f25965 --- /dev/null +++ b/算法周更班/class_2022_05_4_week/Code01_SomeDPFromVT.java @@ -0,0 +1,246 @@ +package class_2022_05_4_week; + +// 来自弗吉尼亚理工大学(VT),算法考试卷 +// 精选了还可以的几道题 +// 这些都是简单难度的动态规划,是面试中最常见的难度 +// 这几个题都有一些非常小的常见技巧可说 +public class Code01_SomeDPFromVT { + +// // arr[i][0] : 有趣值 +// // arr[i][1] : 进攻值 +// // arr[index...]所有的方案自由选择 +// // 必须让restFunny、restOffense值 <= 0 +// // 返回最小的方案数量(index...) +// public static int process(int[][] arr, int index, int restFunny, int restOffense) { +// if (restFunny <= 0 && restOffense <= 0) { +// return 0; +// } +// // 有的值,还没扣完 +// if (index == arr.length) { +// return Integer.MAX_VALUE; // 无效值 +// } +// // 有的值还没扣完 但是还有方案可选 +// // index号方案 不要 要 +// int p1 = process(arr, index + 1, restFunny, restOffense); +// // 要用index号方案 +// int p2 = Integer.MAX_VALUE; +// int next = process(arr, index + 1, restFunny - arr[index][0], restOffense - arr[index][1]); +// if (next != Integer.MAX_VALUE) { +// p2 = 1 + next; +// } +// return Math.min(p1, p2); +// } + + // 题目1 + // 方案1 : {7, 10} + // xxxx : {a , b} + // 1 2 3 4 + // FunnyGoal = 100 + // OffenseGoal = 130 + // 找到一个最少方案数,让FunnyGoal、OffenseGoal,都大于等于 + // 定义如下尝试过程 + // 贴纸数组stickers + // stickers[i][0] : i号贴纸的Funny值 + // stickers[i][1] : i号贴纸的Offense值 + // index....所有的贴纸,随便选择。index之前的贴纸不能选择, + // 在让restFunny和restOffense都小于等于0的要求下,返回最少的贴纸数量 + public static int minStickers1(int[][] stickers, int funnyGoal, int offenseGoal) { + return process1(stickers, 0, funnyGoal, offenseGoal); + } + + public static int process1(int[][] stickers, int index, int restFunny, int restOffense) { + if (restFunny <= 0 && restOffense <= 0) { + return 0; + } + if (index == stickers.length) { + return Integer.MAX_VALUE; + } + // 不选当前的贴纸 + int p1 = process1(stickers, index + 1, restFunny, restOffense); + // 选当前贴纸 + int p2 = Integer.MAX_VALUE; + int next2 = process1(stickers, index + 1, Math.max(0, restFunny - stickers[index][0]), + Math.max(0, restOffense - stickers[index][1])); + if (next2 != Integer.MAX_VALUE) { + p2 = next2 + 1; + } + return Math.min(p1, p2); + } + + // 改动态规划 + public static int minStickers2(int[][] stickers, int funnyGoal, int offenseGoal) { + int[][][] dp = new int[stickers.length][funnyGoal + 1][offenseGoal + 1]; + for (int i = 0; i < stickers.length; i++) { + for (int j = 0; j <= funnyGoal; j++) { + for (int k = 0; k <= offenseGoal; k++) { + dp[i][j][k] = -1; + } + } + } + return process2(stickers, 0, funnyGoal, offenseGoal, dp); + } + + public static int process2(int[][] stickers, int index, int restFunny, int restOffense, int[][][] dp) { + if (restFunny <= 0 && restOffense <= 0) { + return 0; + } + if (index == stickers.length) { + return Integer.MAX_VALUE; + } + if (dp[index][restFunny][restOffense] != -1) { + return dp[index][restFunny][restOffense]; + } + // 不选当前的贴纸 + int p1 = process2(stickers, index + 1, restFunny, restOffense, dp); + // 选当前贴纸 + int p2 = Integer.MAX_VALUE; + int next2 = process2(stickers, index + 1, Math.max(0, restFunny - stickers[index][0]), + Math.max(0, restOffense - stickers[index][1]), dp); + if (next2 != Integer.MAX_VALUE) { + p2 = next2 + 1; + } + int ans = Math.min(p1, p2); + dp[index][restFunny][restOffense] = ans; + return ans; + } + + // 严格位置依赖的动态规划 + public static int minStickers3(int[][] stickers, int funnyGoal, int offenseGoal) { + int n = stickers.length; + int[][][] dp = new int[n + 1][funnyGoal + 1][offenseGoal + 1]; + for (int f = 0; f <= funnyGoal; f++) { + for (int o = 0; o <= offenseGoal; o++) { + if (f != 0 || o != 0) { + dp[n][f][o] = Integer.MAX_VALUE; + } + } + } + for (int i = n - 1; i >= 0; i--) { + for (int f = 0; f <= funnyGoal; f++) { + for (int o = 0; o <= offenseGoal; o++) { + if (f != 0 || o != 0) { + int p1 = dp[i + 1][f][o]; + int p2 = Integer.MAX_VALUE; + int next2 = dp[i + 1][Math.max(0, f - stickers[i][0])][Math.max(0, o - stickers[i][1])]; + if (next2 != Integer.MAX_VALUE) { + p2 = next2 + 1; + } + dp[i][f][o] = Math.min(p1, p2); + } + } + } + } + return dp[0][funnyGoal][offenseGoal]; + } + + // 题目2 + // 绳子总长度为M + // 100 -> M + // (6, 100) (7,23) (10,34) -> arr + // 每一个长度的绳子对应一个价格,比如(6, 10)表示剪成长度为6的绳子,对应价格10 + // 可以重复切出某个长度的绳子 + // 定义递归如下: + // 所有可以切出来的长度 对应 价值都在数组ropes里 + // ropes[i] = {6, 10} 代表i方案为:切出长度为6的绳子,可以卖10元 + // index....所有的方案,随便选择。index之前的方案,不能选择 + // 返回最大的价值 + // 自己去改动态规划 + // arr[i][0] -> i号方案能切多少长度 + // arr[i][1] -> 切出来这个长度,就能获得的价值 + // arr[index....]自由选择,绳子还剩restLen长度 + // 返回,最大价值 + public static int maxValue(int[][] arr, int index, int restLen) { + if (restLen <= 0 || index == arr.length) { + return 0; + } + // 绳子还有剩余、且还有方案 + // index号方案 + // 不选 + int p1 = maxValue(arr, index + 1, restLen); + // 选 + int p2 = 0; + if (arr[index][0] <= restLen) { // 剩余绳子够长,才能选当前方案 + p2 = arr[index][1] + maxValue(arr, index, restLen - arr[index][0]); + } + return Math.max(p1, p2); + } + +// public static int maxValue(int[][] ropes, int index, int restLen) { +// if (restLen <= 0) { +// return 0; +// } +// // 当前index方案,就是不考虑 +// int p1 = maxValue(ropes, index + 1, restLen); +// // 当前index方案,考虑,然后因为可以重复选,所以注意下面的子过程调用 +// int p2 = -1; +// if (ropes[index][0] <= restLen) { +// // 当前index方案,选了一份 +// // 但是下面依然可以重复的选index方案 +// // 所以子过程里的index不增加,只是剩余长度减少 +// p2 = ropes[index][1] + maxValue(ropes, index, restLen - ropes[index][0]); +// } +// return Math.max(p1, p2); +// } + + // 题目3 + // 每一个序列都是[a,b]的形式,a < b + // 序列连接的方式为,前一个序列的b,要等于后一个序列的a + // 比如 : [3, 7]、[7, 13]、[13, 26]这三个序列就可以依次连接 + // 给定若干个序列,求最大连接的数量 + // 定义尝试过程如下 + // arr[i] = {4, 9}表示,第i个序列4开始,9结束 + // pre : 代表选择的上一个序列,的,index是多少 + // 比如选择的上一个序列如果是(4,9),是第5个序列,那么pre==5 + // 特别注意:如果从来没有选过序列,那么pre == -1 + // 这个函数含义 : + // index....所有的序列,随便选择。index之前的序列,不能选择 + // 上一个选择的序列,是pre号,如果pre==-1,说明之前没有选择过序列 + // 返回题目要求的那种连接方式下,最大的序列数量 + // [5,13] [1,19] [2, 3] [79, 81] ... + // [1,19] [2, 3] [5, 13] [79, 81] + // arr[i][0] : 开头 + // arr[i][1] : 结尾 + // arr已经根据开头排过序了! + // preEnd index + // [1, 3] [2, 4] [4, 7] + // 0 1 2 + // maxLen(0, -1) + // 0(选) -> maxLen(1, 0) + // 在arr[index...]选择序列,之前选的,离index最近的序列,位置在preIndex + // 请返回,index...能链接起来的,序列数量的最大值 + public static int maxLen(int[][] arr, int index, int preIndex) { + if (index == arr.length) { + return 0; + } + // 还有序列可以选 + // index号序列 + // 不选 + int p1 = maxLen(arr, index + 1, preIndex); + // 选 + int p2 = 0; + // [3,17] index(9,24) + if (arr[preIndex][1] == arr[index][0]) { // 才能选 + p2 = 1 + maxLen(arr, index + 1, index); + } + return Math.max(p1, p2); + } + + // O(N^2) + public static int maxNumberSubsequence(int[][] arr, int index, int pre) { + if (index == arr.length) { + return 0; + } + // 就是不要当前序列 + int p1 = maxNumberSubsequence(arr, index + 1, pre); + // 要当前序列 + int p2 = -1; + if (pre == -1 || arr[pre][1] == arr[index][0]) { + p2 = 1 + maxNumberSubsequence(arr, index + 1, index); + } + return Math.max(p1, p2); + } + + // O(N) + + +} diff --git a/算法周更班/class_2022_05_4_week/Code02_MinSetForEveryRange.java b/算法周更班/class_2022_05_4_week/Code02_MinSetForEveryRange.java new file mode 100644 index 0000000..28d2617 --- /dev/null +++ b/算法周更班/class_2022_05_4_week/Code02_MinSetForEveryRange.java @@ -0,0 +1,48 @@ +package class_2022_05_4_week; + +import java.util.Arrays; +import java.util.HashSet; + +// 给定区间的范围[xi,yi],xi<=yi,且都是正整数 +// 找出一个坐标集合set,set中有若干个数字 +// set要和每个给定的区间,有交集 +// 求set的最少需要几个数 +// 比如给定区间 : [5, 8] [1, 7] [2, 4] [1, 9] +// set最小可以是: {2, 6}或者{2, 5}或者{4, 5} +public class Code02_MinSetForEveryRange { + + public static int minSet(int[][] ranges) { + int n = ranges.length; + // events[i] = {a, b, c} + // a == 0, 表示这是一个区间的开始事件,这个区间结束位置是b + // a == 1, 表示这是一个区间的结束事件,b的值没有意义 + // c表示这个事件的时间点,不管是开始事件还是结束事件,都会有c这个值 + int[][] events = new int[n << 1][3]; + for (int i = 0; i < n; i++) { + // [3, 7] + // (0,7,3) + // (1,X,7) + events[i][0] = 0; + events[i][1] = ranges[i][1]; + events[i][2] = ranges[i][0]; + events[i + n][0] = 1; + events[i + n][2] = ranges[i][1]; + } + Arrays.sort(events, (a, b) -> a[2] - b[2]); + // 容器 + HashSet tmp = new HashSet<>(); + int ans = 0; + for (int[] event : events) { + if (event[0] == 0) { + tmp.add(event[1]); + } else { + if (tmp.contains(event[2])) { + ans++; + tmp.clear(); + } + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_05_4_week/Code03_MaxIncreasingSubarrayCanDeleteContinuousPart.java b/算法周更班/class_2022_05_4_week/Code03_MaxIncreasingSubarrayCanDeleteContinuousPart.java new file mode 100644 index 0000000..6eeb8ec --- /dev/null +++ b/算法周更班/class_2022_05_4_week/Code03_MaxIncreasingSubarrayCanDeleteContinuousPart.java @@ -0,0 +1,221 @@ +package class_2022_05_4_week; + +import java.util.Arrays; + +// 来自字节 +// 5.6笔试 +// 给定一个数组arr,长度为n,最多可以删除一个连续子数组, +// 求剩下的数组,严格连续递增的子数组最大长度 +// n <= 10^6 +public class Code03_MaxIncreasingSubarrayCanDeleteContinuousPart { + + // 暴力方法 + // 为了验证 + public static int maxLen1(int[] arr) { + int ans = max(arr); + int n = arr.length; + for (int L = 0; L < n; L++) { + for (int R = L; R < n; R++) { + int[] cur = delete(arr, L, R); + ans = Math.max(ans, max(cur)); + } + } + return ans; + } + + public static int[] delete(int[] arr, int L, int R) { + int n = arr.length; + int[] ans = new int[n - (R - L + 1)]; + int index = 0; + for (int i = 0; i < L; i++) { + ans[index++] = arr[i]; + } + for (int i = R + 1; i < n; i++) { + ans[index++] = arr[i]; + } + return ans; + } + + public static int max(int[] arr) { + if (arr.length == 0) { + return 0; + } + int ans = 1; + int cur = 1; + for (int i = 1; i < arr.length; i++) { + if (arr[i] > arr[i - 1]) { + cur++; + } else { + cur = 1; + } + ans = Math.max(ans, cur); + } + return ans; + } + + // 正式方法 + // 时间复杂度O(N*logN) + public static int maxLen2(int[] arr) { + if (arr.length == 0) { + return 0; + } + int n = arr.length; + int[] sorted = new int[n]; + for (int i = 0; i < n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted); + SegmentTree st = new SegmentTree(n); + st.update(rank(sorted, arr[0]), 1); + int[] dp = new int[n]; + dp[0] = 1; + int ans = 1; + // 一个数字也不删!长度! + int cur = 1; + for (int i = 1; i < n; i++) { + int rank = rank(sorted, arr[i]); + // (dp[i - 1] + 1) + int p1 = arr[i - 1] < arr[i] ? (dp[i - 1] + 1) : 1; +// // rank : 就是当前的数字 +// // 1~rank-1 : 第二个信息的max + int p2 = rank > 1 ? (st.max(rank - 1) + 1) : 1; + dp[i] = Math.max(p1, p2); + ans = Math.max(ans, dp[i]); + if (arr[i] > arr[i - 1]) { + cur++; + } else { + cur = 1; + } + // 我的当前值是rank + // 之前有没有还是rank的记录! + if (st.get(rank) < cur) { + st.update(rank, cur); + } + } + return ans; + } + + public static int rank(int[] sorted, int num) { + int l = 0; + int r = sorted.length - 1; + int m = 0; + int ans = -1; + while (l <= r) { + m = (l + r) / 2; + if (sorted[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans + 1; + } + + public static class SegmentTree { + private int n; + private int[] max; + private int[] update; + + public SegmentTree(int maxSize) { + n = maxSize + 1; + max = new int[n << 2]; + update = new int[n << 2]; + Arrays.fill(update, -1); + } + + public int get(int index) { + return max(index, index, 1, n, 1); + } + + public void update(int index, int c) { + update(index, index, c, 1, n, 1); + } + + public int max(int right) { + return max(1, right, 1, n, 1); + } + + private void pushUp(int rt) { + max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]); + } + + private void pushDown(int rt, int ln, int rn) { + if (update[rt] != -1) { + update[rt << 1] = update[rt]; + max[rt << 1] = update[rt]; + update[rt << 1 | 1] = update[rt]; + max[rt << 1 | 1] = update[rt]; + update[rt] = -1; + } + } + + private void update(int L, int R, int C, int l, int r, int rt) { + if (L <= l && r <= R) { + max[rt] = C; + update[rt] = C; + return; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + if (L <= mid) { + update(L, R, C, l, mid, rt << 1); + } + if (R > mid) { + update(L, R, C, mid + 1, r, rt << 1 | 1); + } + pushUp(rt); + } + + private int max(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { + return max[rt]; + } + int mid = (l + r) >> 1; + pushDown(rt, mid - l + 1, r - mid); + int ans = 0; + if (L <= mid) { + ans = Math.max(ans, max(L, R, l, mid, rt << 1)); + } + if (R > mid) { + ans = Math.max(ans, max(L, R, mid + 1, r, rt << 1 | 1)); + } + return ans; + } + + } + + // 为了验证 + public static int[] randomArray(int len, int v) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * v) - (int) (Math.random() * v); + } + return arr; + } + + // 为了验证 + public static void main(String[] args) { + int n = 100; + int v = 20; + int testTime = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int m = (int) (Math.random() * n); + int[] arr = randomArray(m, v); + int ans1 = maxLen1(arr); + int ans2 = maxLen2(arr); + if (ans1 != ans2) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_05_4_week/Code04_ABCSameNumber.java b/算法周更班/class_2022_05_4_week/Code04_ABCSameNumber.java new file mode 100644 index 0000000..65d61f4 --- /dev/null +++ b/算法周更班/class_2022_05_4_week/Code04_ABCSameNumber.java @@ -0,0 +1,169 @@ +package class_2022_05_4_week; + +// 来自京东 +// 4.2笔试 +// 给定一个长度为3N的数组,其中最多含有0、1、2三种值 +// 你可以把任何一个连续区间上的数组,全变成0、1、2中的一种 +// 目的是让0、1、2三种数字的个数都是N +// 返回最小的变化次数 +public class Code04_ABCSameNumber { + + // 暴力方法 + // 为了验证不会超过2次 + public static int minTimes1(int[] arr) { + int[] set = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + set[i] = arr[i]; + } + return process1(set, 0, arr); + } + + public static int process1(int[] set, int time, int[] origin) { + int[] cnt = new int[3]; + for (int num : set) { + cnt[num]++; + } + if (cnt[0] == cnt[1] && cnt[0] == cnt[2]) { + return time; + } else { + if (time == 2) { + return 3; + } + int ans = Integer.MAX_VALUE; + for (int L = 0; L < set.length; L++) { + for (int R = L; R < set.length; R++) { + set(set, L, R, 0); + ans = Math.min(ans, process1(set, time + 1, origin)); + set(set, L, R, 1); + ans = Math.min(ans, process1(set, time + 1, origin)); + set(set, L, R, 2); + ans = Math.min(ans, process1(set, time + 1, origin)); + rollback(set, L, R, origin); + } + } + return ans; + } + } + + public static void set(int[] set, int L, int R, int v) { + for (int i = L; i <= R; i++) { + set[i] = v; + } + } + + public static void rollback(int[] set, int L, int R, int[] origin) { + for (int i = L; i <= R; i++) { + set[i] = origin[i]; + } + } + + // 正式方法 + // 时间复杂度O(N) + public static int minTimes2(int[] arr) { + int[] cnt = new int[3]; + for (int num : arr) { + cnt[num]++; + } + if (cnt[0] == cnt[1] && cnt[0] == cnt[2]) { + return 0; + } + int n = arr.length; + int m = n / 3; + if ((cnt[0] < m && cnt[1] < m) || (cnt[0] < m && cnt[2] < m) || (cnt[1] < m && cnt[2] < m)) { + return 2; + } else { // 只有一种数的个数是小于m的 + return once(arr, cnt, m) ? 1 : 2; + } + } + + // 只有一种数是少于N/3 + public static boolean once(int[] arr, int[] cnt, int m) { + int lessV = cnt[0] < m ? 0 : (cnt[1] < m ? 1 : 2); + int lessT = lessV == 0 ? cnt[0] : (lessV == 1 ? cnt[1] : cnt[2]); + if (cnt[0] > m && modify(arr, 0, cnt[0], lessV, lessT)) { + return true; + } + if (cnt[1] > m && modify(arr, 1, cnt[1], lessV, lessT)) { + return true; + } + if (cnt[2] > m && modify(arr, 2, cnt[2], lessV, lessT)) { + return true; + } + return false; + } + + // 0 -> 10个 + // 1 -> 10个 + // 2 -> 10个 + // ========== + // 0 -> 7个 + // 2 -> 12个 1 -> 11个 + // 多的数 2 + // 少的数 0 + public static boolean modify(int[] arr, + int more, int moreT, + int less, int lessT) { + int[] cnt = new int[3]; + cnt[less] = lessT; + cnt[more] = moreT; + // 目标 + int aim = arr.length / 3; + int L = 0; + int R = 0; + while (R < arr.length || cnt[more] <= aim) { + // cnt[more] 窗口之外,多的数有几个? + if (cnt[more] > aim) { + // R++ 窗口右边界,右移 + cnt[arr[R++]]--; + } else if (cnt[more] < aim) { + cnt[arr[L++]]++; + } else { // 在窗口之外,多的数,够了! + // 少的数,和,另一种数other,能不能平均!都是10个! + if (cnt[less] + R - L < aim) { + cnt[arr[R++]]--; + } else if (cnt[less] + R - L > aim) { + cnt[arr[L++]]++; + } else { + return true; + } + } + } + return false; + } + + // 为了验证 + public static int[] randomArray(int len) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * 3); + } + return arr; + } + + // 为了验证 + public static void main(String[] args) { + // 数组长度一定是3的整数倍,且 <= 3*n + // 如下代码是验证操作次数一定不大于2次 + // 24个,8个 + int n = 8; + int testTime = 2000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int m = ((int) (Math.random() * n) + 1) * 3; + int[] arr = randomArray(m); + int ans1 = minTimes1(arr); + int ans2 = minTimes2(arr); + if (ans1 != ans2) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_06_1_week/Code01_WhereWillTheBallFall.java b/算法周更班/class_2022_06_1_week/Code01_WhereWillTheBallFall.java new file mode 100644 index 0000000..6727152 --- /dev/null +++ b/算法周更班/class_2022_06_1_week/Code01_WhereWillTheBallFall.java @@ -0,0 +1,46 @@ +package class_2022_06_1_week; + +// 最好打开链接看图 +// 用一个大小为 m x n 的二维网格 grid 表示一个箱子 +// 你有 n 颗球。箱子的顶部和底部都是开着的。 +// 箱子中的每个单元格都有一个对角线挡板,跨过单元格的两个角, +// 可以将球导向左侧或者右侧。 +// 将球导向右侧的挡板跨过左上角和右下角,在网格中用 1 表示。 +// 将球导向左侧的挡板跨过右上角和左下角,在网格中用 -1 表示。 +// 在箱子每一列的顶端各放一颗球。每颗球都可能卡在箱子里或从底部掉出来。 +// 如果球恰好卡在两块挡板之间的 "V" 形图案,或者被一块挡导向到箱子的任意一侧边上,就会卡住。 +// 返回一个大小为 n 的数组 answer , +// 其中 answer[i] 是球放在顶部的第 i 列后从底部掉出来的那一列对应的下标, +// 如果球卡在盒子里,则返回 -1 +// 本题测试链接 : https://leetcode.com/problems/where-will-the-ball-fall/ +public class Code01_WhereWillTheBallFall { + + public static int[] findBall(int[][] grid) { + int n = grid.length; + int m = grid[0].length; + int[] ans = new int[m]; + for (int col = 0; col < m; col++) { + // (0,0) (0,1) (0,2) + int i = 0; + int j = col; + while (i < n) { + // (i,j) 左上 -> 右下的格子 grid[i][j] == 1 + // (i+1, j+1) + // (i,j) 右上 -> 左下的格子 grid[i][j] == -1 + // (i+1, j-1) + int jnext = j + grid[i][j]; + if (jnext < 0 || jnext == m || grid[i][j] != grid[i][jnext]) { + ans[col] = -1; + break; + } + i++; + j = jnext; + } + if (i == n) { + ans[col] = j; + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_06_1_week/Code02_UniqueSubstringsInWraparoundString.java b/算法周更班/class_2022_06_1_week/Code02_UniqueSubstringsInWraparoundString.java new file mode 100644 index 0000000..7e0b6a9 --- /dev/null +++ b/算法周更班/class_2022_06_1_week/Code02_UniqueSubstringsInWraparoundString.java @@ -0,0 +1,34 @@ +package class_2022_06_1_week; + +// 把字符串 s 看作 "abcdefghijklmnopqrstuvwxyz" 的无限环绕字符串, +// 所以 s 看起来是这样的: +// ...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd.... +// 现在给定另一个字符串 p 。返回 s 中 不同 的 p 的 非空子串 的数量 +// 测试链接 : https://leetcode.com/problems/unique-substrings-in-wraparound-string/ +public class Code02_UniqueSubstringsInWraparoundString { + + public int findSubstringInWraproundString(String s) { + char[] str = s.toCharArray(); + int n = str.length; + int ans = 0; + int len = 1; + // 256 0~255 + int[] max = new int[256]; + max[str[0]]++; + for (int i = 1; i < n; i++) { + char cur = str[i]; + char pre = str[i - 1]; + if ((pre == 'z' && cur == 'a') || pre + 1 == cur) { + len++; + } else { + len = 1; + } + max[cur] = Math.max(max[cur], len); + } + for (int i = 0; i < 256; i++) { + ans += max[i]; + } + return ans; + } + +} diff --git a/算法周更班/class_2022_06_1_week/Code03_NumberOfAtoms.java b/算法周更班/class_2022_06_1_week/Code03_NumberOfAtoms.java new file mode 100644 index 0000000..b41256f --- /dev/null +++ b/算法周更班/class_2022_06_1_week/Code03_NumberOfAtoms.java @@ -0,0 +1,104 @@ +package class_2022_06_1_week; + +import java.util.TreeMap; + +// 给你一个字符串化学式 formula ,返回 每种原子的数量 。 +// 原子总是以一个大写字母开始,接着跟随 0 个或任意个小写字母,表示原子的名字。 +// 如果数量大于 1,原子后会跟着数字表示原子的数量。如果数量等于 1 则不会跟数字。 +// 例如,"H2O" 和 "H2O2" 是可行的,但 "H1O2" 这个表达是不可行的。 +// 两个化学式连在一起可以构成新的化学式。 +// 例如 "H2O2He3Mg4" 也是化学式。 +// 由括号括起的化学式并佐以数字(可选择性添加)也是化学式。 +// 例如 "(H2O2)" 和 "(H2O2)3" 是化学式。 +// 返回所有原子的数量,格式为:第一个(按字典序)原子的名字,跟着它的数量(如果数量大于 1), +// 然后是第二个原子的名字(按字典序),跟着它的数量(如果数量大于 1),以此类推。 +// 示例 1: +// 输入:formula = "H2O" +// 输出:"H2O" +// 解释:原子的数量是 {'H': 2, 'O': 1}。 +// 示例 2: +// 输入:formula = "Mg(OH)2" +// 输出:"H2MgO2" +// 解释:原子的数量是 {'H': 2, 'Mg': 1, 'O': 2}。 +// 示例 3: +// 输入:formula = "K4(ON(SO3)2)2" +// 输出:"K4N2O14S4" +// 解释:原子的数量是 {'K': 4, 'N': 2, 'O': 14, 'S': 4}。 +// 测试链接 : https://leetcode.com/problems/number-of-atoms/ +public class Code03_NumberOfAtoms { + + public static String countOfAtoms(String str) { + char[] s = str.toCharArray(); + Info info = process(s, 0); + StringBuilder builder = new StringBuilder(); + for (String key : info.cntMap.keySet()) { + builder.append(key); + int cnt = info.cntMap.get(key); + if (cnt > 1) { + builder.append(cnt); + } + } + return builder.toString(); + } + + public static class Info { + public TreeMap cntMap; + public int end; + + public Info(TreeMap c, int e) { + cntMap = c; + end = e; + } + } + + public static Info process(char[] s, int i) { + TreeMap cntMap = new TreeMap<>(); + int cnt = 0; + StringBuilder builder = new StringBuilder(); + Info info = null; + while (i < s.length && s[i] != ')') { + if (s[i] >= 'A' && s[i] <= 'Z' || s[i] == '(') { + if (builder.length() != 0 || info != null) { + cnt = cnt == 0 ? 1 : cnt; + if (builder.length() != 0) { + String key = builder.toString(); + cntMap.put(key, cntMap.getOrDefault(key, 0) + cnt); + builder.delete(0, builder.length()); + } else { + for (String key : info.cntMap.keySet()) { + cntMap.put(key, cntMap.getOrDefault(key, 0) + info.cntMap.get(key) * cnt); + } + info = null; + } + cnt = 0; + } + if (s[i] == '(') { + info = process(s, i + 1); + i = info.end + 1; + } else { + builder.append(s[i++]); + } + } else if (s[i] >= 'a' && s[i] <= 'z') { + builder.append(s[i++]); + } else { + cnt = cnt * 10 + s[i++] - '0'; + } + } + if (builder.length() != 0 || info != null) { + cnt = cnt == 0 ? 1 : cnt; + if (builder.length() != 0) { + String key = builder.toString(); + cntMap.put(key, cntMap.getOrDefault(key, 0) + cnt); + builder.delete(0, builder.length()); + } else { + for (String key : info.cntMap.keySet()) { + cntMap.put(key, cntMap.getOrDefault(key, 0) + info.cntMap.get(key) * cnt); + } + info = null; + } + cnt = 0; + } + return new Info(cntMap, i); + } + +} diff --git a/算法周更班/class_2022_06_1_week/Code04_SubstringWithLargestVariance.java b/算法周更班/class_2022_06_1_week/Code04_SubstringWithLargestVariance.java new file mode 100644 index 0000000..86d8d59 --- /dev/null +++ b/算法周更班/class_2022_06_1_week/Code04_SubstringWithLargestVariance.java @@ -0,0 +1,98 @@ +package class_2022_06_1_week; + +// 字符串的 波动 定义为子字符串中出现次数 最多 的字符次数与出现次数 最少 的字符次数之差。 +// 给你一个字符串 s ,它只包含小写英文字母。请你返回 s 里所有 子字符串的 最大波动 值。 +// 子字符串 是一个字符串的一段连续字符序列。 +// 注意:必须同时有,最多字符和最少字符的字符串才是有效的 +// 测试链接 : https://leetcode.cn/problems/substring-with-largest-variance/ +public class Code04_SubstringWithLargestVariance { + + public static int largestVariance1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int n = s.length(); + // a b a c b b a + // 0 1 0 2 1 1 0 + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = s.charAt(i) - 'a'; + } + int ans = 0; + // 26 * 26 * n O(N) + for (int more = 0; more < 26; more++) { + for (int less = 0; less < 26; less++) { + if (more != less) { + int continuousA = 0; + boolean appearB = false; + int max = 0; + // 从左到右遍历, + for (int i = 0; i < n; i++) { + if (arr[i] != more && arr[i] != less) { + continue; + } + if (arr[i] == more) { // 当前字符是more + continuousA++; + if (appearB) { + max++; + } + } else { // 当前字符是B + max = Math.max(max, continuousA) - 1; + continuousA = 0; + appearB = true; + } + ans = Math.max(ans, max); + } + } + } + } + return ans; + } + + public static int largestVariance2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int n = s.length(); + // a b a c b b a + // 0 1 0 2 1 1 0 + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = s.charAt(i) - 'a'; + } + // dp[a][b] = more a less b max + // dp[b][a] = more b less a max + int[][] dp = new int[26][26]; + // continuous[a][b] more a less b 连续出现a的次数 + // continuous[b][a] more b less a 连续出现b的次数 + int[][] continuous = new int[26][26]; + // appear[a][b] more a less b b有没有出现过 + // appear[b][a] more b less a a有没有出现过 + boolean[][] appear = new boolean[26][26]; + int ans = 0; + // 26 * N + for (int i : arr) { + for (int j = 0; j < 26; j++) { + if (j != i) { + // i,j + // more i less j 三个变量 连续出现i,j有没有出现过,i-j max + // more j less i 三个变量 连续出现j,i有没有出现过,j-i max + ++continuous[i][j]; + if (appear[i][j]) { + ++dp[i][j]; + } + if (!appear[j][i]) { + appear[j][i] = true; + dp[j][i] = continuous[j][i] - 1; + } else { + dp[j][i] = Math.max(dp[j][i], continuous[j][i]) - 1; + } + continuous[j][i] = 0; + ans = Math.max(ans, Math.max(dp[j][i], dp[i][j])); + } + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_06_2_week/Code01_MostStonesRemovedWithSameRowOrColumn.java b/算法周更班/class_2022_06_2_week/Code01_MostStonesRemovedWithSameRowOrColumn.java new file mode 100644 index 0000000..fd951c1 --- /dev/null +++ b/算法周更班/class_2022_06_2_week/Code01_MostStonesRemovedWithSameRowOrColumn.java @@ -0,0 +1,87 @@ +package class_2022_06_2_week; + +import java.util.HashMap; + +// n块石头放置在二维平面中的一些整数坐标点上 +// 每个坐标点上最多只能有一块石头 +// 如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。 +// 给你一个长度为 n 的数组 stones , +// 其中 stones[i] = [xi, yi] 表示第 i 块石头的位置, +// 返回 可以移除的石子 的最大数量。 +// 测试链接 : https://leetcode.com/problems/most-stones-removed-with-same-row-or-column/ +public class Code01_MostStonesRemovedWithSameRowOrColumn { + + public static int removeStones(int[][] stones) { + int n = stones.length; + HashMap rowPre = new HashMap(); + HashMap colPre = new HashMap(); + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n; i++) { + int x = stones[i][0]; + int y = stones[i][1]; + if (!rowPre.containsKey(x)) { + rowPre.put(x, i); + } else { + uf.union(i, rowPre.get(x)); + } + if (!colPre.containsKey(y)) { + colPre.put(y, i); + } else { + uf.union(i, colPre.get(y)); + } + } + return n - uf.sets(); + } + + public static class UnionFind { + + public int[] father; + public int[] size; + public int[] help; + public int sets; + + public UnionFind(int n) { + father = new int[n]; + size = new int[n]; + help = new int[n]; + for (int i = 0; i < n; i++) { + father[i] = i; + size[i] = 1; + } + sets = n; + } + + private int find(int i) { + int hi = 0; + while (i != father[i]) { + help[hi++] = i; + i = father[i]; + } + while (hi != 0) { + father[help[--hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int fi = find(i); + int fj = find(j); + if (fi != fj) { + if (size[fi] >= size[fj]) { + father[fj] = fi; + size[fi] += size[fj]; + } else { + father[fi] = fj; + size[fj] += size[fi]; + } + sets--; + } + } + + public int sets() { + return sets; + } + + } + +} diff --git a/算法周更班/class_2022_06_2_week/Code02_Solution.HEIC b/算法周更班/class_2022_06_2_week/Code02_Solution.HEIC new file mode 100644 index 0000000..e9fb575 Binary files /dev/null and b/算法周更班/class_2022_06_2_week/Code02_Solution.HEIC differ diff --git a/算法周更班/class_2022_06_2_week/Code02_SumOfTotalStrengthOfWizards.java b/算法周更班/class_2022_06_2_week/Code02_SumOfTotalStrengthOfWizards.java new file mode 100644 index 0000000..af33e2c --- /dev/null +++ b/算法周更班/class_2022_06_2_week/Code02_SumOfTotalStrengthOfWizards.java @@ -0,0 +1,53 @@ +package class_2022_06_2_week; + +// 作为国王的统治者,你有一支巫师军队听你指挥。 +// 给你一个下标从 0 开始的整数数组 strength , +// 其中 strength[i] 表示第 i 位巫师的力量值。 +// 对于连续的一组巫师(也就是这些巫师的力量值是 strength 的 子数组), +// 总力量 定义为以下两个值的 乘积 : +// 巫师中 最弱 的能力值 * 组中所有巫师的个人力量值 之和 。 +// 请你返回 所有 巫师组的 总 力量之和。由于答案可能很大,请将答案对 109 + 7 取余 后返回。 +// 子数组 是一个数组里 非空 连续子序列。 +// 测试链接 : https://leetcode.cn/problems/sum-of-total-strength-of-wizards/ +public class Code02_SumOfTotalStrengthOfWizards { + + public static final long mod = 1000000007; + + public static int totalStrength(int[] arr) { + int n = arr.length; + long preSum = arr[0]; + long[] sumSum = new long[n]; + sumSum[0] = arr[0]; + for (int i = 1; i < n; i++) { + preSum += arr[i]; + sumSum[i] = (sumSum[i - 1] + preSum) % mod; + } + int[] stack = new int[n]; + int size = 0; + long ans = 0; + for (int i = 0; i < n; i++) { + while (size > 0 && arr[stack[size - 1]] >= arr[i]) { + int m = stack[--size]; + int l = size > 0 ? stack[size - 1] : -1; + // l(<当前值,且最近,到不了) m(当前数,做为最小值) i(<=当前数,到不了的!) + ans += magicSum(arr, sumSum, l, m, i); + ans %= mod; + } + stack[size++] = i; + } + while (size > 0) { + int m = stack[--size]; + int l = size > 0 ? stack[size - 1] : -1; + ans += magicSum(arr, sumSum, l, m, n); + ans %= mod; + } + return (int) ans; + } + + public static long magicSum(int[] arr, long[] sumSum, int l, int m, int r) { + long left = (long) (m - l) * (sumSum[r - 1] - (m - 1 >= 0 ? sumSum[m - 1] : 0) + mod) % mod; + long right = (long) (r - m) * ((m - 1 >= 0 ? sumSum[m - 1] : 0) - (l - 1 >= 0 ? sumSum[l - 1] : 0) + mod) % mod; + return (long) arr[m] * ((left - right + mod) % mod); + } + +} diff --git a/算法周更班/class_2022_06_2_week/Code03_NumberOfDifferentSubsequencesGCDs.java b/算法周更班/class_2022_06_2_week/Code03_NumberOfDifferentSubsequencesGCDs.java new file mode 100644 index 0000000..cc51b17 --- /dev/null +++ b/算法周更班/class_2022_06_2_week/Code03_NumberOfDifferentSubsequencesGCDs.java @@ -0,0 +1,60 @@ +package class_2022_06_2_week; + +// 给你一个由正整数组成的数组 nums 。 +// 数字序列的 最大公约数 定义为序列中所有整数的共有约数中的最大整数。 +// 例如,序列 [4,6,16] 的最大公约数是 2 。 +// 数组的一个 子序列 本质是一个序列,可以通过删除数组中的某些元素(或者不删除)得到。 +// 例如,[2,5,10] 是 [1,2,1,2,4,1,5,10] 的一个子序列。 +// 计算并返回 nums 的所有 非空 子序列中 不同 最大公约数的 数目 。 +// 测试链接 : https://leetcode.com/problems/number-of-different-subsequences-gcds/ +public class Code03_NumberOfDifferentSubsequencesGCDs { + + // n不是数字的个数,是数组中的最大值 + // 体系学习班, + // 根据数据量猜解法, + // 要想通过测试,一定要让计算量不超过10的7次方~10的8次方 + // n/1 + n/2 + n/3 + n/4 + ... + n/n -> O(N * logN) + public static int countDifferentSubsequenceGCDs(int[] nums) { + // 找到数组中的最大数!max + int max = Integer.MIN_VALUE; + for (int num : nums) { + max = Math.max(max, num); + } + // 1~max,哪个数有哪个数没有 + boolean[] set = new boolean[max + 1]; + for (int num : nums) { + set[num] = true; + } + int ans = 0; + // a是当前想确定,是不是某个子序列的最大公约数,有a! + for (int a = 1; a <= max; a++) { + // 1)找到,离a最近的,a的倍数!1 2 3 ... g就是 + int g = a; + for (; g <= max; g += a) { + if (set[g]) { + break; + } + } + // 2) 找到了a最近的倍数,g + // g + 0 , g ?= a + // g + a , g ?= a + // g + 2a , g ?= a + // g + 3a , g ?= a + for (int b = g; b <= max; b += a) { + if (set[b]) { + g = gcd(g, b); + if (g == a) { + ans++; + break; + } + } + } + } + return ans; + } + + public static int gcd(int m, int n) { + return n == 0 ? m : gcd(n, m % n); + } + +} diff --git a/算法周更班/class_2022_06_2_week/Code04_ConsecutiveNumbersSum.java b/算法周更班/class_2022_06_2_week/Code04_ConsecutiveNumbersSum.java new file mode 100644 index 0000000..15e38da --- /dev/null +++ b/算法周更班/class_2022_06_2_week/Code04_ConsecutiveNumbersSum.java @@ -0,0 +1,101 @@ +package class_2022_06_2_week; + +// 给定一个正整数 n,返回 连续正整数满足所有数字之和为 n 的组数 。  +// 示例 1: +// 输入: n = 5 +// 输出: 2 +// 解释: 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5。 +// 示例 2: +// 输入: n = 9 +// 输出: 3 +// 解释: 9 = 4 + 5 = 2 + 3 + 4 +// 示例 3: +// 输入: n = 15 +// 输出: 4 +// 解释: 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5 +// 测试链接 : https://leetcode.com/problems/consecutive-numbers-sum/ +public class Code04_ConsecutiveNumbersSum { + + // 如果有,N = (x+1) + (x+2) + ... + (x+k) + // 上式子可以化简为:N = kx + k(k+1)/2 + // 左右两边同时乘以2,可以得到:2N = 2kx + k^2 + k + // 进而得到:2N = k(2x + k + 1) + // 2N 偶 k * (2x + k + 1) + // k 2x + k + 1 + // 所以,对于2N = k(2x + k + 1),这个式子来说,只要给定不同的一组x和k,就对应一种不同的方案 + // 进一步分析可以看出: + // 如果k为偶数,那么2x + k + 1就是奇数 + // 如果k为奇数,那么2x + k + 1就是偶数 + // 2N = 左 K 右 2x + k + 1 + // 2N 奇数因子K, 2x + k + 1 + // 也就是说,对于每一种方案,k和2x + k + 1,一定是不同的,并且连奇偶性都相反 + // 所以2N里任何一个奇数因子,可能作为k这一项,也可能作为2x+k+1这一项, + // 不管奇数因子作为哪一项,都可以推出另外一项的值,进而确定k和x具体是多少 + // 进而可以推出,2N里有多少个奇数因子,就有多少种方案 + // 于是这个题就变成了求N里有多少奇数因子 + // 一般来说,求N里有多少奇数因子,用O(根号N)的方法肯定可以 + // 但其实可以更加的优化, + // 如果 N = 3^a * 5^b * 7^c * 9^d ....那么N一共会出现多少奇数因子呢? + // N的质数因子:可以选择0个3..可以选择1个3...可以选择2个3...可以选择a个3,所以有a+1种选择 + // 上面的选择,去乘以:可以选择0个5..可以选择1个5...可以选择2个5...可以选择b个5,所以有b+1种选择 + // 上面的选择,去乘以:可以选择0个7..可以选择1个7...可以选择2个7...可以选择c个7,所以有c+1种选择 + // ... + // 所以,一共有(a + 1) * (b + 1) * (c + 1) * (d + 1) .....这么多个奇数因子 + public static int consecutiveNumbersSum1(int N) { + while ((N & 1) == 0) { + N >>= 1; + } + int res = 1; + for (int i = 3; i <= N; i += 2) { + int count = 1; + while (N % i == 0) { + N /= i; + count++; + } + res *= count; + } + return res; + } + + // 进一步优化 + public static int consecutiveNumbersSum2(int N) { + while ((N & 1) == 0) { + N >>= 1; + } + int res = 1; + // O(根号N) + for (int i = 3; i * i <= N; i += 2) { + int count = 1; + while (N % i == 0) { + N /= i; + count++; + } + // rest *= (计数+1) + res *= count; + } + // N == 1表示已经找到了所有奇数因子 + // N != 1表示只残留着最后一个奇数因子了 + // 简单证明:如果N最后残留着不只一个奇数因子, + // 比如x*y(不妨设x 10 + // i = 3 + // + // 105 -> 35 + // i = 5 ++ + // 35 -> 7 + // i = 7 + // i * i <= N + // 1 * (1+1) +// System.out.println(consecutiveNumbersSum1(N)); + System.out.println(consecutiveNumbersSum2(N)); + } + +} diff --git a/算法周更班/class_2022_06_3_week/Code01_MaxChunksToMakeSortedII.java b/算法周更班/class_2022_06_3_week/Code01_MaxChunksToMakeSortedII.java new file mode 100644 index 0000000..1b1c68a --- /dev/null +++ b/算法周更班/class_2022_06_3_week/Code01_MaxChunksToMakeSortedII.java @@ -0,0 +1,42 @@ +package class_2022_06_3_week; + +// arr是一个可能包含重复元素的整数数组,我们将这个数组分割成几个“块”, +// 并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。 +// 我们最多能将数组分成多少块? +// 示例 1: +// 输入: arr = [5,4,3,2,1] +// 输出: 1 +// 解释: +// 将数组分成2块或者更多块,都无法得到所需的结果。 +// 例如,分成 [5, 4], [3, 2, 1] 的结果是 [4, 5, 1, 2, 3],这不是有序的数组。 +// 示例 2: +// 输入: arr = [2,1,3,4,4] +// 输出: 4 +// 解释: +// 我们可以把它分成两块,例如 [2, 1], [3, 4, 4]。 +// 然而,分成 [2, 1], [3], [4], [4] 可以得到最多的块数。 +// 测试链接 : https://leetcode.com/problems/max-chunks-to-make-sorted-ii/ +public class Code01_MaxChunksToMakeSortedII { + + public int maxChunksToSorted(int[] arr) { + int n = arr.length; + int[] mins = new int[n]; + // i ~ 最后位置上,最小值! + // 5 | 6... + // 17 | 18... + mins[n - 1] = arr[n - 1]; + for (int i = n - 2; i >= 0; i--) { + mins[i] = Math.min(arr[i], mins[i + 1]); + } + int ans = 1; + int max = arr[0]; + for (int i = 1; i < n; i++) { + if (max <= mins[i]) { + ans++; + } + max = Math.max(max, arr[i]); + } + return ans; + } + +} diff --git a/算法周更班/class_2022_06_3_week/Code02_SellingPiecesOfWood.java b/算法周更班/class_2022_06_3_week/Code02_SellingPiecesOfWood.java new file mode 100644 index 0000000..01a5126 --- /dev/null +++ b/算法周更班/class_2022_06_3_week/Code02_SellingPiecesOfWood.java @@ -0,0 +1,151 @@ +package class_2022_06_3_week; + +// 给你两个整数 m 和 n ,分别表示一块矩形木块的高和宽。 +// 同时给你一个二维整数数组 prices ,其中 prices[i] = [hi, wi, pricei]  +// 表示你可以以 pricei 元的价格卖一块高为 hi 宽为 wi 的矩形木块。 +// 每一次操作中,你必须按下述方式之一执行切割操作,以得到两块更小的矩形木块: +// 沿垂直方向按高度 完全 切割木块,或 +// 沿水平方向按宽度 完全 切割木块 +// 在将一块木块切成若干小木块后,你可以根据 prices 卖木块。 +// 你可以卖多块同样尺寸的木块。 +// 你不需要将所有小木块都卖出去。 +// 你 不能 旋转切好后木块的高和宽。 +// 请你返回切割一块大小为 m x n 的木块后,能得到的 最多 钱数。 +// 注意你可以切割木块任意次。 +// 测试链接 : https://leetcode.cn/problems/selling-pieces-of-wood/ +public class Code02_SellingPiecesOfWood { + + // [1, 3,10元] + // [3, 5, 7元] + // [2, 6, 5元] + // 100 * 100 + // values -> 100 * 100 + // values[1][3] = 10 + // values[3][5] = 7 + // values[2][6] = 5 + // values[5][5] = 0元 + // m * n这块木板,只能水平分割、垂直分割的情况下,能获得的最大总钱数是多少? + public static int zuo(int m, int n, int[][] values) { + // base case + if (m == 0 || n == 0) { + return 0; + } + // m > 0 & n > 0木块还有面积! + // 普遍分析 + // 可能性1:一刀也不切 + int ans = values[m][n]; // 0元 >0元 + // 接下来的一系列可能性:水平方向上,都去试一试 + for (int split = 1; split < m; split++) { + int up = zuo(split, n, values); + int down = zuo(m - split, n, values); + ans = Math.max(ans, up + down); + } + // 垂直方向上,都去试一试 + for (int split = 1; split < n; split++) { + int left = zuo(m, split, values); + int right = zuo(m, n - split, values); + ans = Math.max(ans, left + right); + } + return ans; + } + + // 递归尝试版本 + public static long sellingWood1(int m, int n, int[][] prices) { + // 单一报价 + long[][] values = new long[m + 1][n + 1]; + // 2 * 7 10元 + // 2 * 7 100元 + for (int[] p : prices) { + values[p[0]][p[1]] = Math.max(values[p[0]][p[1]], p[2]); + } + return f1(m, n, values); + } + + public static long f1(int m, int n, long[][] values) { + if (m == 0 || n == 0) { + return 0; + } + long ans = values[m][n]; + for (int split = 1; split < m; split++) { + ans = Math.max(ans, f1(split, n, values) + f1(m - split, n, values)); + } + for (int split = 1; split < n; split++) { + ans = Math.max(ans, f1(m, split, values) + f1(m, n - split, values)); + } + return ans; + } + + // 递归版本 + 记忆化搜索 + public static long sellingWood2(int m, int n, int[][] prices) { + long[][] values = new long[m + 1][n + 1]; + for (int[] p : prices) { + values[p[0]][p[1]] = Math.max(values[p[0]][p[1]], p[2]); + } + long[][] dp = new long[m + 1][n + 1]; + // dp[10][20] :没算过,-1 + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + dp[i][j] = -1; + } + } + return f2(m, n, values, dp); + } + + public static long f2(int m, int n, long[][] values, long[][] dp) { + if (m == 0 || n == 0) { + return 0; + } + if (dp[m][n] != -1) { + return dp[m][n]; + } + long ans = values[m][n]; + for (int split = 1; split < m; split++) { + ans = Math.max(ans, f2(split, n, values, dp) + f2(m - split, n, values, dp)); + } + for (int split = 1; split < n; split++) { + ans = Math.max(ans, f2(m, split, values, dp) + f2(m, n - split, values, dp)); + } + dp[m][n] = ans; + return ans; + } + + // 严格位置依赖的动态规划版本 + 优化 + // 优化1 : 递归的形式,改成迭代形式,课上讲了 + // 优化2 : prices中的单块收益直接填入dp表即可,如果有更好的分割方案,更新掉 + // 优化3 : 分割只需要枚举一半即可 + public static long sellingWood3(int m, int n, int[][] prices) { + // dp表! + long[][] dp = new long[m + 1][n + 1]; + for (int[] p : prices) { + // {3, 5, 100} + // 0 1 2 + // dp[3][5] = 100 + dp[p[0]][p[1]] = p[2]; + } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + // 垂直分割 + // i * j = 100 * 100 + // dp[100][1] + dp[100][99] + // dp[100][2] + dp[100][98] + // .. + for (int k = 1; k <= (j >> 1); k++) { + dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[i][j - k]); + } + // 水平分割 + // 100 * 100 + // 1) 1 * 100 + 99 * 100 + // 1) 2 * 100 + 98 * 100 + // i * j + // 1) 1 * j + (i - 1) * i; + // 2) 2 * j + (i - 2) * j; + // k) k * j + (i - k) * j; + for (int k = 1; k <= (i >> 1); k++) { + dp[i][j] = Math.max(dp[i][j], dp[k][j] + dp[i - k][j]); + } + } + } + return dp[m][n]; + } + +} diff --git a/算法周更班/class_2022_06_3_week/Code03_RangeModule1.java b/算法周更班/class_2022_06_3_week/Code03_RangeModule1.java new file mode 100644 index 0000000..1005200 --- /dev/null +++ b/算法周更班/class_2022_06_3_week/Code03_RangeModule1.java @@ -0,0 +1,196 @@ +package class_2022_06_3_week; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +// Range模块是跟踪数字范围的模块。 +// 设计一个数据结构来跟踪表示为 半开区间 的范围并查询它们。 +// 半开区间 [left, right) 表示所有 left <= x < right 的实数 x 。 +// 实现 RangeModule 类: +// RangeModule() 初始化数据结构的对象 +// void addRange(int left, int right) : +// 添加 半开区间 [left, right),跟踪该区间中的每个实数。 +// 添加与当前跟踪的数字部分重叠的区间时, +// 应当添加在区间 [left, right) 中尚未跟踪的任何数字到该区间中。 +// boolean queryRange(int left, int right) : +// 只有在当前正在跟踪区间 [left, right) 中的每一个实数时,才返回 true +// 否则返回 false 。 +// void removeRange(int left, int right) : +// 停止跟踪 半开区间 [left, right) 中当前正在跟踪的每个实数。 +// 测试连接 : https://leetcode.com/problems/range-module/ +public class Code03_RangeModule1 { + + class RangeModule { + // 某个区间的开头是key,结尾是value + // [3,100) + // key 3 value 100 + // [3,100) 45, 86 + // [3,17) [17,23) -> [3,23) + // 目标:不管怎么调用,一定要保证! + // map里表示的区间,能合并就合并,且没有交集 + TreeMap map; + + public RangeModule() { + map = new TreeMap<>(); + } + + // 加入一个区间 + public void addRange(int left, int right) { + // 无效区间直接返回 + if (right <= left) { + return; + } + // 有效区间! + // 当前要加入的区间是[left, right) + // 比如 : 当前区间[34, 76) + // 第一个if: + // 如果小于等于34的开头,和小于等于76的开头不存在 + // 那么说明,34之前没有区间,34到75也没有区间 + // 直接加入就可以了 + // 第二个if: + // 说明有小于等于34的开头,并且延伸的结尾大于等于34 + // 比如,之前有两个区间[30, 54), [70, 108) + // 有小于等于34的开头,就是30 + // 并且延伸的结尾大于等于34,就是54 + // 同时,看一下小于等于76的开头,就是70,延伸到108,所以一起合并 + // 合并成[30, 108] + // 再比如,之前有两个区间[30, 54), [70, 72) + // 有小于等于34的开头,就是30,并且延伸的结尾大于等于34,就是54 + // 同时,看一下小于等于76的开头,就是70,延伸到72,所以一起合并 + // 合并成[30, 76] + // 即: map.put(start, Math.max(map.get(end), right)); + // 第三个分支,最后的else: + // 说明是前两个if的反面: + // [34, 76) + // 第一个if的反面: 小于等于34的开头,和小于等于76的开头,不是都不存在 + // 分成以下几种情况 + // 1) 小于等于34的开头存在,小于等于76的开头不存在,这是不可能的 + // 2) 小于等于34的开头不存在,小于等于76的开头存在,这是可能的 + // 3) 小于等于34的开头存在,小于等于76的开头也存在,这是可能的 + // 于是,第一个if的反面,分成了如下两种情况 + // a) 小于等于34的开头不存在,小于等于76的开头存在 + // b) 小于等于34的开头存在,小于等于76的开头也存在 + // 再看第二个if的反面 + // [34, 76) + // 第二个if是,小于等于34的开头存在,并且,结尾延伸到了34以右 + // 所以第二个if的反面是: + // c) 小于等于34的开头不存在 + // d) 小于等于34的开头存在,但是结尾没有延伸到34 + // 那么a,b,c,d结合就有四种可能 + // 1) a + c,小于等于34的开头不存在,小于等于76的开头存在 + // 2) a + d,不可能 + // 3) b + c,不可能 + // 4) b + d,小于等于34的开头存在,但是结尾没有延伸到34,小于等于76的开头也存在 + // 只有1)、4)是可能的 + // 如果是1),那么一定会新出现一个开头为34的区间,但是结尾在哪? + // 比如当前区间[34, 76),情况1)是:小于等于34的开头不存在,小于等于76的开头存在 + // 比如,之前有个区间是[56,72),小于等于76的开头是56 + // 这两个区间合并成[34, 76) + // 在比如,之前有个区间是[56,108),小于等于76的开头是108 + // 这两个区间合并成[34, 108) + // 即:map.put(left, Math.max(map.get(end), right)); + // 如果是4),小于等于34的开头存在,但是结尾没有延伸到34,小于等于76的开头也存在 + // 这种情况下,虽然小于等于34的开头存在, + // 但是结尾没有延伸到34,那么就可以不管之前的区间啊,反正不会有影响的。 + // 于是处理和情况1)是一样的 + // 即:map.put(left, Math.max(map.get(end), right)); + // 这就是接下来三个逻辑分支的处理 + // [5, 9) .... + // [4, 6) [7, 100) + Integer start = map.floorKey(left); + Integer end = map.floorKey(right); + // [34, 76) + // 都空 + // 反面1) <= 34 没有,<= 76开头,有 + // 反面2) <= 34 有, <= 76开头,没有 ,不可能 X + // 反面3) <= 34 有, <= 76开头,有 + if (start == null && end == null) { + map.put(left, right); + } else if (start != null && map.get(start) >= left) { + // [34, 76) + // 2if : <= 34 开头 ,有,且!,结尾在34以右 + // 反面1 ) <= 34 开头 ,没有 + // 反面2 ) <= 34 开头 ,有, 结尾没在34以右 + map.put(start, Math.max(map.get(end), right)); + } else { + // 1) a + c,小于等于34的开头不存在,小于等于76的开头存在 + // 4) b + d,小于等于34的开头存在,但是结尾没有延伸到34,小于等于76的开头也存在 + // [1, 2) [5, 6) + // [1,2 ) [5, 6) + map.put(left, Math.max(map.get(end), right)); + } + // 上面做了合并,但是要注意可能要清理一些多余的区间 + // 比如,当前区间[34, 76) + // 之前的区间是[100, 840) + // 这种情况,中了分支一,在合并之后,区间为: + // [34, 76),[100, 840) + // 所以移除掉所有(34,76]的开头 + // 只剩下了[34, 76),[100, 840) + // 再比如,当前区间[34, 76) + // 之前的区间是[30, 54)、[55, 60)、[62, 65)、[70, 84) + // 这种情况,中了分支二,在合并之后,区间为: + // [30, 84)、[55, 60)、[62, 65)、[70, 84) + // 所以移除掉所有(34, 76]的开头区间 + // 只剩下了[30, 84) + // (left, right] + // 再比如,当前区间[34, 76) + // [70, 108) 来的是[34, 76) + // [34, 108) (34, 76]删掉 + // 再比如,当前区间[34, 76) + // [3,17) [73,108) 来的是[34, 76) + // [3,17) [34,108) [73,108) (34, 76]删掉 + Map subMap = map.subMap(left, false, right, true); + Set set = new HashSet<>(subMap.keySet()); + map.keySet().removeAll(set); + } + + public boolean queryRange(int left, int right) { + // [34, 76) 整体被你的结构,有没有包含! + // <=34 开头都没! + Integer start = map.floorKey(left); + if (start == null) + return false; + // [34, 76) 整体被你的结构,有没有包含! + // <=34 开头有![17,~ 60) [60 ~ 76) + return map.get(start) >= right; + } + + public void removeRange(int left, int right) { + if (right <= left) { + return; + } + Integer start = map.floorKey(left); + Integer end = map.floorKey(right); + if (end != null && map.get(end) > right) { + map.put(right, map.get(end)); + } + if (start != null && map.get(start) > left) { + map.put(start, left); + } + Map subMap = map.subMap(left, true, right, false); + Set set = new HashSet<>(subMap.keySet()); + map.keySet().removeAll(set); + } + } + + public static void main(String[] args) { + TreeMap map = new TreeMap<>(); + map.put(6, "我是6"); + map.put(3, "我是3"); + map.put(9, "我是9"); + map.put(5, "我是9"); + map.put(4, "我是9"); + // 3 4 5 6 9 + // [4~6) -> 4, 5,6 + + Map subMap = map.subMap(4, true, 6, false); + + for (int key : subMap.keySet()) { + System.out.println(key); + } + + } + +} diff --git a/算法周更班/class_2022_06_3_week/Code03_RangeModule2.java b/算法周更班/class_2022_06_3_week/Code03_RangeModule2.java new file mode 100644 index 0000000..c57766c --- /dev/null +++ b/算法周更班/class_2022_06_3_week/Code03_RangeModule2.java @@ -0,0 +1,112 @@ +package class_2022_06_3_week; + +// 测试连接 : https://leetcode.com/problems/range-module/ +// 动态开点线段树的解 +public class Code03_RangeModule2 { + + // 只提交这个类,可以直接通过 + class RangeModule { + + DynamicSegmentTree dst; + + public RangeModule() { + dst = new DynamicSegmentTree(1000000001); + } + + public void addRange(int left, int right) { + dst.update(left, right - 1, 1); + } + + public boolean queryRange(int left, int right) { + return dst.query(left, right - 1) == right - left; + } + + public void removeRange(int left, int right) { + dst.update(left, right - 1, 0); + } + + class Node { + public int sum; + public int change; + public boolean update; + public Node left; + public Node right; + } + + class DynamicSegmentTree { + public Node root; + public int size; + + public DynamicSegmentTree(int max) { + root = new Node(); + size = max; + } + + private void pushUp(Node c) { + c.sum = c.left.sum + c.right.sum; + } + + private void pushDown(Node p, int ln, int rn) { + if (p.left == null) { + p.left = new Node(); + } + if (p.right == null) { + p.right = new Node(); + } + if (p.update) { + p.left.update = true; + p.right.update = true; + p.left.change = p.change; + p.right.change = p.change; + p.left.sum = p.change * ln; + p.right.sum = p.change * rn; + p.update = false; + } + } + + public void update(int s, int e, int v) { + update(root, 1, size, s, e, v); + } + + private void update(Node c, int l, int r, int s, int e, int v) { + if (s <= l && r <= e) { + c.update = true; + c.change = v; + c.sum = v * (r - l + 1); + } else { + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + if (s <= mid) { + update(c.left, l, mid, s, e, v); + } + if (e > mid) { + update(c.right, mid + 1, r, s, e, v); + } + pushUp(c); + } + } + + public int query(int s, int e) { + return query(root, 1, size, s, e); + } + + private int query(Node c, int l, int r, int s, int e) { + if (s <= l && r <= e) { + return c.sum; + } + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + int ans = 0; + if (s <= mid) { + ans += query(c.left, l, mid, s, e); + } + if (e > mid) { + ans += query(c.right, mid + 1, r, s, e); + } + return ans; + } + + } + } + +} diff --git a/算法周更班/class_2022_06_3_week/Code04_StarNumber.java b/算法周更班/class_2022_06_3_week/Code04_StarNumber.java new file mode 100644 index 0000000..e3ae402 --- /dev/null +++ b/算法周更班/class_2022_06_3_week/Code04_StarNumber.java @@ -0,0 +1,87 @@ +package class_2022_06_3_week; + +// 一个字符串s,表示仓库的墙 与 货物,其中'|'表示墙,'*'表示货物。 +// 给定一个起始下标start和一个终止下标end, +// 找出子串中 被墙包裹的货物 数量 +// 比如 +// s = "|**|**|*" +// start = 1, end = 7 +// start和end截出的子串是 "**|**|*" +// 被 '|'包裹的 '*' 有两个,所以返回2 +// 现在给定一系列的start,startIndices[],和对应一系列的end ,endIndices[] +// 返回每一对[start,end]的截出来的货物数量 +// 数据规模: +// 字符串s长度<=10^5 +// startIndices长度 == endIndices长度 <=10^5 +public class Code04_StarNumber { + + // 解法 + // 生成每个位置左边离这个位置最近的'|'在哪,left数组 + // 生成每个位置右边离这个位置最近的'|'在哪,right数组 + // 生成每个位置左边一共有几个'*',sum数组 + // 比如,s如下,第一行是字符串,第二行是下标 + // | * * | * * | * + // 0 1 2 3 4 5 6 7 + // 生成的left数组,第一行是值,第二行是下标 + // 0 0 0 3 3 3 6 6 + // 0 1 2 3 4 5 6 7 + // 生成的right数组,第一行是值,第二行是下标 + // 0 3 3 3 6 6 6 -1 + // 0 1 2 3 4 5 6 7 + // 生成的sum数组,第一行是值,第二行是下标 + // 0 1 2 2 3 4 4 5 + // 0 1 2 3 4 5 6 7 + // 比如,start = 1, end = 7 + // 找到start右边最近的'|'在哪,根据right直接查到,在3位置 + // 找到end左边最近的'|'在哪,根据left直接查到,在6位置 + // 1~7范围上被墙包裹*的个数 = 3~6范围上被墙包裹*的个数 + // 3~6范围上被墙包裹*的个数 = sum[6] - sum[2] = 2 + public static int[] number(String s, int[] starts, int[] ends) { + char[] str = s.toCharArray(); + int n = str.length; + int[] left = new int[n]; + int[] right = new int[n]; + int[] sum = new int[n]; + + int pre = -1; + int num = 0; + for (int i = 0; i < n; i++) { + pre = str[i] == '|' ? i : pre; + num += str[i] == '*' ? 1 : 0; + left[i] = pre; + sum[i] = num; + } + pre = -1; + for (int i = n - 1; i >= 0; i--) { + pre = str[i] == '|' ? i : pre; + right[i] = pre; + } + + int m = starts.length; + int[] ans = new int[m]; + for (int i = 0; i < m; i++) { + ans[i] = stars(starts[i], ends[i], left, right, sum); + } + return ans; + } + + public static int stars(int start, int end, int[] l, int[] r, int[] s) { + int left = r[start]; + int right = l[end]; + if (left == -1 || right == -1 || (left >= right)) { + return 0; + } + return left == 0 ? s[right] : (s[right] - s[left - 1]); + } + + public static void main(String[] args) { + String s = "|**|**|*"; + int[] a = new int[] { 0, 1, 3, 4 }; + int[] b = new int[] { 7, 7, 6, 5 }; + int[] arr = number(s, a, b); + for (int ans : arr) { + System.out.println(ans); + } + } + +} diff --git a/算法周更班/class_2022_06_4_week/Code01_MinimumWindowSubsequence.java b/算法周更班/class_2022_06_4_week/Code01_MinimumWindowSubsequence.java new file mode 100644 index 0000000..4179c1f --- /dev/null +++ b/算法周更班/class_2022_06_4_week/Code01_MinimumWindowSubsequence.java @@ -0,0 +1,267 @@ +package class_2022_06_4_week; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.TreeSet; + +// 给定字符串 S and T,找出 S 中最短的(连续)子串 W ,使得 T 是 W 的 子序列 。 +// 如果 S 中没有窗口可以包含 T 中的所有字符,返回空字符串 ""。 +// 如果有不止一个最短长度的窗口,返回开始位置最靠左的那个。 +// 示例 1: +// 输入: +// S = "abcdebdde", T = "bde" +// 输出:"bcde" +// 解释: +// "bcde" 是答案,因为它在相同长度的字符串 "bdde" 出现之前。 +// "deb" 不是一个更短的答案,因为在窗口中必须按顺序出现 T 中的元素。 +// 测试链接 : https://leetcode.cn/problems/minimum-window-subsequence/ +public class Code01_MinimumWindowSubsequence { + +// public static int minLenContainsT1(char[] s, char[] t) { +// int n = s.length; +// int m = t.length; +// +// // s = xya...a... +// // t = abc +// int min = Integer.MAX_VALUE; +// for (int i = 0; i < n; i++) { +// if(s[i] == t[0]) { +// int findEnd = findEnd(s, t, i); +// if(findEnd != -1) { // 找到了!s[i...findEnd]答案 +// min = Math.min(min, findEnd - i + 1); +// } +// } +// } +// return min; +// } +// +// // s = a.....? +// // si +// // t = abcd +// // 0 +// // 最后的?,s[si...?] 整出来t的整体 +// // 如果从si出发,就没有t的整体,返回-1 +// public static int findEnd(char[] s, char[] t, int si) { +// +// } + + public static void main(String[] args) { + String s = "xxaxxbxxcxx"; + // 0 8 + String t = "abc"; + System.out.println(minLen(s, t)); + } + + public static int minLen(String str, String target) { + char[] s = str.toCharArray(); + char[] t = target.toCharArray(); + int len = Integer.MAX_VALUE; + for (int i = 0; i < s.length; i++) { + // 0 > t + // 1 > t + // 2 > t + int end = zuo(s, t, i, 0); + if (end != Integer.MAX_VALUE) { + int cur = end - i; + len = Math.min(len, cur); + } + } + return len; + } + + // s[si.....] + // t[ti....] + // 把t的整体,都配出来,s在哪能尽早结束!的下个位置 + // s = x x a x x b x x c x a b c + // 0 1 2 3 4 5 6 7 8 9 10 11 12 + // t = a b c + // 0 1 2 + // s[1...] t[0...] + // s[4...] t[1...] 8 + // s[4...] t[0...] 12 + public static int zuo(char[] s, char[] t, int si, int ti) { + if (ti == t.length) { // 配完了! + return si; + } + // ti < t.length; + if (si == s.length) { + return Integer.MAX_VALUE; + } + // 都有字符 + // 可能性1:根本不让s[si]去消化掉t[ti] + int p1 = zuo(s, t, si + 1, ti); + // 可能性2:让s[si]去消化掉t[ti] + int p2 = Integer.MAX_VALUE; + if (s[si] == t[ti]) { + // si ti + p2 = zuo(s, t, si + 1, ti + 1); + } + return Math.min(p1, p2); + } + + public String minWindow1(String s, String t) { + char[] str = s.toCharArray(); + char[] target = t.toCharArray(); + int n = str.length; + // key : 字符! value:有序表! + HashMap> map = new HashMap<>(); + for (char cha : target) { + map.put(cha, new TreeSet<>()); + } + for (int i = 0; i < n; i++) { + if (map.containsKey(str[i])) { + map.get(str[i]).add(i); + } + } + int ansLen = Integer.MAX_VALUE; + int l = -1; + int r = -1; + for (int i = 0; i < n; i++) { + if (str[i] == target[0]) { + int right = right1(str, i, target, map); + if (right != -1 && (right - i) < ansLen) { + ansLen = right - i; + l = i; + r = right; + } + } + } + return l == -1 ? "" : s.substring(l, r); + } + + public static int right1(char[] str, int si, char[] target, HashMap> map) { + int ti = 0; + while (ti != target.length) { + if (si == str.length) { + return -1; + } + if (str[si] == target[ti]) { + si++; + ti++; + } else { + Integer next = map.get(target[ti]).ceiling(si); + if (next == null) { + return -1; + } else { + si = next; + } + } + + } + return si; + } + + public String minWindow2(String s, String t) { + char[] str = s.toCharArray(); + char[] target = t.toCharArray(); + int n = str.length; + int[] last = new int[26]; + int[][] near = new int[n][26]; + for (int i = 0; i < n; i++) { + Arrays.fill(near[i], -1); + } + for (int i = 0; i < n; i++) { + int cha = str[i] - 'a'; + for (int j = last[cha]; j < i; j++) { + near[j][cha] = i; + } + last[cha] = i; + } + int ansLen = Integer.MAX_VALUE; + int l = -1; + int r = -1; + for (int i = 0; i < n; i++) { + if (str[i] == target[0]) { + int right = right2(str, i, target, near); + if (right != -1 && (right - i) < ansLen) { + ansLen = right - i; + l = i; + r = right; + } + } + } + return l == -1 ? "" : s.substring(l, r); + } + + public static int right2(char[] str, int si, char[] target, int[][] near) { + int ti = 0; + while (ti != target.length) { + if (si == str.length) { + return -1; + } + if (str[si] == target[ti]) { + si++; + ti++; + } else { + si = near[si][target[ti] - 'a']; + } + if (si == -1) { + return -1; + } + } + return si; + } + + public String minWindow3(String s, String t) { + char[] str = s.toCharArray(); + char[] target = t.toCharArray(); + int len = Integer.MAX_VALUE; + int l = -1; + int r = -1; + for (int si = 0; si < str.length; si++) { + int right = process(str, target, si, 0); + if (right != Integer.MAX_VALUE && right - si < len) { + len = right - si; + l = si; + r = right; + } + } + return l == -1 ? "" : s.substring(l, r); + } + + public static int process(char[] str, char[] target, int si, int ti) { + if (ti == target.length) { + return si; + } + if (si == str.length) { + return Integer.MAX_VALUE; + } + int r1 = process(str, target, si + 1, ti); + int r2 = str[si] == target[ti] ? process(str, target, si + 1, ti + 1) : Integer.MAX_VALUE; + return Math.min(r1, r2); + } + + public String minWindow4(String s, String t) { + char[] str = s.toCharArray(); + char[] target = t.toCharArray(); + int n = str.length; + int m = target.length; + int[][] dp = new int[n + 1][m + 1]; + for (int si = 0; si <= n; si++) { + dp[si][m] = si; + } + for (int ti = 0; ti < m; ti++) { + dp[n][ti] = Integer.MAX_VALUE; + } + for (int si = n - 1; si >= 0; si--) { + for (int ti = m - 1; ti >= 0; ti--) { + int r1 = dp[si + 1][ti]; + int r2 = str[si] == target[ti] ? dp[si + 1][ti + 1] : Integer.MAX_VALUE; + dp[si][ti] = Math.min(r1, r2); + } + } + int len = Integer.MAX_VALUE; + int l = -1; + int r = -1; + for (int si = 0; si < str.length; si++) { + int right = dp[si][0]; + if (right != Integer.MAX_VALUE && right - si < len) { + len = dp[si][0] - si; + l = si; + r = right; + } + } + return l == -1 ? "" : s.substring(l, r); + } + +} diff --git a/算法周更班/class_2022_06_4_week/Code02_StackNotSplit.java b/算法周更班/class_2022_06_4_week/Code02_StackNotSplit.java new file mode 100644 index 0000000..2d8a818 --- /dev/null +++ b/算法周更班/class_2022_06_4_week/Code02_StackNotSplit.java @@ -0,0 +1,177 @@ +package class_2022_06_4_week; + +import java.util.ArrayList; +import java.util.Stack; + +// 来自微软 +// 请设计一种叫做“栈的管理器”的结构,实现如下6个功能 +// 1) void createNewStack() : 可以在该结构中生成一个栈结构,编号从0开始 +// 2) void push(int num, int stackIndex) : 将编号为stackIndex的栈里,压入num +// 3) int pop(int stackIndex) : 从编号为stackIndex的栈里,弹出栈顶返回 +// 4) int peek(int stackIndex) :从编号为stackIndex的栈里,返回栈顶但是不弹出 +// 5) boolean isEmpty(int statckIndex):返回编号为stackIndex的栈是否为空 +// 6) int stackSize() : 返回一共生成了多少个栈 +// 要求:不管用户调用多少次上面的方法,只使用有限几个动态数组(常数个),完成代码实现 +public class Code02_StackNotSplit { + + public static class Stacks1 { + + public ArrayList> stacks; + + public Stacks1() { + stacks = new ArrayList<>(); + } + + public int stackSize() { + return stacks.size(); + } + + public void createNewStack() { + stacks.add(new Stack<>()); + } + + public void push(int num, int stackIndex) { + stacks.get(stackIndex).push(num); + } + + public int pop(int stackIndex) { + return stacks.get(stackIndex).pop(); + } + + public boolean isEmpty(int statckIndex) { + return stacks.get(statckIndex).isEmpty(); + } + + public int peek(int stackIndex) { + return stacks.get(stackIndex).peek(); + } + + } + + public static class Stacks2 { + public ArrayList heads; + public ArrayList values; + public ArrayList lasts; + public ArrayList frees; + // occupySize : 值数组用到哪了? + public int occupySize; + // freeSize : 垃圾区的大小(最先用!) + public int freeSize; + + public Stacks2() { + heads = new ArrayList<>(); + values = new ArrayList<>(); + lasts = new ArrayList<>(); + frees = new ArrayList<>(); + occupySize = 0; + freeSize = 0; + } + + public int stackSize() { + return heads.size(); + } + + public void createNewStack() { + heads.add(-1); + } + + public void push(int num, int stackIndex) { + // 老头部出来,因为新头部往前跳,要能找到老头部 + int headIndex = heads.get(stackIndex); + if (freeSize == 0) { // 垃圾区没有空闲的位置 + // value , occupySize, occupySize++ + heads.set(stackIndex, occupySize++); + values.add(num); + lasts.add(headIndex); + } else { // 垃圾区有空闲空间 + // 新的数 -> freeIndex + int freeIndex = frees.get(--freeSize); + heads.set(stackIndex, freeIndex); + values.set(freeIndex, num); + lasts.set(freeIndex, headIndex); + } + } + + public int pop(int stackIndex) { + // 当前的头部的位置 + int headIndex = heads.get(stackIndex); + // values -> 当前的头部的位置 -> 要返回的数! + int ans = values.get(headIndex); + // 当前头要弹出了!接下来的头是谁? + int newHeadIndex = lasts.get(headIndex); + heads.set(stackIndex, newHeadIndex); + // 垃圾区要接受,当前的头部的位置 + // frees动态数组! + if (freeSize >= frees.size()) { + frees.add(headIndex); + freeSize++; + } else { + frees.set(freeSize++, headIndex); + } + return ans; + } + + public boolean isEmpty(int statckIndex) { + return heads.get(statckIndex) == -1; + } + + public int peek(int stackIndex) { + return values.get(heads.get(stackIndex)); + } + + } + + public static void main(String[] args) { + int V = 10000; + int testTime = 20000; + System.out.println("测试开始"); + Stacks1 stack1 = new Stacks1(); + Stacks2 stack2 = new Stacks2(); + for (int i = 0; i < testTime; i++) { + double decide = Math.random(); + if (decide < 0.25) { + stack1.createNewStack(); + stack2.createNewStack(); + } else { + int stackSize1 = stack1.stackSize(); + int stackSize2 = stack2.stackSize(); + if (stackSize1 != stackSize2) { + System.out.println("栈的数量不一致!"); + break; + } + if (stackSize1 > 0) { + int stackIndex = (int) (Math.random() * stackSize1); + if (decide < 0.5) { + int num = (int) (Math.random() * V); + stack1.push(num, stackIndex); + stack2.push(num, stackIndex); + } else if (decide < 0.75) { + if (stack1.isEmpty(stackIndex) != stack2.isEmpty(stackIndex)) { + System.out.println(stackIndex + "号栈的是否为空不一致!"); + break; + } + if (!stack1.isEmpty(stackIndex)) { + if (stack1.pop(stackIndex) != stack2.pop(stackIndex)) { + System.out.println(stackIndex + "号栈的弹出数据不一致!"); + break; + } + } + } else { + if (stack1.isEmpty(stackIndex) != stack2.isEmpty(stackIndex)) { + System.out.println(stackIndex + "号栈的是否为空不一致!"); + break; + } + if (!stack1.isEmpty(stackIndex)) { + if (stack1.peek(stackIndex) != stack2.peek(stackIndex)) { + System.out.println(stackIndex + "号栈的栈顶数据不一致!"); + break; + } + } + } + } + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_06_4_week/Code03_MaxAnimalNumber.java b/算法周更班/class_2022_06_4_week/Code03_MaxAnimalNumber.java new file mode 100644 index 0000000..defb65d --- /dev/null +++ b/算法周更班/class_2022_06_4_week/Code03_MaxAnimalNumber.java @@ -0,0 +1,117 @@ +package class_2022_06_4_week; + +// 有n个动物重量分别是a1、a2、a3.....an, +// 这群动物一起玩叠罗汉游戏, +// 规定从左往右选择动物,每只动物左边动物的总重量不能超过自己的重量 +// 返回最多能选多少个动物,求一个高效的算法。 +// 比如有7个动物,从左往右重量依次为:1,3,5,7,9,11,21 +// 则最多能选5个动物:1,3,5,9,21 +// 注意本题给的例子是有序的,但是实际给定的动物数组,可能是无序的, +// 要求从左往右选动物,且不能打乱原始数组 +public class Code03_MaxAnimalNumber { + + // 普通动态规划 + // 非常一般的方法 + // 来自背包的思路 + public static int maxAnimals1(int[] arr) { + int sum = 0; + for (int num : arr) { + sum += num; + } + int[][] dp = new int[arr.length][sum + 1]; + for (int i = 0; i < arr.length; i++) { + for (int j = 0; j <= sum; j++) { + dp[i][j] = -1; + } + } + return process1(arr, 0, 0, dp); + } + + public static int process1(int[] arr, int index, int pre, int[][] dp) { + if (index == arr.length) { + return 0; + } + if (dp[index][pre] != -1) { + return dp[index][pre]; + } + int p1 = process1(arr, index + 1, pre, dp); + int p2 = 0; + if (arr[index] >= pre) { + p2 = 1 + process1(arr, index + 1, pre + arr[index], dp); + } + int ans = Math.max(p1, p2); + dp[index][pre] = ans; + return ans; + } + + // 最优解 + // 如果arr长度为N,时间复杂度O(N*logN) + public static int maxAnimals2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + // ends数组 + int[] ends = new int[arr.length + 1]; + ends[0] = 0; + int endsSize = 1; + int max = 1; + for (int i = 0; i < arr.length; i++) { + int l = 0; + int r = endsSize - 1; + int m = 0; + int find = 0; + while (l <= r) { + m = (l + r) / 2; + if (ends[m] <= arr[i]) { + find = m; + l = m + 1; + } else { + r = m - 1; + } + } + if (find == endsSize - 1) { + ends[endsSize] = ends[endsSize - 1] + arr[i]; + endsSize++; + } else { + if (ends[find + 1] > ends[find] + arr[i]) { + ends[find + 1] = ends[find] + arr[i]; + } + } + max = Math.max(max, find + 1); + } + return max; + } + + public static int[] randomArray(int n, int v) { + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + public static void main(String[] args) { + int N = 100; + int V = 1000; + int testTime = 2000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr = randomArray(n, V); + int ans1 = maxAnimals1(arr); + int ans2 = maxAnimals2(arr); + if (ans1 != ans2) { + System.out.println("出错了"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_06_4_week/Code04_MinimizeMaxDistanceToGasStation.java b/算法周更班/class_2022_06_4_week/Code04_MinimizeMaxDistanceToGasStation.java new file mode 100644 index 0000000..ed5e52e --- /dev/null +++ b/算法周更班/class_2022_06_4_week/Code04_MinimizeMaxDistanceToGasStation.java @@ -0,0 +1,45 @@ +package class_2022_06_4_week; + +// 整数数组 stations 表示 水平数轴 上各个加油站的位置。给你一个整数 k 。 +// 请你在数轴上增设 k 个加油站, +// 新增加油站可以位于 水平数轴 上的任意位置,而不必放在整数位置上。 +// 设 penalty() 是:增设 k 个新加油站后,相邻 两个加油站间的最大距离。 +// 请你返回 penalty() 可能的最小值。与实际答案误差在 10-6 范围内的答案将被视作正确答案。 +// 测试链接 : https://leetcode.cn/problems/minimize-max-distance-to-gas-station/ +public class Code04_MinimizeMaxDistanceToGasStation { + + public static double minmaxGasDist(int[] stations, int K) { + // 精度 + double accuracy = 0.0000001D; + double l = 0; + double r = 100000000D; + double m = 0; + double ans = 0; + while (r - l > accuracy) { + m = (l + r) / 2; + if (ok(m, stations, K)) { + r = m; + ans = m; + } else { + l = m; + } + } + return ans; + } + + // int[] stations : 所有加油站的分布情况! + // double limit : 强制要求,相邻加油站的距离,不能超过limit + // int K : 一共可以使用的加油站数量! + // 所有加油站的分布情况, 相邻加油站的距离, 共可以使用的加油站数量, 能不能做到! + public static boolean ok(double limit, int[] stations, int K) { + int used = 0; + for (int i = 1; i < stations.length; i++) { + used += (int) ((stations[i] - stations[i - 1]) / limit); + if (used > K) { + return false; + } + } + return true; + } + +} diff --git a/算法周更班/class_2022_07_1_week/Code01_WindPrevent.java b/算法周更班/class_2022_07_1_week/Code01_WindPrevent.java new file mode 100644 index 0000000..9e9fb7d --- /dev/null +++ b/算法周更班/class_2022_07_1_week/Code01_WindPrevent.java @@ -0,0 +1,149 @@ +package class_2022_07_1_week; + +// 来自真实笔试 +// 给定一个二维数组matrix,数组中的每个元素代表一棵树的高度。 +// 你可以选定连续的若干行组成防风带,防风带每一列的防风高度为这一列的最大值 +// 防风带整体的防风高度为,所有列防风高度的最小值。 +// 比如,假设选定如下三行 +// 1 5 4 +// 7 2 6 +// 2 3 4 +// 1、7、2的列,防风高度为7 +// 5、2、3的列,防风高度为5 +// 4、6、4的列,防风高度为6 +// 防风带整体的防风高度为5,是7、5、6中的最小值 +// 给定一个正数k,k <= matrix的行数,表示可以取连续的k行,这k行一起防风。 +// 求防风带整体的防风高度最大值 +public class Code01_WindPrevent { + + public static int bestHeight1(int[][] matrix, int k) { + int n = matrix.length; + int m = matrix[0].length; + int ans = 0; + for (int startRow = 0; startRow < n; startRow++) { + int bottleNeck = Integer.MAX_VALUE; + for (int col = 0; col < m; col++) { + int height = 0; + for (int endRow = startRow; endRow < n && (endRow - startRow + 1 <= k); endRow++) { + height = Math.max(height, matrix[endRow][col]); + } + bottleNeck = Math.min(bottleNeck, height); + } + ans = Math.max(ans, bottleNeck); + } + return ans; + } + +// public static class WindowManager { +// +// // 建立出m个窗口! +// public WindowManager(int m) { +// +// } +// +// public void addRow(int[][] matrix, int row) { +// +// } +// +// public void deleteRow(int[][] matrix, int row) { +// +// } +// +// public int getAllWindowMaxMin() { +// return 100; +// } +// +// } +// +// public static int bestWindHeight(int[][] matrix, int k) { +// int n = matrix.length; +// int m = matrix[0].length; +// k = Math.min(k, n); +// WindowManager windowManager = new WindowManager(m); +// for (int i = 0; i < k - 1; i++) { +// windowManager.addRow(matrix, i); +// } +// int ans = 0; +// for (int i = k - 1; i < n; i++) { +// windowManager.addRow(matrix, i); +// int cur = windowManager.getAllWindowMaxMin(); +// ans = Math.max(ans, cur); +// windowManager.deleteRow(matrix, i - k + 1); +// } +// return ans; +// } + + public static int bestHeight2(int[][] matrix, int k) { + int n = matrix.length; + int m = matrix[0].length; + int[][] windowMaxs = new int[m][n]; + int[][] windowLR = new int[m][2]; + for (int i = 0; i < k; i++) { + addRow(matrix, m, i, windowMaxs, windowLR); + } + int ans = bottleNeck(matrix, m, windowMaxs, windowLR); + for (int i = k; i < n; i++) { + addRow(matrix, m, i, windowMaxs, windowLR); + deleteRow(m, i - k, windowMaxs, windowLR); + ans = Math.max(ans, bottleNeck(matrix, m, windowMaxs, windowLR)); + } + return ans; + } + + public static void addRow(int[][] matrix, int m, int row, int[][] windowMaxs, int[][] windowLR) { + for (int col = 0; col < m; col++) { + while (windowLR[col][0] != windowLR[col][1] + && matrix[windowMaxs[col][windowLR[col][1] - 1]][col] <= matrix[row][col]) { + windowLR[col][1]--; + } + windowMaxs[col][windowLR[col][1]++] = row; + } + } + + public static void deleteRow(int m, int row, int[][] windowMaxs, int[][] windowLR) { + for (int col = 0; col < m; col++) { + if (windowMaxs[col][windowLR[col][0]] == row) { + windowLR[col][0]++; + } + } + } + + public static int bottleNeck(int[][] matrix, int m, int[][] windowMaxs, int[][] windowLR) { + int ans = Integer.MAX_VALUE; + for (int col = 0; col < m; col++) { + ans = Math.min(ans, matrix[windowMaxs[col][windowLR[col][0]]][col]); + } + return ans; + } + + public static int[][] generateMatrix(int n, int m, int v) { + int[][] matrix = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + matrix[i][j] = (int) (Math.random() * v) + 1; + } + } + return matrix; + } + + public static void main(String[] args) { + int nMax = 10; + int mMax = 10; + int vMax = 50; + int testTimes = 1000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * nMax) + 1; + int m = (int) (Math.random() * mMax) + 1; + int[][] matrix = generateMatrix(n, m, vMax); + int k = (int) (Math.random() * n) + 1; + int ans1 = bestHeight1(matrix, k); + int ans2 = bestHeight2(matrix, k); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_07_1_week/Code02_MinimumScoreAfterRemovalsOnATree.java b/算法周更班/class_2022_07_1_week/Code02_MinimumScoreAfterRemovalsOnATree.java new file mode 100644 index 0000000..d092d6b --- /dev/null +++ b/算法周更班/class_2022_07_1_week/Code02_MinimumScoreAfterRemovalsOnATree.java @@ -0,0 +1,136 @@ +package class_2022_07_1_week; + +import java.util.ArrayList; + +// 给定一个棵树 +// 树上每个节点都有自己的值,记录在数组nums里 +// 比如nums[4] = 10,表示4号点的值是10 +// 给定树上的每一条边,记录在二维数组edges里 +// 比如edges[8] = {4, 9}表示4和9之间有一条无向边 +// 可以保证输入一定是一棵树,只不过边是无向边 +// 那么我们知道,断掉任意两条边,都可以把整棵树分成3个部分。 +// 假设是三个部分为a、b、c +// a部分的值是:a部分所有点的值异或起来 +// b部分的值是:b部分所有点的值异或起来 +// c部分的值是:c部分所有点的值异或起来 +// 请问怎么分割,能让最终的:三个部分中最大的异或值 - 三个部分中最小的异或值,最小 +// 返回这个最小的差值 +// 测试链接 : https://leetcode.cn/problems/minimum-score-after-removals-on-a-tree/ +public class Code02_MinimumScoreAfterRemovalsOnATree { + +// public static int minDiff(int[] nums, int[][] edges) { +// int ans = Integer.MAX_VALUE; +// // 计算量,不能超过10的8次方! +// // 体系学习班,根据数据量猜解法 +// // 边 1000 边^平方 : 10的6次方 +// for (int i = 0; i < edges.length; i++) { +// for (int j = i + 1; j < edges.length; j++) { +// // 在删掉i号边,和删掉j号边的情况下 +// // 一定会有三个部分! +// // 请告诉我,三个部分 max(异或和) - min(异或和) 差值是多少? +// // O(1) +// int curDiff = ????? ; +// ans = Math.min(ans, curDiff); +// } +// } +// return ans; +// } + + public static int cnt; + + public static int minimumScore(int[] nums, int[][] edges) { + int n = nums.length; + // 先建立图 + ArrayList> graph = new ArrayList<>(); + // 4个点,0、1、2、3 + // 0 : {} + // 1 : {} + // 2 : {} + // 3 : {} + for (int i = 0; i < n; i++) { + graph.add(new ArrayList<>()); + } + for (int[] edge : edges) { + // a,b + // graph.get(a).add(b); + // graph.get(b).add(a); + graph.get(edge[0]).add(edge[1]); + graph.get(edge[1]).add(edge[0]); + } + // 无向边组成的无环图 + // 为了方便,就认为0是头 + // dfn[i] = ? + int[] dfn = new int[n]; + // xor[i] 以i为头的整棵树,整体异或的结果是多少? + int[] xor = new int[n]; + // size[i] 以i为头的整棵树,一共几个点? + int[] size = new int[n]; + cnt = 1; + dfs(nums, graph, 0, dfn, xor, size); + int ans = Integer.MAX_VALUE, m = edges.length, cut1, cut2, pre, pos, part1, part2, part3, max, min; + for (int i = 0; i < m; i++) { + // i,要删掉的第一条边,i号边 + // edges[i][0] edges[i][1] dfn 谁大,谁就是删掉之后的树的头!cut1 + // a b cut1 + // { a, b} + // 0 1 + int a = edges[i][0]; + int b = edges[i][1]; + cut1 = dfn[a] < dfn[b] ? b : a; + for (int j = i + 1; j < m; j++) { + // j, 要删掉的第二条边,j号边 + // { c, d} + // 0 1 + int c = edges[j][0]; + int d = edges[j][1]; + cut2 = dfn[c] < dfn[d] ? d : c; + // cut1,cut2 + pre = dfn[cut1] < dfn[cut2] ? cut1 : cut2; + pos = pre == cut1 ? cut2 : cut1; + // 早 pre 晚 pos + part1 = xor[pos]; + // pos为头的树,是pre为头的树的子树! + if (dfn[pos] < dfn[pre] + size[pre]) { + part2 = xor[pre] ^ xor[pos]; + part3 = xor[0] ^ xor[pre]; + } else { // pos为头的树,不是pre为头的树的子树! + part2 = xor[pre]; + part3 = xor[0] ^ part1 ^ part2; + } + max = Math.max(Math.max(part1, part2), part3); + min = Math.min(Math.min(part1, part2), part3); + ans = Math.min(ans, max - min); + } + } + return ans; + } + + // 所有节点的值,存在nums数组里 + // 整个图结构,存在graph里 + // 当前来到的是cur号点 + // 请把cur为头,整棵树,所有节点的dfn、size、xor填好! + // 返回! + public static void dfs(int[] nums, + ArrayList> graph, + int cur, + int[] dfn, int[] xor, int[] size) { + // 当前节点了!, + dfn[cur] = cnt++; + // 只是来到了cur的头部! + xor[cur] = nums[cur]; + size[cur] = 1; + // 遍历所有的孩子! + for (int next : graph.get(cur)) { + // 只有dfn是0的孩子,才是cur在树中的下级!!!! + if (dfn[next] == 0) { + // cur某个孩子是next + dfs(nums, graph, next, dfn, xor, size); + // next整棵树的异或和, + xor[cur] ^= xor[next]; + // next整棵树的size + size[cur] += size[next]; + } + } + } + +} diff --git a/算法周更班/class_2022_07_1_week/Code03_NumberOfPeopleAwareOfASecret.java b/算法周更班/class_2022_07_1_week/Code03_NumberOfPeopleAwareOfASecret.java new file mode 100644 index 0000000..37eab25 --- /dev/null +++ b/算法周更班/class_2022_07_1_week/Code03_NumberOfPeopleAwareOfASecret.java @@ -0,0 +1,52 @@ +package class_2022_07_1_week; + +// 在第 1 天,有一个人发现了一个秘密。 +// 给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后, +// 每天 给一个新的人 分享 秘密。 +// 同时给你一个整数 forget ,表示每个人在发现秘密 forget 天之后会 忘记 这个秘密。 +// 一个人 不能 在忘记秘密那一天及之后的日子里分享秘密。 +// 给你一个整数 n ,请你返回在第 n 天结束时,知道秘密的人数。 +// 由于答案可能会很大,请你将结果对 109 + 7 取余 后返回。 +// 测试链接 : https://leetcode.cn/problems/number-of-people-aware-of-a-secret/ +public class Code03_NumberOfPeopleAwareOfASecret { + + public static int peopleAwareOfSecret(int n, int delay, int forget) { + long mod = 1000000007; + // dpKnow[i], 第i天知道秘密的人 + long[] dpKnow = new long[n + 1]; + // dpForget[i], 第i天将要忘记秘密的人 + long[] dpForget = new long[n + 1]; + // dpShare[i], 第i天可以分享秘密的人 + long[] dpShare = new long[n + 1]; + // 第1天的时候,知道秘密的人1个,A + // 第1天的时候,将要忘记秘密的人0个 + // 第1天的时候,可以分享秘密的人0个 + dpKnow[1] = 1; + if (1 + forget <= n) { + dpForget[1 + forget] = 1; + } + if (1 + delay <= n) { + dpShare[1 + delay] = 1; + } + // 从第2天开始!i + for (int i = 2; i <= n; i++) { + // 第i天 + // dpKnow[i - 1] - dpForget[i] + dpShare[i] + dpKnow[i] = (mod + dpKnow[i - 1] - dpForget[i] + dpShare[i]) % mod; + if (i + forget <= n) { + // dpShare[i] 是第i天,刚知道秘密的人! + // 这批人,会在i + forget天,都忘了! + dpForget[i + forget] = dpShare[i]; + } + if (i + delay <= n) { + // dpShare[i + delay - 1] + dpShare[i] - dpForget[i + delay] + // i + delay 天 , 100天后,会分享秘密的人 + // 第i天,有一些新人,i + delay天分享,一部分, dpShare[i] + // 第二部分呢?i + delay - 1天,知道秘密并且会散播的人,- dpForget[i + delay] + dpShare[i + delay] = (mod + dpShare[i + delay - 1] - dpForget[i + delay] + dpShare[i]) % mod; + } + } + return (int) dpKnow[n]; + } + +} diff --git a/算法周更班/class_2022_07_2_week/Code01_DistinctSubseqValue.java b/算法周更班/class_2022_07_2_week/Code01_DistinctSubseqValue.java new file mode 100644 index 0000000..9e43a0f --- /dev/null +++ b/算法周更班/class_2022_07_2_week/Code01_DistinctSubseqValue.java @@ -0,0 +1,68 @@ +package class_2022_07_2_week; + +import java.util.HashMap; + +// 给定一个字符串 s,计算 s 的 不同非空子序列 的个数 +// 因为结果可能很大,所以返回答案需要对 10^9 + 7 取余 。 +// 字符串的 子序列 是经由原字符串删除一些(也可能不删除)字符 +// 但不改变剩余字符相对位置的一个新字符串。 +// 本题来自大厂刷题班17节 +// 但是为了讲述一个最新题目,不得不重提这个题 +// 本题测试链接 : https://leetcode.com/problems/distinct-subsequences-ii/ +public class Code01_DistinctSubseqValue { + + public static int distinctSubseqII(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int m = 1000000007; + char[] str = s.toCharArray(); + // a -> 0 -> + // b -> 1 -> + // c -> 2 -> + // z -> 25 -> + // count[i] = 0 + int[] count = new int[26]; + // { } + int all = 1; // 算空集 + for (char x : str) { + // x + // 纯新增 + // add = all - count[x] + // all += add + // count[x] + add + int add = (all - count[x - 'a'] + m) % m; + all = (all + add) % m; + count[x - 'a'] = (count[x - 'a'] + add) % m; + } + // {} 去掉! + return all - 1; + } + + public static int zuo(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int m = 1000000007; + char[] str = s.toCharArray(); + HashMap map = new HashMap<>(); + int all = 1; // 一个字符也没遍历的时候,有空集 + for (char x : str) { + int newAdd = all; +// int curAll = all + newAdd - (map.containsKey(x) ? map.get(x) : 0); + int curAll = all; + curAll = (curAll + newAdd) % m; + curAll = (curAll - (map.containsKey(x) ? map.get(x) : 0) + m) % m; + all = curAll; + map.put(x, newAdd); + } + return all; + } + + public static void main(String[] args) { + String s = "bccaccbaabbc"; + System.out.println(distinctSubseqII(s) + 1); + System.out.println(zuo(s)); + } + +} diff --git a/算法周更班/class_2022_07_2_week/Code02_WaysSubsqenceXToY.java b/算法周更班/class_2022_07_2_week/Code02_WaysSubsqenceXToY.java new file mode 100644 index 0000000..79f5c5c --- /dev/null +++ b/算法周更班/class_2022_07_2_week/Code02_WaysSubsqenceXToY.java @@ -0,0 +1,207 @@ +package class_2022_07_2_week; + +import java.util.HashSet; + +// 来自SnowFlake +// 给定一个正数n,比如6 +// 表示数轴上有 0,1,2,3,4,5,6 +// <0 或者 >6 的位置认为无法到达 +// 给定两个数字x和y,0<= x,y <= n +// 表示小人一开始在x的位置,它的目的地是y的位置,比如x = 1, y = 3 +// 给定一个字符串s,比如 : rrlrlr +// 任何一个s的子序列,对应着一种运动轨迹,r表示向右,l表示向左 +// 比如一开始小人在1位置,"rlr"是s的一个子序列 +// 那么运动轨迹是:1 -> 2 -> 1 -> 2 +// 求,s中有多少个字面值不同的子序列,能让小人从x走到y, +// 走的过程中完全不走出0到n的区域。 +// 比如,s = "rrlrlr", n = 6, x = 1, y = 3 +// 有如下5个字面值不同的子序列 +// rr : 1 -> 2 -> 3 +// rrlr : 1 -> 2 -> 3 -> 2 -> 3 +// rrrl : 1 -> 2 -> 3 -> 4 -> 3 +// rlrr : 1 -> 2 -> 1 -> 2 -> 3 +// rrlrlr : 1 -> 2 -> 3 -> 2 -> 3 -> 2 -> 3 +// 注意:一定要是字面值不同的子序列!相同字面值的子序列算一种 +// 比如s中,有很多个rr的子序列,但是算一个 +// 数据规模 : s串长度 <= 1000, x,y,n <= 2500 +public class Code02_WaysSubsqenceXToY { + + // 不要求去重? + // 指令来到index位置的字符! + // 当前来到的位置,cur + // 最终要去的位置,aim + // 返回方法数(不去重的!) + // index 字符(要!不要!) + public static int f(char[] str, int index, int cur, int n, int aim) { + if (index == str.length) { + return cur == aim ? 1 : 0; + } + // 可能性1 : 当前的字符,不要! + int ways1 = f(str, index + 1, cur, n, aim); + // 可能性2 : 当前的字符,要! + int ways2 = 0; + if (str[index] == 'L') { + if (cur - 1 >= 0) { + ways2 = f(str, index + 1, cur - 1, n, aim); + } + } else { // R + if (cur + 1 <= n) { + ways2 = f(str, index + 1, cur + 1, n, aim); + } + } + return ways1 + ways2; + } + + // 暴力方法 + // 为了测试 + // 生成所有去重的子序列,一个一个验证 + public static int ways1(String s, int n, int x, int y) { + HashSet set = new HashSet<>(); + process1(s, 0, new StringBuilder(), set); + int ans = 0; + int cur = 0; + for (String path : set) { + cur = x; + for (char cha : path.toCharArray()) { + cur += cha == 'r' ? 1 : -1; + if (cur < 0 || cur > n) { + cur = -1; + break; + } + } + if (cur == y) { + ans++; + } + } + return ans; + } + + public static void process1(String s, int index, StringBuilder builder, HashSet set) { + if (index == s.length()) { + set.add(builder.toString()); + } else { + process1(s, index + 1, builder, set); + builder.append(s.charAt(index)); + process1(s, index + 1, builder, set); + builder.deleteCharAt(builder.length() - 1); + } + } + + // 最优解 + // 思路来自:大厂刷题班,17节,Code05,DistinctSubseqValue问题 + // 如果字符串长度为m,位置数量n + // 时间复杂度O(m * n) + public static int ways2(String s, int n, int x, int y) { + // all[i] : 让小人来到i位置的不同字面值的子序列数量 + int[] all = new int[n + 1]; + // r[i] : 让小人来到i位置的不同字面值,且以r字符结尾,的子序列数量 + int[] r = new int[n + 1]; + // l[i] : 让小人来到i位置的不同字面值,且以l字符结尾,的子序列数量 + int[] l = new int[n + 1]; + int[] add = new int[n + 1]; + // 一开始小人在x,all[x] = 1, {} + all[x] = 1; + // M + for (char cha : s.toCharArray()) { + // 当前的指令字符串,cha + if (cha == 'r') { + + // 当前小人往右走 + // 0 -> 1 + // 1 -> 2 + // 5 -> 6 + // n-1 -> n + // n -> 死 + // 4 1000 + // 5 +1000 + // + // 8 200 + // 9 +200 + for (int i = 0; i < n; i++) { + // 9 方法数 新增 all[8] + // 每一个新增方法,都还没有减去修正值呢! + add[i + 1] += all[i]; + } + for (int i = 0; i <= n; i++) { + // 变了!成了纯新增! + add[i] -= r[i]; + all[i] += add[i]; + r[i] += add[i]; + add[i] = 0; + } + } else { + // 遇到的是l + // 当前小人往左走 + // 0 左 死 + // 1 0 + // 2 1 + // 3 2 + for (int i = 1; i <= n; i++) { + // 7 新增 之前8位置方法数 + add[i - 1] += all[i]; + } + for (int i = 0; i <= n; i++) { + // 修正,变成纯新增! + add[i] -= l[i]; + all[i] += add[i]; + l[i] += add[i]; + add[i] = 0; + } + } + } + // 去重的! + return all[y]; + } + + // 为了测试 + public static String randomLRString(int n) { + char[] str = new char[n]; + for (int i = 0; i < n; i++) { + str[i] = Math.random() < 0.5 ? 'r' : 'l'; + } + return String.valueOf(str); + } + + // 为了测试 + public static void main(String[] args) { + int max = 16; + int testTime = 2000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * max) + 1; + int m = (int) (Math.random() * max) + 1; + String s = randomLRString(m); + int x = (int) (Math.random() * (n + 1)); + int y = (int) (Math.random() * (n + 1)); + int ans1 = ways1(s, n, x, y); + int ans2 = ways2(s, n, x, y); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(s); + System.out.println(n); + System.out.println(x); + System.out.println(y); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + int n = 25000; + int m = 10000; + System.out.println("位置规模 : " + n); + System.out.println("字符串规模 : " + m); + String s = randomLRString(m); + int x = (int) (Math.random() * (n + 1)); + int y = (int) (Math.random() * (n + 1)); + long start = System.currentTimeMillis(); + ways2(s, n, x, y); + long end = System.currentTimeMillis(); + System.out.println("运行时间: " + (end - start) + " 毫秒"); + System.out.println("性能测试结束"); + + } + +} diff --git a/算法周更班/class_2022_07_2_week/Code03_SwimInRisingWater.java b/算法周更班/class_2022_07_2_week/Code03_SwimInRisingWater.java new file mode 100644 index 0000000..0ac8490 --- /dev/null +++ b/算法周更班/class_2022_07_2_week/Code03_SwimInRisingWater.java @@ -0,0 +1,155 @@ +package class_2022_07_2_week; + +import java.util.Arrays; +import java.util.PriorityQueue; + +// 在一个 n x n 的整数矩阵 grid 中, +// 每一个方格的值 grid[i][j] 表示位置 (i, j) 的平台高度。 +// 当开始下雨时,在时间为 t 时,水池中的水位为 t 。 +// 你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。 +// 假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。 +// 当然,在你游泳的时候你必须待在坐标方格里面。 +// 你从坐标方格的左上平台 (0,0) 出发。 +// 返回 你到达坐标方格的右下平台 (n-1, n-1) 所需的最少时间 。 +// 测试链接 :https://leetcode.cn/problems/swim-in-rising-water +public class Code03_SwimInRisingWater { + + // 并查集的解法 + public static int swimInWater1(int[][] grid) { + // 行号 + int n = grid.length; + // 列号 + int m = grid[0].length; + // [0,0,5] + // [0,1,3].... + int[][] points = new int[n * m][3]; + int pi = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + points[pi][0] = i; + points[pi][1] = j; + points[pi++][2] = grid[i][j]; + } + } + // 所有格子小对象,生成好了! + // 排序![a,b,c] [d,e,f] + Arrays.sort(points, (a, b) -> a[2] - b[2]); + // 生成并查集!n * m + // 初始化的时候,把所有格子独自成一个集合! + UnionFind uf = new UnionFind(n, m); + int ans = 0; + for (int i = 0; i < points.length; i++) { + int r = points[i][0]; + int c = points[i][1]; + int v = points[i][2]; + if (r > 0 && grid[r - 1][c] <= v) { + uf.union(r, c, r - 1, c); + } + if (r < n - 1 && grid[r + 1][c] <= v) { + uf.union(r, c, r + 1, c); + } + if (c > 0 && grid[r][c - 1] <= v) { + uf.union(r, c, r, c - 1); + } + if (c < m - 1 && grid[r][c + 1] <= v) { + uf.union(r, c, r, c + 1); + } + if (uf.isSameSet(0, 0, n - 1, m - 1)) { + ans = v; + break; + } + } + return ans; + } + + public static class UnionFind { + public int col; + public int pointsSize; + public int[] father; + public int[] size; + public int[] help; + + public UnionFind(int n, int m) { + col = m; + pointsSize = n * m; + father = new int[pointsSize]; + size = new int[pointsSize]; + help = new int[pointsSize]; + for (int i = 0; i < pointsSize; i++) { + father[i] = i; + size[i] = 1; + } + } + + private int find(int i) { + int hi = 0; + while (i != father[i]) { + help[hi++] = i; + i = father[i]; + } + while (hi > 0) { + father[help[--hi]] = i; + } + return i; + } + + private int index(int i, int j) { + return i * col + j; + } + + public void union(int row1, int col1, int row2, int col2) { + int f1 = find(index(row1, col1)); + int f2 = find(index(row2, col2)); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + father[f2] = f1; + size[f1] += size[f2]; + } else { + father[f1] = f2; + size[f2] += size[f1]; + } + } + } + + public boolean isSameSet(int row1, int col1, int row2, int col2) { + return find(index(row1, col1)) == find(index(row2, col2)); + } + + } + + // Dijkstra算法 + public static int swimInWater2(int[][] grid) { + int n = grid.length; + int m = grid[0].length; + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[2] - b[2]); + boolean[][] visited = new boolean[n][m]; + heap.add(new int[] { 0, 0, grid[0][0] }); + int ans = 0; + while (!heap.isEmpty()) { + int r = heap.peek()[0]; + int c = heap.peek()[1]; + int v = heap.peek()[2]; + heap.poll(); + if (visited[r][c]) { + continue; + } + visited[r][c] = true; + if (r == n - 1 && c == m - 1) { + ans = v; + break; + } + add(grid, heap, visited, r - 1, c, v); + add(grid, heap, visited, r + 1, c, v); + add(grid, heap, visited, r, c - 1, v); + add(grid, heap, visited, r, c + 1, v); + } + return ans; + } + + public static void add(int[][] grid, PriorityQueue heap, boolean[][] visited, int r, int c, int preV) { + if (r >= 0 && r < grid.length && c >= 0 && c < grid[0].length && !visited[r][c]) { + heap.add(new int[] { r, c, preV + Math.max(0, grid[r][c] - preV) }); + } + } + +} diff --git a/算法周更班/class_2022_07_2_week/Code04_EmployeeFreeTime.java b/算法周更班/class_2022_07_2_week/Code04_EmployeeFreeTime.java new file mode 100644 index 0000000..16d701a --- /dev/null +++ b/算法周更班/class_2022_07_2_week/Code04_EmployeeFreeTime.java @@ -0,0 +1,57 @@ +package class_2022_07_2_week; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +// 给定员工的 schedule 列表,表示每个员工的工作时间。 +// 每个员工都有一个非重叠的时间段  Intervals 列表,这些时间段已经排好序。 +// 返回表示 所有 员工的 共同,正数长度的空闲时间 的有限时间段的列表,同样需要排好序。 +// 测试链接 : https://leetcode.cn/problems/employee-free-time/ +public class Code04_EmployeeFreeTime { + + // 不要提交这个类 + public static class Interval { + public int start; + public int end; + + public Interval(int s, int e) { + start = s; + end = e; + } + } + + // 提交以下的code + // 哈希表! + public static List employeeFreeTime(List> schedule) { + ArrayList arr = new ArrayList<>(); + for (List people : schedule) { + for (Interval interval : people) { + // 0 开始时间点,有个员工要上线 + // 1 结束时间点,有个员工要下线 + arr.add(new int[] { interval.start, interval.end, 0 }); + arr.add(new int[] { interval.end, interval.end, 1 }); + } + } + arr.sort((a, b) -> a[0] - b[0]); + HashSet set = new HashSet(); + set.add(arr.get(0)[1]); + List ans = new ArrayList<>(); + for (int i = 1; i < arr.size(); i++) { + int[] cur = arr.get(i); + if (cur[2] == 0) { + // 开始时间点来到的时候,来看看有没有空闲时间段 + // 3 7 + if (set.isEmpty() && arr.get(i - 1)[0] != cur[0]) { + ans.add(new Interval(arr.get(i - 1)[0], cur[0])); + } + // 哈希表填人了,cur[1] + set.add(cur[1]); + } else { + set.remove(cur[0]); + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_07_2_week/Code05_LineSweepAlgorithm1.java b/算法周更班/class_2022_07_2_week/Code05_LineSweepAlgorithm1.java new file mode 100644 index 0000000..cef05df --- /dev/null +++ b/算法周更班/class_2022_07_2_week/Code05_LineSweepAlgorithm1.java @@ -0,0 +1,129 @@ +package class_2022_07_2_week; + +import java.util.Arrays; + +// 我们给出了一个(轴对齐的)二维矩形列表 rectangles 。 +// 对于 rectangle[i] = [x1, y1, x2, y2],其中(x1,y1)是矩形 i 左下角的坐标 +// (xi1, yi1) 是该矩形 左下角 的坐标, (xi2, yi2) 是该矩形 右上角 的坐标。 +// 计算平面中所有 rectangles 所覆盖的 总面积 。 +// 任何被两个或多个矩形覆盖的区域应只计算 一次 。 +// 返回 总面积 。因为答案可能太大,返回 10^9 + 7 的 模 。 +// 本题测试链接 : https://leetcode.cn/problems/rectangle-area-ii/ +public class Code05_LineSweepAlgorithm1 { + + // x y + public static int rectangleArea(int[][] rectangles) { + int n = rectangles.length; + long[][] arr = new long[n << 1][4]; + long max = 0; + for (int i = 0; i < n; i++) { + // x1 y1 左下角点的坐标 + // x2 y2 右上角点的坐标 + // 解释一下y1为啥要+1 + // 比如y1 = 3, y2 = 7 + // 实际的处理的时候,真实的线段认为是闭区间[4,7]的 + // 如果不这么处理会有问题 + // 比如先在y1 = 3, y2 = 7上,都+1 + // 那么此时: + // value: 0 0 1 1 1 1 1 0 + // index: 1 2 3 4 5 6 7 8 + // 这是不对的! + // 因为线段[3,7]长度是4啊!而在线段树里,是5个1! + // 所以,y1 = 3, y2 = 7 + // 我们就是认为是4~7,都+1 + // 那么此时: + // value: 0 0 0 1 1 1 1 0 + // index: 1 2 3 4 5 6 7 8 + // 线段树上,正好4个1,和我们想要的距离是一致的 + int x1 = rectangles[i][0]; + int y1 = rectangles[i][1] + 1; + int x2 = rectangles[i][2]; + int y2 = rectangles[i][3]; + arr[i][0] = x1; + arr[i][1] = y1; + arr[i][2] = y2; + arr[i][3] = 1; + arr[i + n][0] = x2; + arr[i + n][1] = y1; + arr[i + n][2] = y2; + arr[i + n][3] = -1; + max = Math.max(max, y2); + } + return coverArea(arr, n << 1, max); + } + + public static int coverArea(long[][] arr, int n, long max) { + // 所有的事件,都在arr里 + // [x, y1, y2, +1/-1] + // 早 -> 晚 + Arrays.sort(arr, 0, n, (a, b) -> a[0] <= b[0] ? -1 : 1); + // max y的值,可能的最大值,非常大也支持! + DynamicSegmentTree dst = new DynamicSegmentTree(max); + long preX = 0; + long ans = 0; + for (int i = 0; i < n; i++) { + // dst.query() : 开点线段树告诉你!y方向真实的长度! + ans += dst.query() * (arr[i][0] - preX); + ans %= 1000000007; + preX = arr[i][0]; + dst.add(arr[i][1], arr[i][2], arr[i][3]); + } + return (int) ans; + } + + public static class Node { + public long cover; + public long len; + public Node left; + public Node right; + } + + public static class DynamicSegmentTree { + public Node root; + public long size; + + public DynamicSegmentTree(long max) { + root = new Node(); + size = max; + } + + public void add(long L, long R, long cover) { + add(root, 1, size, L, R, cover); + } + + private void add(Node cur, long l, long r, long L, long R, long cover) { + if (L <= l && R >= r) { + cur.cover += cover; + } else { + if (cur.left == null) { + cur.left = new Node(); + } + if (cur.right == null) { + cur.right = new Node(); + } + long m = l + ((r - l) >> 1); + if (L <= m) { + add(cur.left, l, m, L, R, cover); + } + if (R > m) { + add(cur.right, m + 1, r, L, R, cover); + } + } + pushUp(cur, l, r); + } + + private void pushUp(Node cur, long l, long r) { + if (cur.cover > 0) { + cur.len = r - l + 1; + } else { + cur.len = (cur.left != null ? cur.left.len : 0) + (cur.right != null ? cur.right.len : 0); + } + } + + public long query() { + return root.len; + } + + } + +} diff --git a/算法周更班/class_2022_07_2_week/Code05_LineSweepAlgorithm2.java b/算法周更班/class_2022_07_2_week/Code05_LineSweepAlgorithm2.java new file mode 100644 index 0000000..4071ad6 --- /dev/null +++ b/算法周更班/class_2022_07_2_week/Code05_LineSweepAlgorithm2.java @@ -0,0 +1,98 @@ +package class_2022_07_2_week; + +// 本题测试链接 : https://www.luogu.com.cn/problem/P5490 +// 提交以下代码,并把主类名改成"Main" +// 可以直接通过 +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code05_LineSweepAlgorithm2 { + + public static int maxn = 300001; + public static long[][] arr = new long[maxn][4]; + public static long[] orderedY = new long[maxn]; + public static long[] cover = new long[maxn << 2]; + public static long[] realLength = new long[maxn << 2]; + public static long[] left = new long[maxn << 2]; + public static long[] right = new long[maxn << 2]; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer scanner = new StreamTokenizer(br); + scanner.nextToken(); + int n = (int) scanner.nval; + for (int i = 1; i <= n; i++) { + scanner.nextToken(); + int x1 = (int) scanner.nval; + scanner.nextToken(); + int y1 = (int) scanner.nval; + scanner.nextToken(); + int x2 = (int) scanner.nval; + scanner.nextToken(); + int y2 = (int) scanner.nval; + orderedY[i] = y1; + orderedY[i + n] = y2; + arr[i][0] = x1; + arr[i][1] = y1; + arr[i][2] = y2; + arr[i][3] = 1; + arr[i + n][0] = x2; + arr[i + n][1] = y1; + arr[i + n][2] = y2; + arr[i + n][3] = -1; + } + System.out.println(coverArea(n << 1)); + } + + public static long coverArea(int n) { + Arrays.sort(orderedY, 1, n + 1); + Arrays.sort(arr, 1, n + 1, (a, b) -> a[0] <= b[0] ? -1 : 1); + build(1, n, 1); + long preX = 0; + long ans = 0; + for (int i = 1; i <= n; i++) { + ans += realLength[1] * (arr[i][0] - preX); + preX = arr[i][0]; + add(arr[i][1], arr[i][2], (int) arr[i][3], 1); + } + return ans; + } + + private static void build(int l, int r, int i) { + if (r - l > 1) { + int m = (l + r) >> 1; + build(l, m, i << 1); + build(m, r, (i << 1) | 1); + } + left[i] = orderedY[l]; + right[i] = orderedY[r]; + } + + private static void add(long L, long R, long C, int i) { + long l = left[i]; + long r = right[i]; + if (L <= l && R >= r) { + cover[i] += C; + } else { + if (L < right[i << 1]) { + add(L, R, C, i << 1); + } + if (R > left[(i << 1) | 1]) { + add(L, R, C, (i << 1) | 1); + } + } + pushUp(i); + } + + public static void pushUp(int i) { + if (cover[i] > 0) { + realLength[i] = right[i] - left[i]; + } else { + realLength[i] = realLength[i << 1] + realLength[(i << 1) | 1]; + } + } + +} diff --git a/算法周更班/class_2022_07_3_week/Code01_SetIntersectionSizeAtLeastTwo.java b/算法周更班/class_2022_07_3_week/Code01_SetIntersectionSizeAtLeastTwo.java new file mode 100644 index 0000000..eaf9d06 --- /dev/null +++ b/算法周更班/class_2022_07_3_week/Code01_SetIntersectionSizeAtLeastTwo.java @@ -0,0 +1,57 @@ +package class_2022_07_3_week; + +import java.util.Arrays; + +// 一个整数区间 [a, b]  ( a < b ) 代表着从 a 到 b 的所有连续整数,包括 a 和 b。 +// 给你一组整数区间intervals,请找到一个最小的集合 S, +// 使得 S 里的元素与区间intervals中的每一个整数区间都至少有2个元素相交。 +// 输出这个最小集合S的大小。 +// 测试链接 : https://leetcode.cn/problems/set-intersection-size-at-least-two/ +public class Code01_SetIntersectionSizeAtLeastTwo { + + public static int intersectionSizeTwo(int[][] intervals) { + // O(N*logN) + // 区间根据,结束位置谁小,谁在前 + // 结束位置一样的,开头位置谁大,谁在前 + Arrays.sort(intervals, (a, b) -> a[1] != b[1] ? (a[1] - b[1]) : (b[0] - a[0])); + // 区间排好序了 + // [1,7] [2,8] [1,8] [13,40] + int n = intervals.length; + // [1,7] pre = 6 pos =7 + int pos = intervals[0][1]; + int pre = pos - 1; + int ans = 2; + for (int i = 1; i < n; i++) { + // intervals[i] = {开头,结尾} + // 6 7 [<=6, 结尾] + // + // if(intervals[i][0] <= pre) { + // continue; + // } + // >6 讨论! + if (intervals[i][0] > pre) { + // 6 7 [开头>6, 结尾] + // 1) 6 < 开头 <= 7 + // 只有7满足了当前的区间,我们要加个数字,结尾 + // 6 7 结尾 + // pre pos + // 6 7 + // 2) 6 < 开头、7 < 开头 + // 结尾-1 结尾 + // pre pos + if (intervals[i][0] > pos) { // 对应的就是情况2) + pre = intervals[i][1] - 1; + ans += 2; + } else { // 对应的就是情况1) + pre = pos; + ans += 1; + } + // 不管情况2)还是情况1)都需要这一句 + pos = intervals[i][1]; + } + + } + return ans; + } + +} diff --git a/算法周更班/class_2022_07_3_week/Code02_ValidParenthesisString.java b/算法周更班/class_2022_07_3_week/Code02_ValidParenthesisString.java new file mode 100644 index 0000000..5ed6a38 --- /dev/null +++ b/算法周更班/class_2022_07_3_week/Code02_ValidParenthesisString.java @@ -0,0 +1,119 @@ +package class_2022_07_3_week; + +// 来自蔚来汽车 +// 给定一个只包含三种字符的字符串:( 、) 和 *, +// 写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则: +// 任何左括号 ( 必须有相应的右括号 )。 +// 任何右括号 ) 必须有相应的左括号 ( 。 +// 左括号 ( 必须在对应的右括号之前 )。 +// * 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符。 +// 一个空字符串也被视为有效字符串。 +// 测试链接 : https://leetcode.cn/problems/valid-parenthesis-string/ +public class Code02_ValidParenthesisString { + + public static boolean valid(String str) { + char[] s = str.toCharArray(); + int n = s.length; + int[][] dp = new int[n][n + 1]; + // dp[i][j] == 0 没算过! + // dp[i][j] == -1 算过了!结果是false! + // dp[i][j] == 1 算过了!结果是true! + return zuo(s, 0, 0, dp); + } + + public static boolean zuo(char[] s, int i, int c, int[][] dp) { + if (i == s.length) { + return c == 0; + } + if (c < 0) { + return false; + } + if (dp[i][c] != 0) { + return dp[i][c] == 1; + } + boolean ans = false; + if (s[i] == '(') { + ans = zuo(s, i + 1, c + 1, dp); + } else if (s[i] == ')') { + ans = zuo(s, i + 1, c - 1, dp); + } else { // * + boolean p1 = zuo(s, i + 1, c + 1, dp); + boolean p2 = zuo(s, i + 1, c - 1, dp); + boolean p3 = zuo(s, i + 1, c, dp); + ans = p1 || p2 || p3; + } + dp[i][c] = ans ? 1 : -1; + return ans; + } + + // 时间复杂度O(N平方) + // 从左往右的尝试 + // 暴力递归改动态规划 + public static boolean checkValidString1(String s) { + char[] str = s.toCharArray(); + int n = str.length; + int[][] dp = new int[n][n]; + return f(str, 0, 0, dp); + } + + public static boolean f(char[] s, int i, int c, int[][] dp) { + if (i == s.length) { + return c == 0; + } + if (c < 0) { + return false; + } + if (c > s.length - i) { + return false; + } + if (dp[i][c] != 0) { + return dp[i][c] == 1; + } + boolean ans = false; + if (s[i] == '(') { + ans = f(s, i + 1, c + 1, dp); + } else if (s[i] == ')') { + ans = f(s, i + 1, c - 1, dp); + } else { + ans |= f(s, i + 1, c + 1, dp); + ans |= f(s, i + 1, c - 1, dp); + ans |= f(s, i + 1, c, dp); + } + dp[i][c] = ans ? 1 : -1; + return ans; + } + + // 贪心方法 + // 最优解 + // 时间复杂度O(N),额外空间复杂度O(1) + public static boolean checkValidString2(String s) { + char[] str = s.toCharArray(); + int max = 0; + int min = 0; + for (char x : str) { + if (x == '(') { + max++; + min++; + } else { + // ) * + if (x == ')' && max == 0) { // 不够减了! + return false; + } + // max 够减 + // ) * + // max -1 +1 + max += x == ')' ? -1 : 1; + // min ( - ) 弹性范围中,最小的差值 + // ) * min -1 + // min == 0 + if (min > 0) { + min--; + } + } + } + // 0 ~ 7 + // 3 ~ 9 + return min == 0; + } + +} diff --git a/算法周更班/class_2022_07_3_week/Code03_TopKFrequentElements.java b/算法周更班/class_2022_07_3_week/Code03_TopKFrequentElements.java new file mode 100644 index 0000000..375d405 --- /dev/null +++ b/算法周更班/class_2022_07_3_week/Code03_TopKFrequentElements.java @@ -0,0 +1,68 @@ +package class_2022_07_3_week; + +// 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。 +// 你可以按 任意顺序 返回答案。 +// 要求时间复杂度O(N) +// 本题测试链接 : https://leetcode.cn/problems/top-k-frequent-elements/ +// 提交时直接提交以下代码,并把主类名改成"Solution", 可以直接通过 + +import java.util.HashMap; +import java.util.Map.Entry; + +public class Code03_TopKFrequentElements { + + public static int[] topKFrequent(int[] nums, int k) { + HashMap map = new HashMap<>(); + for (int num : nums) { + map.put(num, map.getOrDefault(num, 0) + 1); + } + int i = map.size(); + int[][] arr = new int[i][2]; + for (Entry entry : map.entrySet()) { + arr[--i][0] = entry.getKey(); + arr[i][1] = entry.getValue(); + } + moreLess(arr, 0, arr.length - 1, k); + int[] ans = new int[k]; + for (; i < k; i++) { + ans[i] = arr[i][0]; + } + return ans; + } + + public static void moreLess(int[][] arr, int l, int r, int k) { + if (k == r - l + 1) { + return; + } + swap(arr, r, l + (int) (Math.random() * (r - l + 1))); + int pivot = partition(arr, l, r); + if (pivot - l == k) { + return; + } else if (pivot - l > k) { + moreLess(arr, l, pivot - 1, k); + } else { + moreLess(arr, pivot, r, k - pivot + l); + } + } + + public static int partition(int[][] arr, int l, int r) { + int left = l - 1; + int index = l; + while (index < r) { + if (arr[index][1] <= arr[r][1]) { + index++; + } else { + swap(arr, ++left, index++); + } + } + swap(arr, ++left, r); + return left; + } + + public static void swap(int[][] arr, int i, int j) { + int[] tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + +} diff --git a/算法周更班/class_2022_07_3_week/Code04_SpecialBinaryString.java b/算法周更班/class_2022_07_3_week/Code04_SpecialBinaryString.java new file mode 100644 index 0000000..1ffcfcd --- /dev/null +++ b/算法周更班/class_2022_07_3_week/Code04_SpecialBinaryString.java @@ -0,0 +1,59 @@ +package class_2022_07_3_week; + +import java.util.ArrayList; + +// 特殊的二进制序列是具有以下两个性质的二进制序列: +// 0 的数量与 1 的数量相等。 +// 二进制序列的每一个前缀码中 1 的数量要大于等于 0 的数量。 +// 给定一个特殊的二进制序列 S,以字符串形式表示。 +// 定义一个操作 为首先选择 S 的两个连续且非空的特殊的子串,然后将它们交换。 +// (两个子串为连续的当且仅当第一个子串的最后一个字符恰好为第二个子串的第一个字符的前一个字符) +// 在任意次数的操作之后,交换后的字符串按照字典序排列的最大的结果是什么? +// 测试链接 : https://leetcode.cn/problems/special-binary-string/ +public class Code04_SpecialBinaryString { + + public static String makeLargestSpecial(String s) { + ArrayList arr = new ArrayList<>(); + // 主! + for (int index = 0; index < s.length();) { + Info info = process(s, index + 1); + arr.add(info.ans); + index = info.end + 1; + } + StringBuilder builder = new StringBuilder(); + arr.sort((a, b) -> b.compareTo(a)); + for (String cur : arr) { + builder.append(cur); + } + return builder.toString(); + } + + public static class Info { + public String ans; + public int end; + + public Info(String a, int e) { + ans = a; + end = e; + } + } + + // process(i) + public static Info process(String s, int index) { + ArrayList arr = new ArrayList<>(); + // index 不能是 ) -> 0 + while (s.charAt(index) != '0') { + // index ( -> 1 + Info info = process(s, index + 1); + arr.add(info.ans); + index = info.end + 1; + } + StringBuilder builder = new StringBuilder(); + arr.sort((a, b) -> b.compareTo(a)); + for (String cur : arr) { + builder.append(cur); + } + return new Info("1" + builder.toString() + "0", index); + } + +} diff --git a/算法周更班/class_2022_07_4_week/Code01_WaysWiggle.java b/算法周更班/class_2022_07_4_week/Code01_WaysWiggle.java new file mode 100644 index 0000000..2eb13bf --- /dev/null +++ b/算法周更班/class_2022_07_4_week/Code01_WaysWiggle.java @@ -0,0 +1,146 @@ +package class_2022_07_4_week; + +// 一个数组如果满足 : +// 升降升降升降... 或者 降升降升...都是满足的 +// 给定一个数组, +// 1,看有几种方法能够剔除一个元素,达成上述的要求 +// 2,数组天然符合要求返回0 +// 3,剔除1个元素达成不了要求,返回-1, +// 比如: +// 给定[3, 4, 5, 3, 7],返回3 +// 移除0元素,4 5 3 7 符合 +// 移除1元素,3 5 3 7 符合 +// 移除2元素,3 4 3 7 符合 +// 再比如:给定[1, 2, 3, 4] 返回-1 +// 因为达成不了要求 +public class Code01_WaysWiggle { + + // 暴力方法 + // 为了验证 + public static int ways1(int[] arr) { + if (isWiggle(arr, -1)) { + return 0; + } + int ans = 0; + for (int i = 0; i < arr.length; i++) { + if (isWiggle(arr, i)) { + ans++; + } + } + return ans == 0 ? -1 : ans; + } + + public static boolean isWiggle(int[] arr, int removeIndex) { + boolean ans = true; + // 升 + boolean request = true; + for (int i = 1; i < arr.length; i++) { + if (i == removeIndex) { + continue; + } + if (i - 1 == removeIndex && removeIndex == 0) { + continue; + } + int last = i - 1 == removeIndex ? (i - 2) : (i - 1); + if (request) { + if (arr[last] >= arr[i]) { + ans = false; + break; + } + } else { + if (arr[last] <= arr[i]) { + ans = false; + break; + } + } + request = !request; + } + if (ans) { + return true; + } + ans = true; + // 降 + request = false; + for (int i = 1; i < arr.length; i++) { + if (i == removeIndex) { + continue; + } + if (i - 1 == removeIndex && removeIndex == 0) { + continue; + } + int last = i - 1 == removeIndex ? (i - 2) : (i - 1); + if (request) { + if (arr[last] >= arr[i]) { + ans = false; + break; + } + } else { + if (arr[last] <= arr[i]) { + ans = false; + break; + } + } + request = !request; + } + return ans; + } + + // 时间复杂度O(N) + public static int ways2(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int n = arr.length; + boolean[] up = new boolean[n]; + boolean[] down = new boolean[n]; + up[n - 1] = true; + down[n - 1] = true; + for (int i = n - 2; i >= 0; i--) { + up[i] = arr[i] < arr[i + 1] && down[i + 1]; + down[i] = arr[i] > arr[i + 1] && up[i + 1]; + } + if (up[0] || down[0]) { + return 0; + } + int ans = (up[1] || down[1]) ? 1 : 0; + boolean leftUp = true; + boolean leftDown = true; + boolean tmp; + for (int i = 1, l = 0, r = 2; i < n - 1; i++, l++, r++) { + ans += (arr[l] > arr[r] && up[r] && leftDown) || (arr[l] < arr[r] && down[r] && leftUp) ? 1 : 0; + tmp = leftUp; + leftUp = arr[l] > arr[i] && leftDown; + leftDown = arr[l] < arr[i] && tmp; + } + ans += leftUp || leftDown ? 1 : 0; + return ans == 0 ? -1 : ans; + } + + // 为了验证 + public static int[] randomArray(int len, int maxValue) { + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * maxValue) + 1; + } + return ans; + } + + // 为了验证 + public static void main(String[] args) { + int maxLen = 10; + int maxValue = 100; + int testTime = 30000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * maxLen) + 1; + int[] arr = randomArray(len, maxValue); + int ans1 = ways1(arr); + int ans2 = ways2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_07_4_week/Code02_SidingPuzzle1.java b/算法周更班/class_2022_07_4_week/Code02_SidingPuzzle1.java new file mode 100644 index 0000000..6399d6a --- /dev/null +++ b/算法周更班/class_2022_07_4_week/Code02_SidingPuzzle1.java @@ -0,0 +1,103 @@ +package class_2022_07_4_week; + +import java.util.HashSet; +import java.util.PriorityQueue; + +// 在一个 2 * 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, +// 以及一块空缺用 0 来表示。一次 移动 定义为选择 0 与一个相邻的数字(上下左右)进行交换. +// 最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。 +// 给出一个谜板的初始状态 board , +// 返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。 +// 测试链接 : https://leetcode.cn/problems/sliding-puzzle/ +public class Code02_SidingPuzzle1 { + + public static int b6 = 100000; + + public static int b5 = 10000; + + public static int b4 = 1000; + + public static int b3 = 100; + + public static int b2 = 10; + + public static int[] nexts = new int[3]; + + public static int[][] end = { { 1, 2 }, { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 } }; + + public static int slidingPuzzle(int[][] m) { + HashSet set = new HashSet<>(); + int from = m[0][0] * b6 + m[0][1] * b5 + m[0][2] * b4 + m[1][0] * b3 + m[1][1] * b2 + m[1][2]; + PriorityQueue heap = new PriorityQueue<>((a, b) -> (a[0] + a[1]) - (b[0] + b[1])); + heap.add(new int[] { 0, distance(from), from }); + int ans = -1; + while (!heap.isEmpty()) { + int[] arr = heap.poll(); + int distance = arr[0]; + int cur = arr[2]; + if (set.contains(cur)) { + continue; + } + if (cur == 123450) { + ans = distance; + break; + } + set.add(cur); + int nextSize = nexts(cur); + for (int i = 0; i < nextSize; i++) { + int next = nexts[i]; + if (!set.contains(next)) { + heap.add(new int[] { distance + 1, distance(next), next }); + } + } + } + return ans; + } + + public static int nexts(int from) { + int a = from / b6; + int b = (from / b5) % 10; + int c = (from / b4) % 10; + int d = (from / b3) % 10; + int e = (from / b2) % 10; + int f = from % 10; + if (a == 0) { + nexts[0] = from + (b - a) * b6 + (a - b) * b5; + nexts[1] = from + (d - a) * b6 + (a - d) * b3; + return 2; + } else if (b == 0) { + nexts[0] = from + (a - b) * b5 + (b - a) * b6; + nexts[1] = from + (c - b) * b5 + (b - c) * b4; + nexts[2] = from + (e - b) * b5 + (b - e) * b2; + return 3; + } else if (c == 0) { + nexts[0] = from + (b - c) * b4 + (c - b) * b5; + nexts[1] = from + (f - c) * b4 + (c - f); + return 2; + } else if (d == 0) { + nexts[0] = from + (a - d) * b3 + (d - a) * b6; + nexts[1] = from + (e - d) * b3 + (d - e) * b2; + return 2; + } else if (e == 0) { + nexts[0] = from + (b - e) * b2 + (e - b) * b5; + nexts[1] = from + (d - e) * b2 + (e - d) * b3; + nexts[2] = from + (f - e) * b2 + (e - f); + return 3; + } else { + nexts[0] = from + (e - f) + (f - e) * b2; + nexts[1] = from + (c - f) + (f - c) * b4; + return 2; + } + } + + public static int distance(int num) { + int ans = end[num / b6][0] + end[num / b6][1]; + ans += end[(num / b5) % 10][0] + Math.abs(end[(num / b5) % 10][1] - 1); + ans += end[(num / b4) % 10][0] + Math.abs(end[(num / b4) % 10][1] - 2); + ans += Math.abs(end[(num / b3) % 10][0] - 1) + end[(num / b3) % 10][1]; + ans += Math.abs(end[(num / b2) % 10][0] - 1) + Math.abs(end[(num / b2) % 10][1] - 1); + ans += Math.abs(end[num % 10][0] - 1) + Math.abs(end[num % 10][1] - 2); + return ans; + } + +} diff --git a/算法周更班/class_2022_07_4_week/Code02_SidingPuzzle2.java b/算法周更班/class_2022_07_4_week/Code02_SidingPuzzle2.java new file mode 100644 index 0000000..5f3fdab --- /dev/null +++ b/算法周更班/class_2022_07_4_week/Code02_SidingPuzzle2.java @@ -0,0 +1,120 @@ +package class_2022_07_4_week; + +// 在一个 2 * 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, +// 以及一块空缺用 0 来表示。一次 移动 定义为选择 0 与一个相邻的数字(上下左右)进行交换. +// 最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。 +// 给出一个谜板的初始状态 board , +// 返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。 +// 测试链接 : https://leetcode.cn/problems/sliding-puzzle/ +public class Code02_SidingPuzzle2 { + + public static int[] status = { 12345, 12354, 12435, 12453, 12534, 12543, 13245, 13254, 13425, 13452, 13524, 13542, + 14235, 14253, 14325, 14352, 14523, 14532, 15234, 15243, 15324, 15342, 15423, 15432, 21345, 21354, 21435, + 21453, 21534, 21543, 23145, 23154, 23415, 23451, 23514, 23541, 24135, 24153, 24315, 24351, 24513, 24531, + 25134, 25143, 25314, 25341, 25413, 25431, 31245, 31254, 31425, 31452, 31524, 31542, 32145, 32154, 32415, + 32451, 32514, 32541, 34125, 34152, 34215, 34251, 34512, 34521, 35124, 35142, 35214, 35241, 35412, 35421, + 41235, 41253, 41325, 41352, 41523, 41532, 42135, 42153, 42315, 42351, 42513, 42531, 43125, 43152, 43215, + 43251, 43512, 43521, 45123, 45132, 45213, 45231, 45312, 45321, 51234, 51243, 51324, 51342, 51423, 51432, + 52134, 52143, 52314, 52341, 52413, 52431, 53124, 53142, 53214, 53241, 53412, 53421, 54123, 54132, 54213, + 54231, 54312, 54321, 102345, 102354, 102435, 102453, 102534, 102543, 103245, 103254, 103425, 103452, 103524, + 103542, 104235, 104253, 104325, 104352, 104523, 104532, 105234, 105243, 105324, 105342, 105423, 105432, + 120345, 120354, 120435, 120453, 120534, 120543, 123045, 123054, 123405, 123450, 123504, 123540, 124035, + 124053, 124305, 124350, 124503, 124530, 125034, 125043, 125304, 125340, 125403, 125430, 130245, 130254, + 130425, 130452, 130524, 130542, 132045, 132054, 132405, 132450, 132504, 132540, 134025, 134052, 134205, + 134250, 134502, 134520, 135024, 135042, 135204, 135240, 135402, 135420, 140235, 140253, 140325, 140352, + 140523, 140532, 142035, 142053, 142305, 142350, 142503, 142530, 143025, 143052, 143205, 143250, 143502, + 143520, 145023, 145032, 145203, 145230, 145302, 145320, 150234, 150243, 150324, 150342, 150423, 150432, + 152034, 152043, 152304, 152340, 152403, 152430, 153024, 153042, 153204, 153240, 153402, 153420, 154023, + 154032, 154203, 154230, 154302, 154320, 201345, 201354, 201435, 201453, 201534, 201543, 203145, 203154, + 203415, 203451, 203514, 203541, 204135, 204153, 204315, 204351, 204513, 204531, 205134, 205143, 205314, + 205341, 205413, 205431, 210345, 210354, 210435, 210453, 210534, 210543, 213045, 213054, 213405, 213450, + 213504, 213540, 214035, 214053, 214305, 214350, 214503, 214530, 215034, 215043, 215304, 215340, 215403, + 215430, 230145, 230154, 230415, 230451, 230514, 230541, 231045, 231054, 231405, 231450, 231504, 231540, + 234015, 234051, 234105, 234150, 234501, 234510, 235014, 235041, 235104, 235140, 235401, 235410, 240135, + 240153, 240315, 240351, 240513, 240531, 241035, 241053, 241305, 241350, 241503, 241530, 243015, 243051, + 243105, 243150, 243501, 243510, 245013, 245031, 245103, 245130, 245301, 245310, 250134, 250143, 250314, + 250341, 250413, 250431, 251034, 251043, 251304, 251340, 251403, 251430, 253014, 253041, 253104, 253140, + 253401, 253410, 254013, 254031, 254103, 254130, 254301, 254310, 301245, 301254, 301425, 301452, 301524, + 301542, 302145, 302154, 302415, 302451, 302514, 302541, 304125, 304152, 304215, 304251, 304512, 304521, + 305124, 305142, 305214, 305241, 305412, 305421, 310245, 310254, 310425, 310452, 310524, 310542, 312045, + 312054, 312405, 312450, 312504, 312540, 314025, 314052, 314205, 314250, 314502, 314520, 315024, 315042, + 315204, 315240, 315402, 315420, 320145, 320154, 320415, 320451, 320514, 320541, 321045, 321054, 321405, + 321450, 321504, 321540, 324015, 324051, 324105, 324150, 324501, 324510, 325014, 325041, 325104, 325140, + 325401, 325410, 340125, 340152, 340215, 340251, 340512, 340521, 341025, 341052, 341205, 341250, 341502, + 341520, 342015, 342051, 342105, 342150, 342501, 342510, 345012, 345021, 345102, 345120, 345201, 345210, + 350124, 350142, 350214, 350241, 350412, 350421, 351024, 351042, 351204, 351240, 351402, 351420, 352014, + 352041, 352104, 352140, 352401, 352410, 354012, 354021, 354102, 354120, 354201, 354210, 401235, 401253, + 401325, 401352, 401523, 401532, 402135, 402153, 402315, 402351, 402513, 402531, 403125, 403152, 403215, + 403251, 403512, 403521, 405123, 405132, 405213, 405231, 405312, 405321, 410235, 410253, 410325, 410352, + 410523, 410532, 412035, 412053, 412305, 412350, 412503, 412530, 413025, 413052, 413205, 413250, 413502, + 413520, 415023, 415032, 415203, 415230, 415302, 415320, 420135, 420153, 420315, 420351, 420513, 420531, + 421035, 421053, 421305, 421350, 421503, 421530, 423015, 423051, 423105, 423150, 423501, 423510, 425013, + 425031, 425103, 425130, 425301, 425310, 430125, 430152, 430215, 430251, 430512, 430521, 431025, 431052, + 431205, 431250, 431502, 431520, 432015, 432051, 432105, 432150, 432501, 432510, 435012, 435021, 435102, + 435120, 435201, 435210, 450123, 450132, 450213, 450231, 450312, 450321, 451023, 451032, 451203, 451230, + 451302, 451320, 452013, 452031, 452103, 452130, 452301, 452310, 453012, 453021, 453102, 453120, 453201, + 453210, 501234, 501243, 501324, 501342, 501423, 501432, 502134, 502143, 502314, 502341, 502413, 502431, + 503124, 503142, 503214, 503241, 503412, 503421, 504123, 504132, 504213, 504231, 504312, 504321, 510234, + 510243, 510324, 510342, 510423, 510432, 512034, 512043, 512304, 512340, 512403, 512430, 513024, 513042, + 513204, 513240, 513402, 513420, 514023, 514032, 514203, 514230, 514302, 514320, 520134, 520143, 520314, + 520341, 520413, 520431, 521034, 521043, 521304, 521340, 521403, 521430, 523014, 523041, 523104, 523140, + 523401, 523410, 524013, 524031, 524103, 524130, 524301, 524310, 530124, 530142, 530214, 530241, 530412, + 530421, 531024, 531042, 531204, 531240, 531402, 531420, 532014, 532041, 532104, 532140, 532401, 532410, + 534012, 534021, 534102, 534120, 534201, 534210, 540123, 540132, 540213, 540231, 540312, 540321, 541023, + 541032, 541203, 541230, 541302, 541320, 542013, 542031, 542103, 542130, 542301, 542310, 543012, 543021, + 543102, 543120, 543201, 543210 }; + + public static int[] ans = { 15, -1, -1, 3, 15, -1, -1, 15, 3, -1, -1, 17, 17, -1, -1, 15, 13, -1, -1, 17, 13, -1, + -1, 7, -1, 21, 15, -1, -1, 13, 3, -1, -1, 11, 11, -1, -1, 9, 15, -1, -1, 15, 9, -1, -1, 15, 17, -1, 15, -1, + -1, 11, 19, -1, -1, 13, 17, -1, -1, 19, 13, -1, -1, 15, 13, -1, -1, 7, 9, -1, -1, 11, -1, 9, 13, -1, -1, 9, + 17, -1, -1, 15, 7, -1, -1, 17, 7, -1, -1, 11, 19, -1, -1, 11, 11, -1, 17, -1, -1, 13, 11, -1, -1, 5, 13, -1, + -1, 15, 11, -1, -1, 15, 17, -1, -1, 11, 11, -1, -1, 15, 14, -1, -1, 2, 14, -1, -1, 14, 2, -1, -1, 16, 16, + -1, -1, 14, 12, -1, -1, 16, 12, -1, -1, 6, 13, -1, -1, 1, 13, -1, 2, -1, 1, 0, -1, -1, -1, 10, -1, -1, 11, + 12, 10, -1, 11, 12, -1, -1, -1, 15, 3, -1, -1, 17, -1, 14, -1, -1, 15, 16, 14, -1, 15, 16, -1, -1, -1, 6, + -1, -1, 5, 4, 17, -1, -1, 15, 13, -1, 16, -1, 15, 16, -1, -1, -1, 16, -1, -1, 15, 14, 18, -1, 17, 18, -1, + -1, -1, 15, 13, -1, -1, 5, -1, 4, -1, -1, 3, 4, 12, -1, 13, 14, -1, -1, -1, 12, -1, -1, 13, 14, -1, 20, 16, + -1, -1, 12, 4, -1, -1, 12, 12, -1, -1, 8, 16, -1, -1, 14, 8, -1, -1, 16, 16, -1, -1, 19, 17, -1, -1, 13, -1, + 14, -1, -1, 13, 14, 18, -1, 17, 18, -1, -1, -1, 18, -1, -1, 17, 18, 5, -1, -1, 13, 13, -1, 16, -1, 15, 14, + -1, -1, -1, 16, -1, -1, 15, 14, 8, -1, 7, 6, -1, -1, -1, 7, 15, -1, -1, 13, -1, 10, -1, -1, 11, 12, 6, -1, + 5, 6, -1, -1, -1, 12, -1, -1, 13, 14, 9, -1, -1, 15, 15, -1, 18, -1, 19, 16, -1, -1, -1, 14, -1, -1, 13, 14, + 10, -1, 9, 10, -1, -1, 14, -1, -1, 12, 18, -1, -1, 12, 16, -1, -1, 18, 12, -1, -1, 14, 14, -1, -1, 8, 10, + -1, -1, 12, 13, -1, -1, 13, 17, -1, 16, -1, 15, 14, -1, -1, -1, 16, -1, -1, 15, 16, 12, -1, 11, 12, -1, -1, + -1, 13, 15, -1, -1, 19, -1, 20, -1, -1, 19, 20, 14, -1, 13, 14, -1, -1, -1, 14, -1, -1, 13, 14, 11, -1, -1, + 15, 15, -1, 14, -1, 15, 16, -1, -1, -1, 16, -1, -1, 17, 16, 10, -1, 9, 10, -1, -1, -1, 9, 11, -1, -1, 13, + -1, 14, -1, -1, 13, 14, 12, -1, 11, 10, -1, -1, -1, 14, -1, -1, 13, 12, -1, 8, 12, -1, -1, 8, 18, -1, -1, + 14, 6, -1, -1, 18, 6, -1, -1, 10, 20, -1, -1, 10, 10, -1, -1, 7, 11, -1, -1, 7, -1, 4, -1, -1, 5, 6, 4, -1, + 5, 6, -1, -1, -1, 8, -1, -1, 9, 10, 19, -1, -1, 15, 7, -1, 14, -1, 13, 14, -1, -1, -1, 10, -1, -1, 9, 8, 18, + -1, 19, 20, -1, -1, -1, 19, 7, -1, -1, 11, -1, 10, -1, -1, 9, 10, 18, -1, 19, 20, -1, -1, -1, 10, -1, -1, 9, + 8, 21, -1, -1, 11, 11, -1, 10, -1, 9, 10, -1, -1, -1, 14, -1, -1, 13, 12, 18, -1, 19, 20, -1, -1, 16, -1, + -1, 12, 12, -1, -1, 6, 14, -1, -1, 16, 10, -1, -1, 14, 16, -1, -1, 10, 12, -1, -1, 14, 15, -1, -1, 13, 13, + -1, 16, -1, 15, 14, -1, -1, -1, 16, -1, -1, 15, 14, 14, -1, 13, 14, -1, -1, -1, 7, 15, -1, -1, 15, -1, 14, + -1, -1, 13, 14, 10, -1, 9, 8, -1, -1, -1, 16, -1, -1, 15, 16, 11, -1, -1, 15, 17, -1, 18, -1, 17, 18, -1, + -1, -1, 18, -1, -1, 17, 18, 12, -1, 11, 12, -1, -1, -1, 9, 13, -1, -1, 13, -1, 10, -1, -1, 11, 12, 8, -1, 7, + 8, -1, -1, -1, 12, -1, -1, 13, 14 }; + + public static int slidingPuzzle(int[][] board) { + int from = board[0][0] * 100000 + board[0][1] * 10000 + board[0][2] * 1000 + board[1][0] * 100 + + board[1][1] * 10 + board[1][2]; + return ans[find(from)]; + } + + public static int find(int num) { + int ans = 0; + int l = 0; + int r = status.length - 1; + int m = 0; + while (l <= r) { + m = (l + r) / 2; + if (status[m] == num) { + ans = m; + break; + } else if (status[m] > num) { + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_07_4_week/Code03_TheNumberOfGoodSubsets.java b/算法周更班/class_2022_07_4_week/Code03_TheNumberOfGoodSubsets.java new file mode 100644 index 0000000..9ab791b --- /dev/null +++ b/算法周更班/class_2022_07_4_week/Code03_TheNumberOfGoodSubsets.java @@ -0,0 +1,49 @@ +package class_2022_07_4_week; + +import java.util.Arrays; + +// 测试链接 : https://leetcode.cn/problems/the-number-of-good-subsets/ +public class Code03_TheNumberOfGoodSubsets { + + // 2, 3, 5, 6, 7, 10, 11, 13, 14, + // 15, 17, 19, 21, 22, 23, 26, 29, 30 + public static int[] primes = { + 0, 0, 1, 2, 0, 4, 3, 8, 0, 0, + 5, 16, 0, 32, 9, 6, 0, 64, 0, 128, + 0, 10, 17, 256, 0, 0, 33, 0, 0, 512, 7 }; + + public static int[] counts = new int[31]; + + public static int[] status = new int[1 << 10]; + + public static int mod = 1000000007; + + public static int numberOfGoodSubsets(int[] nums) { + Arrays.fill(counts, 0); + Arrays.fill(status, 0); + for (int num : nums) { + counts[num]++; + } + status[0] = 1; + for (int i = 0; i < counts[1]; i++) { + status[0] = (status[0] << 1) % mod; + } + for (int i = 2; i <= 30; i++) { + int cur = primes[i]; + if (cur != 0 && counts[i] != 0) { + for (int from = 0; from < (1 << 10); from++) { + if ((from & cur) == 0) { + int to = from | cur; + status[to] = (int) (((long) status[to] + ((long) status[from] * counts[i])) % mod); + } + } + } + } + int ans = 0; + for (int s = 1; s < (1 << 10); s++) { + ans = (ans + status[s]) % mod; + } + return ans; + } + +} diff --git a/算法周更班/class_2022_07_4_week/Code04_MatchsticksToSquare.java b/算法周更班/class_2022_07_4_week/Code04_MatchsticksToSquare.java new file mode 100644 index 0000000..d57cb40 --- /dev/null +++ b/算法周更班/class_2022_07_4_week/Code04_MatchsticksToSquare.java @@ -0,0 +1,40 @@ +package class_2022_07_4_week; + +// 测试链接 : https://leetcode.cn/problems/matchsticks-to-square/ +public class Code04_MatchsticksToSquare { + + public static boolean makesquare(int[] matchsticks) { + int sum = 0; + for (int num : matchsticks) { + sum += num; + } + if ((sum & 3) != 0) { + return false; + } + int[] dp = new int[1 << matchsticks.length]; + return process(matchsticks, 0, 0, sum >> 2, 4, dp); + } + + public static boolean process(int[] arr, int status, int cur, int len, int edges, int[] dp) { + if (dp[status] != 0) { + return dp[status] == 1; + } + boolean ans = false; + if (edges == 0) { + ans = (status == (1 << arr.length) - 1) ? true : false; + } else { + for (int i = 0; i < arr.length && !ans; i++) { + if (((1 << i) & status) == 0 && cur + arr[i] <= len) { + if (cur + arr[i] == len) { + ans |= process(arr, status | (1 << i), 0, len, edges - 1, dp); + } else { + ans |= process(arr, status | (1 << i), cur + arr[i], len, edges, dp); + } + } + } + } + dp[status] = ans ? 1 : -1; + return ans; + } + +} diff --git a/算法新手班/class01/Code01_PrintBinary.java b/算法新手班/class01/Code01_PrintBinary.java new file mode 100644 index 0000000..c66a968 --- /dev/null +++ b/算法新手班/class01/Code01_PrintBinary.java @@ -0,0 +1,65 @@ +package class01; + +public class Code01_PrintBinary { + + public static void print(int num) { + for (int i = 31; i >= 0; i--) { + System.out.print((num & (1 << i)) == 0 ? "0" : "1"); + } + System.out.println(); + } + + public static void main(String[] args) { + // 32位 +// int num = 4; +// +// print(num); +// +// +// int test = 1123123; +// print(test); +// print(test<<1); +// print(test<<2); +// print(test<<8); +// +// +// int a = Integer.MAX_VALUE; +// System.out.println(a); + +// print(-1); +// int a = Integer.MIN_VALUE; +// print(a); + +// int b = 123823138; +// int c = ~b; +// print(b); +// print(c); + +// print(-5); + +// System.out.println(Integer.MIN_VALUE); +// System.out.println(Integer.MAX_VALUE); + +// int a = 12319283; +// int b = 3819283; +// print(a); +// print(b); +// System.out.println("============="); +// print(a | b); +// print(a & b); +// print(a ^ b); + +// int a = Integer.MIN_VALUE; +// print(a); +// print(a >> 1); +// print(a >>> 1); +// +// int c = Integer.MIN_VALUE; +// int d = -c ; +// +// print(c); +// print(d); + + } + +} diff --git a/算法新手班/class01/Code02_SumOfFactorial.java b/算法新手班/class01/Code02_SumOfFactorial.java new file mode 100644 index 0000000..cc55c53 --- /dev/null +++ b/算法新手班/class01/Code02_SumOfFactorial.java @@ -0,0 +1,37 @@ +package class01; + +public class Code02_SumOfFactorial { + + public static long f1(int N) { + long ans = 0; + for (int i = 1; i <= N; i++) { + ans += factorial(i); + } + return ans; + } + + public static long factorial(int N) { + long ans = 1; + for (int i = 1; i <= N; i++) { + ans *= i; + } + return ans; + } + + public static long f2(int N) { + long ans = 0; + long cur = 1; + for (int i = 1; i <= N; i++) { + cur = cur * i; + ans += cur; + } + return ans; + } + + public static void main(String[] args) { + int N = 10; + System.out.println(f1(N)); + System.out.println(f2(N)); + } + +} diff --git a/算法新手班/class01/Code03_Sort.java b/算法新手班/class01/Code03_Sort.java new file mode 100644 index 0000000..b5db68a --- /dev/null +++ b/算法新手班/class01/Code03_Sort.java @@ -0,0 +1,79 @@ +package class01; + +public class Code03_Sort { + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[j]; + arr[j] = arr[i]; + arr[i] = tmp; + } + + public static void selectSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int N = arr.length; + for (int i = 0; i < N; i++) { + int minValueIndex = i; + for (int j = i + 1; j < N; j++) { + minValueIndex = arr[j] < arr[minValueIndex] ? j : minValueIndex; + } + swap(arr, i, minValueIndex); + } + } + + public static void bubbleSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int N = arr.length; + for (int end = N - 1; end >= 0; end--) { + for (int second = 1; second <= end; second++) { + if (arr[second - 1] > arr[second]) { + swap(arr, second - 1, second); + } + } + } + } + + public static void insertSort1(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int N = arr.length; + for (int end = 1; end < N; end++) { + int newNumIndex = end; + while (newNumIndex - 1 >= 0 && arr[newNumIndex - 1] > arr[newNumIndex]) { + swap(arr, newNumIndex - 1, newNumIndex); + newNumIndex--; + } + } + } + + public static void insertSort2(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int N = arr.length; + for (int end = 1; end < N; end++) { + for (int pre = end - 1; pre >= 0 && arr[pre] > arr[pre + 1]; pre--) { + swap(arr, pre, pre + 1); + } + } + } + + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int[] arr = { 7, 1, 3, 5, 1, 6, 8, 1, 3, 5, 7, 5, 6 }; + printArray(arr); + insertSort2(arr); + printArray(arr); + } + +} diff --git a/算法新手班/class01/Code04_SelectionSort.java b/算法新手班/class01/Code04_SelectionSort.java new file mode 100644 index 0000000..8755706 --- /dev/null +++ b/算法新手班/class01/Code04_SelectionSort.java @@ -0,0 +1,114 @@ +package class01; + +import java.util.Arrays; + +public class Code04_SelectionSort { + + public static void selectionSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + for (int i = 0; i < arr.length - 1; i++) { + int minIndex = i; + for (int j = i + 1; j < arr.length; j++) { + if(arr[j] < arr[minIndex]) { + minIndex = j; + } + } + swap(arr, i, minIndex); + } + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + // Math.random() [0,1) + // Math.random() * N [0,N) + // (int)(Math.random() * N) [0, N-1] + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + // [-? , +?] + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + selectionSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + succeed = false; + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + selectionSort(arr); + printArray(arr); + } + +} diff --git a/算法新手班/class01/Code05_BubbleSort.java b/算法新手班/class01/Code05_BubbleSort.java new file mode 100644 index 0000000..32537d3 --- /dev/null +++ b/算法新手班/class01/Code05_BubbleSort.java @@ -0,0 +1,107 @@ +package class01; + +import java.util.Arrays; + +public class Code05_BubbleSort { + + public static void bubbleSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + for (int end = arr.length - 1; end > 0; end--) { + for (int i = 0; i < end; i++) { + if (arr[i] > arr[i + 1]) { + swap(arr, i, i + 1); + } + } + } + } + + // 交换arr的i和j位置上的值 + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + bubbleSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + bubbleSort(arr); + printArray(arr); + } + +} diff --git a/算法新手班/class01/Code06_InsertionSort.java b/算法新手班/class01/Code06_InsertionSort.java new file mode 100644 index 0000000..bdf2232 --- /dev/null +++ b/算法新手班/class01/Code06_InsertionSort.java @@ -0,0 +1,111 @@ +package class01; + +import java.util.Arrays; + +public class Code06_InsertionSort { + + public static void insertionSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序 + for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) { + swap(arr, j, j + 1); + } + } + } + + // i和j,数交换 + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // for test + public static void comparator(int[] arr) { + Arrays.sort(arr); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + // Math.random() -> [0,1) 所有的小数,等概率返回一个 + // Math.random() * N -> [0,N) 所有小数,等概率返回一个 + // (int)(Math.random() * N) -> [0,N-1] 所有的整数,等概率返回一个 + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; // 长度随机 + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) + - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; // 随机数组的长度0~100 + int maxValue = 100;// 值:-100~100 + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + insertionSort(arr1); + comparator(arr2); + if (!isEqual(arr1, arr2)) { + // 打印arr1 + // 打印arr2 + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + + int[] arr = generateRandomArray(maxSize, maxValue); + printArray(arr); + insertionSort(arr); + printArray(arr); + } + +} diff --git a/算法新手班/class02/Code01_PreSum.java b/算法新手班/class02/Code01_PreSum.java new file mode 100644 index 0000000..39dcbd1 --- /dev/null +++ b/算法新手班/class02/Code01_PreSum.java @@ -0,0 +1,42 @@ +package class02; + +public class Code01_PreSum { + + public static class RangeSum1 { + + private int[] arr; + + public RangeSum1(int[] array) { + arr = array; + } + + public int rangeSum(int L, int R) { + int sum = 0; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + return sum; + } + + } + + public static class RangeSum2 { + + private int[] preSum; + + public RangeSum2(int[] array) { + int N = array.length; + preSum = new int[N]; + preSum[0] = array[0]; + for (int i = 1; i < N; i++) { + preSum[i] = preSum[i - 1] + array[i]; + } + } + + public int rangeSum(int L, int R) { + return L == 0 ? preSum[R] : preSum[R] - preSum[L - 1]; + } + + } + +} diff --git a/算法新手班/class02/Code02_RandToRand.java b/算法新手班/class02/Code02_RandToRand.java new file mode 100644 index 0000000..87dbc82 --- /dev/null +++ b/算法新手班/class02/Code02_RandToRand.java @@ -0,0 +1,231 @@ +package class02; + +public class Code02_RandToRand { + + // 此函数只能用,不能修改 + // 等概率返回1~5 + public static int f() { + return (int) (Math.random() * 5) + 1; + } + + // 等概率得到0和1 + public static int a() { + int ans = 0; + do { + ans = f(); + } while (ans == 3); + return ans < 3 ? 0 : 1; + } + + // 等概率返回0~6 + public static int b() { + int ans = 0; + do { + ans = (a() << 2) + (a() << 1) + a(); + } while (ans == 7); + return ans; + } + + // 等概率返回1~7 + public static int c() { + return b() + 1; + } + + // 这个结构是唯一的随机机制 + // 你只能初始化并使用,不可修改 + public static class RandomBox { + private final int min; + private final int max; + + // 初始化时请一定不要让mi==ma + public RandomBox(int mi, int ma) { + min = mi; + max = ma; + } + + // 13 ~ 17 + // 13 + [0,4] + public int random() { + return min + (int) (Math.random() * (max - min + 1)); + } + + public int min() { + return min; + } + + public int max() { + return max; + } + } + + // 利用条件RandomBox,如何等概率返回0和1 + public static int rand01(RandomBox randomBox) { + int min = randomBox.min(); + int max = randomBox.max(); + // min ~ max + int size = max - min + 1; + // size是不是奇数,odd 奇数 + boolean odd = (size & 1) != 0; + int mid = size / 2; + int ans = 0; + do { + ans = randomBox.random() - min; + } while (odd && ans == mid); + return ans < mid ? 0 : 1; + } + + // 给你一个RandomBox,这是唯一能借助的随机机制 + // 等概率返回from~to范围上任何一个数 + // 要求from<=to + public static int random(RandomBox randomBox, int from, int to) { + if (from == to) { + return from; + } + // 3 ~ 9 + // 0 ~ 6 + // 0 ~ range + int range = to - from; + int num = 1; + // 求0~range需要几个2进制位 + while ((1 << num) - 1 < range) { + num++; + } + + // 我们一共需要num位 + // 最终的累加和,首先+0位上是1还是0,1位上是1还是0,2位上是1还是0... + int ans = 0; + do { + ans = 0; + for (int i = 0; i < num; i++) { + ans |= (rand01(randomBox) << i); + } + } while (ans > range); + return ans + from; + } + + public static void main(String[] args) { + System.out.println("测试开始"); + // Math.random() -> double -> [0,1) + // + + int testTimes = 10000000; + int count = 0; + for (int i = 0; i < testTimes; i++) { + if (Math.random() < 0.75) { + count++; + } + } + System.out.println((double) count / (double) testTimes); + + System.out.println("========="); + + // [0,1) -> [0,8) + count = 0; + for (int i = 0; i < testTimes; i++) { + if (Math.random() * 8 < 5) { + count++; + } + } + System.out.println((double) count / (double) testTimes); + System.out.println((double) 5 / (double) 8); + + int K = 9; + // [0,K) -> [0,8] + + int[] counts = new int[9]; + for (int i = 0; i < testTimes; i++) { + int ans = (int) (Math.random() * K); // [0,K-1] + counts[ans]++; + } + for (int i = 0; i < K; i++) { + System.out.println(i + "这个数,出现了 " + counts[i] + " 次"); + } + + System.out.println("========="); + + count = 0; + double x = 0.17; + for (int i = 0; i < testTimes; i++) { + if (xToXPower2() < x) { + count++; + } + } + System.out.println((double) count / (double) testTimes); + System.out.println((double) 1 - Math.pow((double) 1 - x, 2)); + + System.out.println("=========="); + count = 0; + for (int i = 0; i < testTimes; i++) { + if (f2() == 0) { + count++; + } + } + System.out.println((double) count / (double) testTimes); + + System.out.println("=========="); + + counts = new int[8]; + for (int i = 0; i < testTimes; i++) { + int num = g(); + counts[num]++; + } + for (int i = 0; i < 8; i++) { + System.out.println(i + "这个数,出现了 " + counts[i] + " 次"); + } + + } + + // 返回[0,1)的一个小数 + // 任意的x,x属于[0,1),[0,x)范围上的数出现概率由原来的x调整成x平方 + public static double xToXPower2() { + return Math.min(Math.random(), Math.random()); + } + + // lib里的,不能改! + public static int f1() { + return (int) (Math.random() * 5) + 1; + } + + // 随机机制,只能用f1, + // 等概率返回0和1 + public static int f2() { + int ans = 0; + do { + ans = f1(); + } while (ans == 3); + return ans < 3 ? 0 : 1; + } + + // 得到000 ~ 111 做到等概率 0 ~ 7等概率返回一个 + public static int f3() { + return (f2() << 2) + (f2() << 1) + f2(); + } + + // 0 ~ 6等概率返回一个 + public static int f4() { + int ans = 0; + do { + ans = f3(); + } while (ans == 7); + return ans; + } + + public static int g() { + return f4() + 1; + } + + // 你只能知道,x会以固定概率返回0和1,但是x的内容,你看不到! + public static int x() { + return Math.random() < 0.84 ? 0 : 1; + } + + // 等概率返回0和1 + public static int y() { + int ans = 0; + do { + ans = x(); + } while (ans == x()); + return ans; + } + +} diff --git a/算法新手班/class02/Code03_Comp.java b/算法新手班/class02/Code03_Comp.java new file mode 100644 index 0000000..133327e --- /dev/null +++ b/算法新手班/class02/Code03_Comp.java @@ -0,0 +1,90 @@ +package class02; + +public class Code03_Comp { + + public static void selectionSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + for (int i = 0; i < arr.length - 1; i++) { + int minIndex = i; + for (int j = i + 1; j < arr.length; j++) { + if (arr[j] < arr[minIndex]) { + minIndex = j; + } + } + swap(arr, i, minIndex); + } + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + public static void insertionSort(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序 + for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) { + swap(arr, j, j + 1); + } + } + } + + // 返回一个数组arr,arr长度[0,maxLen-1],arr中的每个值[0,maxValue-1] + public static int[] lenRandomValueRandom(int maxLen, int maxValue) { + int len = (int) (Math.random() * maxLen); + int[] ans = new int[len]; + for (int i = 0; i < len; i++) { + ans[i] = (int) (Math.random() * maxValue); + } + return ans; + } + + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // arr1和arr2一定等长 + public static boolean isSorted(int[] arr) { + if (arr.length < 2) { + return true; + } + int max = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (max > arr[i]) { + return false; + } + max = Math.max(max, arr[i]); + } + return true; + } + + public static void main(String[] args) { + int maxLen = 5; + int maxValue = 1000; + int testTime = 10000; + for (int i = 0; i < testTime; i++) { + int[] arr1 = lenRandomValueRandom(maxLen, maxValue); + int[] tmp = copyArray(arr1); + selectionSort(arr1); + if (!isSorted(arr1)) { + for (int j = 0; j < tmp.length; j++) { + System.out.print(tmp[j] + " "); + } + System.out.println(); + System.out.println("选择排序错了!"); + break; + } + } + + } + +} diff --git a/算法新手班/class02/Code03_EqualProbabilityRandom.java b/算法新手班/class02/Code03_EqualProbabilityRandom.java new file mode 100644 index 0000000..fafb424 --- /dev/null +++ b/算法新手班/class02/Code03_EqualProbabilityRandom.java @@ -0,0 +1,67 @@ +package class02; + +public class Code03_EqualProbabilityRandom { + + // 内部内容不可见 + public static int f() { + return Math.random() < 0.8 ? 0 : 1; + } + + // 等概率返回0和1 + public static int g() { + int first = 0; + do { + first = f(); // 0 1 + } while (first == f()); + return first; + } + + // 这个结构是唯一的随机机制 + // 你只能初始化并使用,不可修改 + public static class RandomBox { + private final double p; + + // 初始化时请一定满足:0 < zeroP < 1 + public RandomBox(double zeroP) { + p = zeroP; + } + + public int random() { + return Math.random() < p ? 0 : 1; + } + + } + + // 底层依赖一个以p概率返回0,以1-p概率返回1的随机函数rand01p + // 如何加工出等概率返回0和1的函数 + public static int rand01(RandomBox randomBox) { + int num; + do { + num = randomBox.random(); + } while (num == randomBox.random()); + return num; + } + + public static void main(String[] args) { + int[] count = new int[2];// 0 1 + for (int i = 0; i < 1000000; i++) { + int ans = g(); + count[ans]++; + } + System.out.println(count[0] + " , " + count[1]); + +// double zeroP = 0.88; +// RandomBox randomBox = new RandomBox(zeroP); +// +// int testTime = 10000000; +// int count = 0; +// for (int i = 0; i < testTime; i++) { +// if (rand01(randomBox) == 0) { +// count++; +// } +// } +// System.out.println((double) count / (double) testTime); + + } + +} diff --git a/算法新手班/class03/Code01_BSExist.java b/算法新手班/class03/Code01_BSExist.java new file mode 100644 index 0000000..6385f24 --- /dev/null +++ b/算法新手班/class03/Code01_BSExist.java @@ -0,0 +1,64 @@ +package class03; + +import java.util.Arrays; + +public class Code01_BSExist { + + // arr保证有序 + public static boolean find(int[] arr, int num) { + if (arr == null || arr.length == 0) { + return false; + } + int L = 0; + int R = arr.length - 1; + while (L <= R) { + int mid = (L + R) / 2; + if (arr[mid] == num) { + return true; + } else if (arr[mid] < num) { + L = mid + 1; + } else { + R = mid - 1; + } + } + return false; + } + + // for test + public static boolean test(int[] sortedArr, int num) { + for (int cur : sortedArr) { + if (cur == num) { + return true; + } + } + return false; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 10; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + Arrays.sort(arr); + int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + if (test(arr, value) != find(arr, value)) { + System.out.println("出错了!"); + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/算法新手班/class03/Code02_BSNearLeft.java b/算法新手班/class03/Code02_BSNearLeft.java new file mode 100644 index 0000000..7096d3d --- /dev/null +++ b/算法新手班/class03/Code02_BSNearLeft.java @@ -0,0 +1,78 @@ +package class03; + +import java.util.Arrays; + +public class Code02_BSNearLeft { + + // arr有序的,>=num 最左 + public static int mostLeftNoLessNumIndex(int[] arr, int num) { + if (arr == null || arr.length == 0) { + return -1; + } + int L = 0; + int R = arr.length - 1; + int ans = -1; + while (L <= R) { + int mid = (L + R) / 2; + if (arr[mid] >= num) { + ans = mid; + R = mid - 1; + } else { + L = mid + 1; + } + } + return ans; + } + + // for test + public static int test(int[] arr, int value) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] >= value) { + return i; + } + } + return -1; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 10; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + Arrays.sort(arr); + int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + if (test(arr, value) != mostLeftNoLessNumIndex(arr, value)) { + printArray(arr); + System.out.println(value); + System.out.println(test(arr, value)); + System.out.println(mostLeftNoLessNumIndex(arr, value)); + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/算法新手班/class03/Code03_BSNearRight.java b/算法新手班/class03/Code03_BSNearRight.java new file mode 100644 index 0000000..4bf52e2 --- /dev/null +++ b/算法新手班/class03/Code03_BSNearRight.java @@ -0,0 +1,75 @@ +package class03; + +import java.util.Arrays; + +public class Code03_BSNearRight { + + // 在arr上,找满足<=value的最右位置 + public static int nearestIndex(int[] arr, int value) { + int L = 0; + int R = arr.length - 1; + int index = -1; // 记录最右的对号 + while (L <= R) { + int mid = L + ((R - L) >> 1); + if (arr[mid] <= value) { + index = mid; + L = mid + 1; + } else { + R = mid - 1; + } + } + return index; + } + + // for test + public static int test(int[] arr, int value) { + for (int i = arr.length - 1; i >= 0; i--) { + if (arr[i] <= value) { + return i; + } + } + return -1; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 10; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + Arrays.sort(arr); + int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + if (test(arr, value) != nearestIndex(arr, value)) { + printArray(arr); + System.out.println(value); + System.out.println(test(arr, value)); + System.out.println(nearestIndex(arr, value)); + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/算法新手班/class03/Code04_BSAwesome.java b/算法新手班/class03/Code04_BSAwesome.java new file mode 100644 index 0000000..f40b167 --- /dev/null +++ b/算法新手班/class03/Code04_BSAwesome.java @@ -0,0 +1,90 @@ +package class03; + +public class Code04_BSAwesome { + + // arr 相邻的数不相等! + public static int oneMinIndex(int[] arr) { + if (arr == null || arr.length == 0) { + return -1; + } + int N = arr.length; + if (N == 1) { + return 0; + } + if (arr[0] < arr[1]) { + return 0; + } + if (arr[N - 1] < arr[N - 2]) { + return N - 1; + } + int L = 0; + int R = N - 1; + // L...R 肯定有局部最小 + while (L < R - 1) { + int mid = (L + R) / 2; + if (arr[mid] < arr[mid - 1] && arr[mid] < arr[mid + 1]) { + return mid; + } else { + if (arr[mid] > arr[mid - 1]) { + R = mid - 1; + } else { + L = mid + 1; + } + } + } + return arr[L] < arr[R] ? L : R; + } + + // 生成随机数组,且相邻数不相等 + public static int[] randomArray(int maxLen, int maxValue) { + int len = (int) (Math.random() * maxLen); + int[] arr = new int[len]; + if (len > 0) { + arr[0] = (int) (Math.random() * maxValue); + for (int i = 1; i < len; i++) { + do { + arr[i] = (int) (Math.random() * maxValue); + } while (arr[i] == arr[i - 1]); + } + } + return arr; + } + + // 也用于测试 + public static boolean check(int[] arr, int minIndex) { + if (arr.length == 0) { + return minIndex == -1; + } + int left = minIndex - 1; + int right = minIndex + 1; + boolean leftBigger = left >= 0 ? arr[left] > arr[minIndex] : true; + boolean rightBigger = right < arr.length ? arr[right] > arr[minIndex] : true; + return leftBigger && rightBigger; + } + + public static void printArray(int[] arr) { + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int maxLen = 100; + int maxValue = 200; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int ans = oneMinIndex(arr); + if (!check(arr, ans)) { + printArray(arr); + System.out.println(ans); + break; + } + } + System.out.println("测试结束"); + + } + +} diff --git a/算法新手班/class03/Code05_HashMapTreeMap.java b/算法新手班/class03/Code05_HashMapTreeMap.java new file mode 100644 index 0000000..244c1d4 --- /dev/null +++ b/算法新手班/class03/Code05_HashMapTreeMap.java @@ -0,0 +1,93 @@ +package class03; + +import java.util.HashMap; +import java.util.TreeMap; + +public class Code05_HashMapTreeMap { + + public static class Node { + public int value; + + public Node(int v) { + value = v; + } + } + + // (K V)表 + public static void main(String[] args) { + HashMap map = new HashMap<>(); + map.put("zuochengyun", "我是左程云"); + System.out.println(map.containsKey("zuochengyun")); + System.out.println(map.containsKey("zuo")); + System.out.println(map.get("zuochengyun")); + + map.put("zuochengyun", "他是左程云"); + System.out.println(map.get("zuochengyun")); + +// map.remove("zuochengyun"); +// System.out.println(map.containsKey("zuochengyun")); +// System.out.println(map.get("zuochengyun")); + + String test1 = "zuochengyun"; + String test2 = "zuochengyun"; + System.out.println(map.containsKey(test1)); + System.out.println(map.containsKey(test2)); + + HashMap map2 = new HashMap<>(); + map2.put(1234567, "我是1234567"); + + Integer a = 1234567; + Integer b = 1234567; + + System.out.println(a == b); + System.out.println(map2.containsKey(a)); + System.out.println(map2.containsKey(b)); + + Node node1 = new Node(1); + Node node2 = new Node(1); + HashMap map3 = new HashMap<>(); + map3.put(node1, "我进来了!"); + System.out.println(map3.containsKey(node1)); + System.out.println(map3.containsKey(node2)); + + System.out.println("==================="); + + TreeMap treeMap1 = new TreeMap<>(); + + treeMap1.put(3, "我是3"); + treeMap1.put(0, "我是3"); + treeMap1.put(7, "我是3"); + treeMap1.put(2, "我是3"); + treeMap1.put(5, "我是3"); + treeMap1.put(9, "我是3"); + + System.out.println(treeMap1.containsKey(7)); + System.out.println(treeMap1.containsKey(6)); + System.out.println(treeMap1.get(3)); + + treeMap1.put(3, "他是3"); + System.out.println(treeMap1.get(3)); + + treeMap1.remove(3); + System.out.println(treeMap1.get(3)); + + System.out.println(treeMap1.firstKey()); + System.out.println(treeMap1.lastKey()); + // <=5 离5最近的key告诉我 + System.out.println(treeMap1.floorKey(5)); + // <=6 离6最近的key告诉我 + System.out.println(treeMap1.floorKey(6)); + // >=5 离5最近的key告诉我 + System.out.println(treeMap1.ceilingKey(5)); + // >=6 离6最近的key告诉我 + System.out.println(treeMap1.ceilingKey(6)); + +// Node node3 = new Node(3); +// Node node4 = new Node(4); +// TreeMap treeMap2 = new TreeMap<>(); +// treeMap2.put(node3, "我是node3"); +// treeMap2.put(node4, "我是node4"); + + } + +} diff --git a/算法新手班/class04/Code01_ReverseList.java b/算法新手班/class04/Code01_ReverseList.java new file mode 100644 index 0000000..f641c9b --- /dev/null +++ b/算法新手班/class04/Code01_ReverseList.java @@ -0,0 +1,223 @@ +package class04; + +import java.util.ArrayList; +import java.util.List; + +public class Code01_ReverseList { + + public static class Node { + public int value; + public Node next; + + public Node(int data) { + value = data; + } + } + + public static class DoubleNode { + public int value; + public DoubleNode last; + public DoubleNode next; + + public DoubleNode(int data) { + value = data; + } + } + + public static Node reverseLinkedList(Node head) { + Node pre = null; + Node next = null; + while (head != null) { + next = head.next; + head.next = pre; + pre = head; + head = next; + } + return pre; + } + + public static DoubleNode reverseDoubleList(DoubleNode head) { + DoubleNode pre = null; + DoubleNode next = null; + while (head != null) { + next = head.next; + head.next = pre; + head.last = next; + pre = head; + head = next; + } + return pre; + } + + public static Node testReverseLinkedList(Node head) { + if (head == null) { + return null; + } + ArrayList list = new ArrayList<>(); + while (head != null) { + list.add(head); + head = head.next; + } + list.get(0).next = null; + int N = list.size(); + for (int i = 1; i < N; i++) { + list.get(i).next = list.get(i - 1); + } + return list.get(N - 1); + } + + public static DoubleNode testReverseDoubleList(DoubleNode head) { + if (head == null) { + return null; + } + ArrayList list = new ArrayList<>(); + while (head != null) { + list.add(head); + head = head.next; + } + list.get(0).next = null; + DoubleNode pre = list.get(0); + int N = list.size(); + for (int i = 1; i < N; i++) { + DoubleNode cur = list.get(i); + cur.last = null; + cur.next = pre; + pre.last = cur; + pre = cur; + } + return list.get(N - 1); + } + + // for test + public static Node generateRandomLinkedList(int len, int value) { + int size = (int) (Math.random() * (len + 1)); + if (size == 0) { + return null; + } + size--; + Node head = new Node((int) (Math.random() * (value + 1))); + Node pre = head; + while (size != 0) { + Node cur = new Node((int) (Math.random() * (value + 1))); + pre.next = cur; + pre = cur; + size--; + } + return head; + } + + // for test + public static DoubleNode generateRandomDoubleList(int len, int value) { + int size = (int) (Math.random() * (len + 1)); + if (size == 0) { + return null; + } + size--; + DoubleNode head = new DoubleNode((int) (Math.random() * (value + 1))); + DoubleNode pre = head; + while (size != 0) { + DoubleNode cur = new DoubleNode((int) (Math.random() * (value + 1))); + pre.next = cur; + cur.last = pre; + pre = cur; + size--; + } + return head; + } + + // for test + public static List getLinkedListOriginOrder(Node head) { + List ans = new ArrayList<>(); + while (head != null) { + ans.add(head.value); + head = head.next; + } + return ans; + } + + // for test + public static boolean checkLinkedListReverse(List origin, Node head) { + for (int i = origin.size() - 1; i >= 0; i--) { + if (!origin.get(i).equals(head.value)) { + return false; + } + head = head.next; + } + return true; + } + + // for test + public static List getDoubleListOriginOrder(DoubleNode head) { + List ans = new ArrayList<>(); + while (head != null) { + ans.add(head.value); + head = head.next; + } + return ans; + } + + // for test + public static boolean checkDoubleListReverse(List origin, DoubleNode head) { + DoubleNode end = null; + for (int i = origin.size() - 1; i >= 0; i--) { + if (!origin.get(i).equals(head.value)) { + return false; + } + end = head; + head = head.next; + } + for (int i = 0; i < origin.size(); i++) { + if (!origin.get(i).equals(end.value)) { + return false; + } + end = end.last; + } + return true; + } + + + public static void f(Node head) { + head = head.next; + } + + // for test + public static void main(String[] args) { + int len = 50; + int value = 100; + int testTime = 100000; + System.out.println("test begin!"); + for (int i = 0; i < testTime; i++) { + Node node1 = generateRandomLinkedList(len, value); + List list1 = getLinkedListOriginOrder(node1); + node1 = reverseLinkedList(node1); + if (!checkLinkedListReverse(list1, node1)) { + System.out.println("Oops1!"); + } + + Node node2 = generateRandomLinkedList(len, value); + List list2 = getLinkedListOriginOrder(node2); + node2 = testReverseLinkedList(node2); + if (!checkLinkedListReverse(list2, node2)) { + System.out.println("Oops2!"); + } + + DoubleNode node3 = generateRandomDoubleList(len, value); + List list3 = getDoubleListOriginOrder(node3); + node3 = reverseDoubleList(node3); + if (!checkDoubleListReverse(list3, node3)) { + System.out.println("Oops3!"); + } + + DoubleNode node4 = generateRandomDoubleList(len, value); + List list4 = getDoubleListOriginOrder(node4); + node4 = reverseDoubleList(node4); + if (!checkDoubleListReverse(list4, node4)) { + System.out.println("Oops4!"); + } + + } + System.out.println("test finish!"); + + } + +} \ No newline at end of file diff --git a/算法新手班/class04/Code02_LinkedListToQueueAndStack.java b/算法新手班/class04/Code02_LinkedListToQueueAndStack.java new file mode 100644 index 0000000..96b8385 --- /dev/null +++ b/算法新手班/class04/Code02_LinkedListToQueueAndStack.java @@ -0,0 +1,222 @@ +package class04; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Stack; + +public class Code02_LinkedListToQueueAndStack { + + public static class Node { + public V value; + public Node next; + + public Node(V v) { + value = v; + next = null; + } + } + + public static class MyQueue { + private Node head; + private Node tail; + private int size; + + public MyQueue() { + head = null; + tail = null; + size = 0; + } + + public boolean isEmpty() { + return size == 0; + } + + public int size() { + return size; + } + + public void offer(V value) { + Node cur = new Node(value); + if (tail == null) { + head = cur; + tail = cur; + } else { + tail.next = cur; + tail = cur; + } + size++; + } + + // C/C++的同学需要做节点析构的工作 + public V poll() { + V ans = null; + if (head != null) { + ans = head.value; + head = head.next; + size--; + } + if (head == null) { + tail = null; + } + return ans; + } + + // C/C++的同学需要做节点析构的工作 + public V peek() { + V ans = null; + if (head != null) { + ans = head.value; + } + return ans; + } + + } + + public static class MyStack { + private Node head; + private int size; + + public MyStack() { + head = null; + size = 0; + } + + public boolean isEmpty() { + return size == 0; + } + + public int size() { + return size; + } + + public void push(V value) { + Node cur = new Node<>(value); + if (head == null) { + head = cur; + } else { + cur.next = head; + head = cur; + } + size++; + } + + public V pop() { + V ans = null; + if (head != null) { + ans = head.value; + head = head.next; + size--; + } + return ans; + } + + public V peek() { + return head != null ? head.value : null; + } + + } + + public static void testQueue() { + MyQueue myQueue = new MyQueue<>(); + Queue test = new LinkedList<>(); + int testTime = 5000000; + int maxValue = 200000000; + System.out.println("测试开始!"); + for (int i = 0; i < testTime; i++) { + if (myQueue.isEmpty() != test.isEmpty()) { + System.out.println("Oops!"); + } + if (myQueue.size() != test.size()) { + System.out.println("Oops!"); + } + double decide = Math.random(); + if (decide < 0.33) { + int num = (int) (Math.random() * maxValue); + myQueue.offer(num); + test.offer(num); + } else if (decide < 0.66) { + if (!myQueue.isEmpty()) { + int num1 = myQueue.poll(); + int num2 = test.poll(); + if (num1 != num2) { + System.out.println("Oops!"); + } + } + } else { + if (!myQueue.isEmpty()) { + int num1 = myQueue.peek(); + int num2 = test.peek(); + if (num1 != num2) { + System.out.println("Oops!"); + } + } + } + } + if (myQueue.size() != test.size()) { + System.out.println("Oops!"); + } + while (!myQueue.isEmpty()) { + int num1 = myQueue.poll(); + int num2 = test.poll(); + if (num1 != num2) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束!"); + } + + public static void testStack() { + MyStack myStack = new MyStack<>(); + Stack test = new Stack<>(); + int testTime = 5000000; + int maxValue = 200000000; + System.out.println("测试开始!"); + for (int i = 0; i < testTime; i++) { + if (myStack.isEmpty() != test.isEmpty()) { + System.out.println("Oops!"); + } + if (myStack.size() != test.size()) { + System.out.println("Oops!"); + } + double decide = Math.random(); + if (decide < 0.33) { + int num = (int) (Math.random() * maxValue); + myStack.push(num); + test.push(num); + } else if (decide < 0.66) { + if (!myStack.isEmpty()) { + int num1 = myStack.pop(); + int num2 = test.pop(); + if (num1 != num2) { + System.out.println("Oops!"); + } + } + } else { + if (!myStack.isEmpty()) { + int num1 = myStack.peek(); + int num2 = test.peek(); + if (num1 != num2) { + System.out.println("Oops!"); + } + } + } + } + if (myStack.size() != test.size()) { + System.out.println("Oops!"); + } + while (!myStack.isEmpty()) { + int num1 = myStack.pop(); + int num2 = test.pop(); + if (num1 != num2) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束!"); + } + + public static void main(String[] args) { + testQueue(); + testStack(); + } + +} diff --git a/算法新手班/class04/Code03_DoubleLinkedListToDeque.java b/算法新手班/class04/Code03_DoubleLinkedListToDeque.java new file mode 100644 index 0000000..bf37a6a --- /dev/null +++ b/算法新手班/class04/Code03_DoubleLinkedListToDeque.java @@ -0,0 +1,189 @@ +package class04; + +import java.util.Deque; +import java.util.LinkedList; + +public class Code03_DoubleLinkedListToDeque { + + public static class Node { + public V value; + public Node last; + public Node next; + + public Node(V v) { + value = v; + last = null; + next = null; + } + } + + public static class MyDeque { + private Node head; + private Node tail; + private int size; + + public MyDeque() { + head = null; + tail = null; + size = 0; + } + + public boolean isEmpty() { + return size == 0; + } + + public int size() { + return size; + } + + public void pushHead(V value) { + Node cur = new Node<>(value); + if (head == null) { + head = cur; + tail = cur; + } else { + cur.next = head; + head.last = cur; + head = cur; + } + size++; + } + + public void pushTail(V value) { + Node cur = new Node<>(value); + if (head == null) { + head = cur; + tail = cur; + } else { + tail.next = cur; + cur.last = tail; + tail = cur; + } + size++; + } + + public V pollHead() { + V ans = null; + if (head == null) { + return ans; + } + size--; + ans = head.value; + if (head == tail) { + head = null; + tail = null; + } else { + head = head.next; + head.last = null; + } + return ans; + } + + public V pollTail() { + V ans = null; + if (head == null) { + return ans; + } + size--; + ans = tail.value; + if (head == tail) { + head = null; + tail = null; + } else { + tail = tail.last; + tail.next = null; + } + return ans; + } + + public V peekHead() { + V ans = null; + if (head != null) { + ans = head.value; + } + return ans; + } + + public V peekTail() { + V ans = null; + if (tail != null) { + ans = tail.value; + } + return ans; + } + + } + + public static void testDeque() { + MyDeque myDeque = new MyDeque<>(); + Deque test = new LinkedList<>(); + int testTime = 5000000; + int maxValue = 200000000; + System.out.println("测试开始!"); + for (int i = 0; i < testTime; i++) { + if (myDeque.isEmpty() != test.isEmpty()) { + System.out.println("Oops!"); + } + if (myDeque.size() != test.size()) { + System.out.println("Oops!"); + } + double decide = Math.random(); + if (decide < 0.33) { + int num = (int) (Math.random() * maxValue); + if (Math.random() < 0.5) { + myDeque.pushHead(num); + test.addFirst(num); + } else { + myDeque.pushTail(num); + test.addLast(num); + } + } else if (decide < 0.66) { + if (!myDeque.isEmpty()) { + int num1 = 0; + int num2 = 0; + if (Math.random() < 0.5) { + num1 = myDeque.pollHead(); + num2 = test.pollFirst(); + } else { + num1 = myDeque.pollTail(); + num2 = test.pollLast(); + } + if (num1 != num2) { + System.out.println("Oops!"); + } + } + } else { + if (!myDeque.isEmpty()) { + int num1 = 0; + int num2 = 0; + if (Math.random() < 0.5) { + num1 = myDeque.peekHead(); + num2 = test.peekFirst(); + } else { + num1 = myDeque.peekTail(); + num2 = test.peekLast(); + } + if (num1 != num2) { + System.out.println("Oops!"); + } + } + } + } + if (myDeque.size() != test.size()) { + System.out.println("Oops!"); + } + while (!myDeque.isEmpty()) { + int num1 = myDeque.pollHead(); + int num2 = test.pollFirst(); + if (num1 != num2) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束!"); + } + + public static void main(String[] args) { + testDeque(); + } + +} diff --git a/算法新手班/class04/Code04_ReverseNodesInKGroup.java b/算法新手班/class04/Code04_ReverseNodesInKGroup.java new file mode 100644 index 0000000..42eaed6 --- /dev/null +++ b/算法新手班/class04/Code04_ReverseNodesInKGroup.java @@ -0,0 +1,59 @@ + + +package class04; + +// 测试链接:https://leetcode.com/problems/reverse-nodes-in-k-group/ +public class Code04_ReverseNodesInKGroup { + + // 不要提交这个类 + public static class ListNode { + public int val; + public ListNode next; + } + + public static ListNode reverseKGroup(ListNode head, int k) { + ListNode start = head; + ListNode end = getKGroupEnd(start, k); + if (end == null) { + return head; + } + // 第一组凑齐了! + head = end; + reverse(start, end); + // 上一组的结尾节点 + ListNode lastEnd = start; + while (lastEnd.next != null) { + start = lastEnd.next; + end = getKGroupEnd(start, k); + if (end == null) { + return head; + } + reverse(start, end); + lastEnd.next = end; + lastEnd = start; + } + return head; + } + + public static ListNode getKGroupEnd(ListNode start, int k) { + while (--k != 0 && start != null) { + start = start.next; + } + return start; + } + + public static void reverse(ListNode start, ListNode end) { + end = end.next; + ListNode pre = null; + ListNode cur = start; + ListNode next = null; + while (cur != end) { + next = cur.next; + cur.next = pre; + pre = cur; + cur = next; + } + start.next = end; + } + +} \ No newline at end of file diff --git a/算法新手班/class04/Code05_AddTwoNumbers.java b/算法新手班/class04/Code05_AddTwoNumbers.java new file mode 100644 index 0000000..5dbff15 --- /dev/null +++ b/算法新手班/class04/Code05_AddTwoNumbers.java @@ -0,0 +1,62 @@ +package class04; + +// 测试链接:https://leetcode.com/problems/add-two-numbers/ +public class Code05_AddTwoNumbers { + + // 不要提交这个类 + public static class ListNode { + public int val; + public ListNode next; + + public ListNode(int val) { + this.val = val; + } + + public ListNode(int val, ListNode next) { + this.val = val; + this.next = next; + } + } + + public static ListNode addTwoNumbers(ListNode head1, ListNode head2) { + int len1 = listLength(head1); + int len2 = listLength(head2); + ListNode l = len1 >= len2 ? head1 : head2; + ListNode s = l == head1 ? head2 : head1; + ListNode curL = l; + ListNode curS = s; + ListNode last = curL; + int carry = 0; + int curNum = 0; + while (curS != null) { + curNum = curL.val + curS.val + carry; + curL.val = (curNum % 10); + carry = curNum / 10; + last = curL; + curL = curL.next; + curS = curS.next; + } + while (curL != null) { + curNum = curL.val + carry; + curL.val = (curNum % 10); + carry = curNum / 10; + last = curL; + curL = curL.next; + } + if (carry != 0) { + last.next = new ListNode(1); + } + return l; + } + + // 求链表长度 + public static int listLength(ListNode head) { + int len = 0; + while (head != null) { + len++; + head = head.next; + } + return len; + } + +} diff --git a/算法新手班/class04/Code06_MergeTwoSortedLinkedList.java b/算法新手班/class04/Code06_MergeTwoSortedLinkedList.java new file mode 100644 index 0000000..88e1595 --- /dev/null +++ b/算法新手班/class04/Code06_MergeTwoSortedLinkedList.java @@ -0,0 +1,34 @@ +package class04; + +// 测试链接:https://leetcode.com/problems/merge-two-sorted-lists +public class Code06_MergeTwoSortedLinkedList { + + // 不要提交这个类 + public static class ListNode { + public int val; + public ListNode next; + } + + public static ListNode mergeTwoLists(ListNode head1, ListNode head2) { + if (head1 == null || head2 == null) { + return head1 == null ? head2 : head1; + } + ListNode head = head1.val <= head2.val ? head1 : head2; + ListNode cur1 = head.next; + ListNode cur2 = head == head1 ? head2 : head1; + ListNode pre = head; + while (cur1 != null && cur2 != null) { + if (cur1.val <= cur2.val) { + pre.next = cur1; + cur1 = cur1.next; + } else { + pre.next = cur2; + cur2 = cur2.next; + } + pre = pre.next; + } + pre.next = cur1 != null ? cur1 : cur2; + return head; + } + +} diff --git a/算法新手班/class05/Code01_BitMap1.java b/算法新手班/class05/Code01_BitMap1.java new file mode 100644 index 0000000..8f7bad0 --- /dev/null +++ b/算法新手班/class05/Code01_BitMap1.java @@ -0,0 +1,61 @@ +package class05; + +import java.util.HashSet; + +// 这个类的实现是错误的 +// 请问为什么? +public class Code01_BitMap1 { + + public static class BitMap { + + private long[] bits; + + public BitMap(int max) { + bits = new long[(max + 64) >> 6]; + } + + public void add(int num) { + bits[num >> 6] |= (1 << (num & 63)); + } + + public void delete(int num) { + bits[num >> 6] &= ~(1 << (num & 63)); + } + + public boolean contains(int num) { + return (bits[num >> 6] & (1 << (num & 63))) != 0; + } + + } + + public static void main(String[] args) { + System.out.println("测试开始!"); + int max = 10000; + BitMap bitMap = new BitMap(max); + HashSet set = new HashSet<>(); + int testTime = 10000000; + for (int i = 0; i < testTime; i++) { + int num = (int) (Math.random() * (max + 1)); + double decide = Math.random(); + if (decide < 0.333) { + bitMap.add(num); + set.add(num); + } else if (decide < 0.666) { + bitMap.delete(num); + set.remove(num); + } else { + if (bitMap.contains(num) != set.contains(num)) { + System.out.println("Oops!"); + break; + } + } + } + for (int num = 0; num <= max; num++) { + if (bitMap.contains(num) != set.contains(num)) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束!"); + } + +} diff --git a/算法新手班/class05/Code02_BitMap2.java b/算法新手班/class05/Code02_BitMap2.java new file mode 100644 index 0000000..cb8adbd --- /dev/null +++ b/算法新手班/class05/Code02_BitMap2.java @@ -0,0 +1,60 @@ +package class05; + +import java.util.HashSet; + +public class Code02_BitMap2 { + + // 这个类的实现是正确的 + public static class BitMap { + + private long[] bits; + + public BitMap(int max) { + bits = new long[(max + 64) >> 6]; + } + + public void add(int num) { + bits[num >> 6] |= (1L << (num & 63)); + } + + public void delete(int num) { + bits[num >> 6] &= ~(1L << (num & 63)); + } + + public boolean contains(int num) { + return (bits[num >> 6] & (1L << (num & 63))) != 0; + } + + } + + public static void main(String[] args) { + System.out.println("测试开始!"); + int max = 10000; + BitMap bitMap = new BitMap(max); + HashSet set = new HashSet<>(); + int testTime = 10000000; + for (int i = 0; i < testTime; i++) { + int num = (int) (Math.random() * (max + 1)); + double decide = Math.random(); + if (decide < 0.333) { + bitMap.add(num); + set.add(num); + } else if (decide < 0.666) { + bitMap.delete(num); + set.remove(num); + } else { + if (bitMap.contains(num) != set.contains(num)) { + System.out.println("Oops!"); + break; + } + } + } + for (int num = 0; num <= max; num++) { + if (bitMap.contains(num) != set.contains(num)) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束!"); + } + +} diff --git a/算法新手班/class05/Code03_BitAddMinusMultiDiv.java b/算法新手班/class05/Code03_BitAddMinusMultiDiv.java new file mode 100644 index 0000000..b4cfa56 --- /dev/null +++ b/算法新手班/class05/Code03_BitAddMinusMultiDiv.java @@ -0,0 +1,70 @@ +package class05; + +// 测试链接:https://leetcode.com/problems/divide-two-integers +public class Code03_BitAddMinusMultiDiv { + + public static int add(int a, int b) { + int sum = a; + while (b != 0) { + sum = a ^ b; + b = (a & b) << 1; + a = sum; + } + return sum; + } + + public static int negNum(int n) { + return add(~n, 1); + } + + public static int minus(int a, int b) { + return add(a, negNum(b)); + } + + public static int multi(int a, int b) { + int res = 0; + while (b != 0) { + if ((b & 1) != 0) { + res = add(res, a); + } + a <<= 1; + b >>>= 1; + } + return res; + } + + public static boolean isNeg(int n) { + return n < 0; + } + + public static int div(int a, int b) { + int x = isNeg(a) ? negNum(a) : a; + int y = isNeg(b) ? negNum(b) : b; + int res = 0; + for (int i = 30; i >= 0; i = minus(i, 1)) { + if ((x >> i) >= y) { + res |= (1 << i); + x = minus(x, y << i); + } + } + return isNeg(a) ^ isNeg(b) ? negNum(res) : res; + } + + public static int divide(int a, int b) { + if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) { + return 1; + } else if (b == Integer.MIN_VALUE) { + return 0; + } else if (a == Integer.MIN_VALUE) { + if (b == negNum(1)) { + return Integer.MAX_VALUE; + } else { + int c = div(add(a, 1), b); + return add(c, div(minus(a, multi(c, b)), b)); + } + } else { + return div(a, b); + } + } + +} diff --git a/算法新手班/class06/Code01_MergeKSortedLists.java b/算法新手班/class06/Code01_MergeKSortedLists.java new file mode 100644 index 0000000..d9b9345 --- /dev/null +++ b/算法新手班/class06/Code01_MergeKSortedLists.java @@ -0,0 +1,52 @@ +package class06; + +import java.util.Comparator; +import java.util.PriorityQueue; + +// 测试链接:https://leetcode.com/problems/merge-k-sorted-lists/ +public class Code01_MergeKSortedLists { + + public static class ListNode { + public int val; + public ListNode next; + } + + public static class ListNodeComparator implements Comparator { + + @Override + public int compare(ListNode o1, ListNode o2) { + return o1.val - o2.val; + } + + } + + public static ListNode mergeKLists(ListNode[] lists) { + if (lists == null) { + return null; + } + PriorityQueue heap = new PriorityQueue<>(new ListNodeComparator()); + for (int i = 0; i < lists.length; i++) { + if (lists[i] != null) { + heap.add(lists[i]); + } + } + if (heap.isEmpty()) { + return null; + } + ListNode head = heap.poll(); + ListNode pre = head; + if (pre.next != null) { + heap.add(pre.next); + } + while (!heap.isEmpty()) { + ListNode cur = heap.poll(); + pre.next = cur; + pre = cur; + if (cur.next != null) { + heap.add(cur.next); + } + } + return head; + } + +} diff --git a/算法新手班/class06/Code02_SameTree.java b/算法新手班/class06/Code02_SameTree.java new file mode 100644 index 0000000..ccfbb99 --- /dev/null +++ b/算法新手班/class06/Code02_SameTree.java @@ -0,0 +1,23 @@ +package class06; + +// 测试链接:https://leetcode.com/problems/same-tree +public class Code02_SameTree { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + public static boolean isSameTree(TreeNode p, TreeNode q) { + if (p == null ^ q == null) { + return false; + } + if (p == null && q == null) { + return true; + } + // 都不为空 + return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right); + } + +} diff --git a/算法新手班/class06/Code03_SymmetricTree.java b/算法新手班/class06/Code03_SymmetricTree.java new file mode 100644 index 0000000..8526f32 --- /dev/null +++ b/算法新手班/class06/Code03_SymmetricTree.java @@ -0,0 +1,26 @@ +package class06; + +// 测试链接:https://leetcode.com/problems/symmetric-tree +public class Code03_SymmetricTree { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + public static boolean isSymmetric(TreeNode root) { + return isMirror(root, root); + } + + public static boolean isMirror(TreeNode h1, TreeNode h2) { + if (h1 == null ^ h2 == null) { + return false; + } + if (h1 == null && h2 == null) { + return true; + } + return h1.val == h2.val && isMirror(h1.left, h2.right) && isMirror(h1.right, h2.left); + } + +} diff --git a/算法新手班/class06/Code04_MaximumDepthOfBinaryTree.java b/算法新手班/class06/Code04_MaximumDepthOfBinaryTree.java new file mode 100644 index 0000000..3296007 --- /dev/null +++ b/算法新手班/class06/Code04_MaximumDepthOfBinaryTree.java @@ -0,0 +1,20 @@ +package class06; + +// 测试链接:https://leetcode.com/problems/maximum-depth-of-binary-tree +public class Code04_MaximumDepthOfBinaryTree { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + // 以root为头的树,最大高度是多少返回! + public static int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; + } + +} diff --git a/算法新手班/class06/Code05_ConstructBinaryTreeFromPreorderAndInorderTraversal.java b/算法新手班/class06/Code05_ConstructBinaryTreeFromPreorderAndInorderTraversal.java new file mode 100644 index 0000000..2a4e77b --- /dev/null +++ b/算法新手班/class06/Code05_ConstructBinaryTreeFromPreorderAndInorderTraversal.java @@ -0,0 +1,72 @@ +package class06; + +import java.util.HashMap; + +//测试链接:https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal +public class Code05_ConstructBinaryTreeFromPreorderAndInorderTraversal { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int val) { + this.val = val; + } + } + + public static TreeNode buildTree1(int[] pre, int[] in) { + if (pre == null || in == null || pre.length != in.length) { + return null; + } + return f(pre, 0, pre.length - 1, in, 0, in.length - 1); + } + + // 有一棵树,先序结果是pre[L1...R1],中序结果是in[L2...R2] + // 请建出整棵树返回头节点 + public static TreeNode f(int[] pre, int L1, int R1, int[] in, int L2, int R2) { + if (L1 > R1) { + return null; + } + TreeNode head = new TreeNode(pre[L1]); + if (L1 == R1) { + return head; + } + int find = L2; + while (in[find] != pre[L1]) { + find++; + } + head.left = f(pre, L1 + 1, L1 + find - L2, in, L2, find - 1); + head.right = f(pre, L1 + find - L2 + 1, R1, in, find + 1, R2); + return head; + } + + public static TreeNode buildTree2(int[] pre, int[] in) { + if (pre == null || in == null || pre.length != in.length) { + return null; + } + HashMap valueIndexMap = new HashMap<>(); + for (int i = 0; i < in.length; i++) { + valueIndexMap.put(in[i], i); + } + return g(pre, 0, pre.length - 1, in, 0, in.length - 1, valueIndexMap); + } + + // 有一棵树,先序结果是pre[L1...R1],中序结果是in[L2...R2] + // 请建出整棵树返回头节点 + public static TreeNode g(int[] pre, int L1, int R1, int[] in, int L2, int R2, + HashMap valueIndexMap) { + if (L1 > R1) { + return null; + } + TreeNode head = new TreeNode(pre[L1]); + if (L1 == R1) { + return head; + } + int find = valueIndexMap.get(pre[L1]); + head.left = g(pre, L1 + 1, L1 + find - L2, in, L2, find - 1, valueIndexMap); + head.right = g(pre, L1 + find - L2 + 1, R1, in, find + 1, R2, valueIndexMap); + return head; + } + +} diff --git a/算法新手班/class06/ShowComparator.java b/算法新手班/class06/ShowComparator.java new file mode 100644 index 0000000..67a16f1 --- /dev/null +++ b/算法新手班/class06/ShowComparator.java @@ -0,0 +1,106 @@ +package class06; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; + +public class ShowComparator { + + public static class Student { + public String name; + public int id; + public int age; + + public Student(String name, int id, int age) { + this.name = name; + this.id = id; + this.age = age; + } + } + + // 谁id大,谁放前! + public static class IdComparator implements Comparator { + + // 如果返回负数,认为第一个参数应该排在前面 + // 如果返回正数,认为第二个参数应该排在前面 + // 如果返回0,认为谁放前面无所谓 + @Override + public int compare(Student o1, Student o2) { + if (o1.id < o2.id) { + return 1; + } else if (o2.id < o1.id) { + return -1; + } else { + return 0; + } + } + } + + // 谁age大,谁放前! + public static class AgeComparator implements Comparator { + + // 如果返回负数,认为第一个参数应该排在前面 + // 如果返回正数,认为第二个参数应该排在前面 + // 如果返回0,认为谁放前面无所谓 + @Override + public int compare(Student o1, Student o2) { + if (o1.age < o2.age) { + return 1; + } else if (o2.age < o1.age) { + return -1; + } else { + return 0; + } + } + } + + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void printStudents(Student[] students) { + for (int i = 0; i < students.length; i++) { + System.out.println(students[i].name + ", " + students[i].id + ", " + students[i].age); + } + } + + public static void main(String[] args) { + int[] arr = { 8, 1, 4, 1, 6, 8, 4, 1, 5, 8, 2, 3, 0 }; + printArray(arr); + Arrays.sort(arr); + printArray(arr); + + Student s1 = new Student("张三", 5, 27); + Student s2 = new Student("李四", 1, 17); + Student s3 = new Student("王五", 4, 29); + Student s4 = new Student("赵六", 3, 9); + Student s5 = new Student("左七", 2, 34); + + Student[] students = { s1, s2, s3, s4, s5 }; + printStudents(students); + System.out.println("======="); + Arrays.sort(students, new IdComparator()); + printStudents(students); + System.out.println("======="); + + ArrayList arrList = new ArrayList<>(); + arrList.add(s1); + arrList.add(s2); + arrList.add(s3); + arrList.add(s4); + arrList.add(s5); + for (Student s : arrList) { + System.out.println(s.name + ", " + s.id + ", " + s.age); + } + System.out.println("======="); + arrList.sort(new AgeComparator()); + for (Student s : arrList) { + System.out.println(s.name + ", " + s.id + ", " + s.age); + } + + } + +} diff --git a/算法新手班/class06/ShowComparator2.java b/算法新手班/class06/ShowComparator2.java new file mode 100644 index 0000000..a4208ef --- /dev/null +++ b/算法新手班/class06/ShowComparator2.java @@ -0,0 +1,78 @@ +package class06; + +import java.util.Comparator; +import java.util.PriorityQueue; + +public class ShowComparator2 { + + public static class MyComparator implements Comparator { + + // 负,第一个参数在前 + // 正,第二个参数在前 + // 0, 谁放前都行 + @Override + public int compare(Integer o1, Integer o2) { + if (o1 < o2) { + return 1; + } else if (o1 > o2) { + return -1; + } else { + return 0; + } + } + + } + + public static class Student { + public String name; + public int id; + public int age; + + public Student(String name, int id, int age) { + this.name = name; + this.id = id; + this.age = age; + } + } + + // 谁id大,谁放前! + public static class IdComparator implements Comparator { + + // 如果返回负数,认为第一个参数应该排在前面 + // 如果返回正数,认为第二个参数应该排在前面 + // 如果返回0,认为谁放前面无所谓 + @Override + public int compare(Student o1, Student o2) { + if (o1.id < o2.id) { + return 1; + } else if (o2.id < o1.id) { + return -1; + } else { + return 0; + } + } + } + + public static void main(String[] args) { + String str1 = "abc"; + String str2 = "b"; + System.out.println(str1.compareTo(str2)); + PriorityQueue heap = new PriorityQueue<>(new IdComparator()); + Student s1 = new Student("张三", 5, 27); + Student s2 = new Student("李四", 1, 17); + Student s3 = new Student("王五", 4, 29); + Student s4 = new Student("赵六", 3, 9); + Student s5 = new Student("左七", 2, 34); + heap.add(s1); + heap.add(s2); + heap.add(s3); + heap.add(s4); + heap.add(s5); + System.out.println("========="); + while (!heap.isEmpty()) { + Student s = heap.poll(); + System.out.println(s.name + ", " + s.id + ", " + s.age); + } + } + +} diff --git a/算法新手班/class06/TraversalBinaryTree.java b/算法新手班/class06/TraversalBinaryTree.java new file mode 100644 index 0000000..b6529bd --- /dev/null +++ b/算法新手班/class06/TraversalBinaryTree.java @@ -0,0 +1,72 @@ +package class06; + +public class TraversalBinaryTree { + + public static class Node { + public int value; + public Node left; + public Node right; + + public Node(int v) { + value = v; + } + } + + public static void f(Node head) { + if (head == null) { + return; + } + // 1 + f(head.left); + // 2 + f(head.right); + // 3 + } + + // 先序打印所有节点 + public static void pre(Node head) { + if (head == null) { + return; + } + System.out.println(head.value); + pre(head.left); + pre(head.right); + } + + public static void in(Node head) { + if (head == null) { + return; + } + in(head.left); + System.out.println(head.value); + in(head.right); + } + + public static void pos(Node head) { + if (head == null) { + return; + } + pos(head.left); + pos(head.right); + System.out.println(head.value); + } + + public static void main(String[] args) { + Node head = new Node(1); + head.left = new Node(2); + head.right = new Node(3); + head.left.left = new Node(4); + head.left.right = new Node(5); + head.right.left = new Node(6); + head.right.right = new Node(7); + + pre(head); + System.out.println("========"); + in(head); + System.out.println("========"); + pos(head); + System.out.println("========"); + + } + +} diff --git a/算法新手班/class07/Code01_BinaryTreeLevelOrderTraversalII.java b/算法新手班/class07/Code01_BinaryTreeLevelOrderTraversalII.java new file mode 100644 index 0000000..e2d42fd --- /dev/null +++ b/算法新手班/class07/Code01_BinaryTreeLevelOrderTraversalII.java @@ -0,0 +1,45 @@ +package class07; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +// 测试链接:https://leetcode.com/problems/binary-tree-level-order-traversal-ii +public class Code01_BinaryTreeLevelOrderTraversalII { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + TreeNode(int val) { + this.val = val; + } + } + + public List> levelOrderBottom(TreeNode root) { + List> ans = new LinkedList<>(); + if (root == null) { + return ans; + } + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List curAns = new LinkedList<>(); + for (int i = 0; i < size; i++) { + TreeNode curNode = queue.poll(); + curAns.add(curNode.val); + if (curNode.left != null) { + queue.add(curNode.left); + } + if (curNode.right != null) { + queue.add(curNode.right); + } + } + ans.add(0, curAns); + } + return ans; + } + +} diff --git a/算法新手班/class07/Code02_BalancedBinaryTree.java b/算法新手班/class07/Code02_BalancedBinaryTree.java new file mode 100644 index 0000000..640ebbc --- /dev/null +++ b/算法新手班/class07/Code02_BalancedBinaryTree.java @@ -0,0 +1,42 @@ +package class07; + +// 测试链接:https://leetcode.com/problems/balanced-binary-tree +public class Code02_BalancedBinaryTree { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + TreeNode(int val) { + this.val = val; + } + } + + public static class Info { + public boolean isBalanced; + public int height; + + public Info(boolean i, int h) { + isBalanced = i; + height = h; + } + } + + public static boolean isBalanced(TreeNode root) { + return process(root).isBalanced; + } + + public static Info process(TreeNode root) { + if (root == null) { + return new Info(true, 0); + } + Info leftInfo = process(root.left); + Info rightInfo = process(root.right); + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + boolean isBalanced = leftInfo.isBalanced && rightInfo.isBalanced + && Math.abs(leftInfo.height - rightInfo.height) < 2; + return new Info(isBalanced, height); + } + +} diff --git a/算法新手班/class07/Code03_PathSum.java b/算法新手班/class07/Code03_PathSum.java new file mode 100644 index 0000000..0f33665 --- /dev/null +++ b/算法新手班/class07/Code03_PathSum.java @@ -0,0 +1,60 @@ +package class07; + +public class Code03_PathSum { + + // 测试链接:https://leetcode.com/problems/path-sum + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + TreeNode(int val) { + this.val = val; + } + } + + public static boolean isSum = false; + + public static boolean hasPathSum(TreeNode root, int sum) { + if (root == null) { + return false; + } + isSum = false; + process(root, 0, sum); + return isSum; + } + + public static void process(TreeNode x, int preSum, int sum) { + if (x.left == null && x.right == null) { + if (x.val + preSum == sum) { + isSum = true; + } + return; + } + // x是非叶节点 + preSum += x.val; + if (x.left != null) { + process(x.left, preSum, sum); + } + if (x.right != null) { + process(x.right, preSum, sum); + } + } + +// public static boolean hasPathSum(TreeNode root, int sum) { +// if (root == null) { +// return false; +// } +// return process(root, sum); +// } +// +// public static boolean process(TreeNode root, int rest) { +// if (root.left == null && root.right == null) { +// return root.val == rest; +// } +// boolean ans = root.left != null ? process(root.left, rest - root.val) : false; +// ans |= root.right != null ? process(root.right, rest - root.val) : false; +// return ans; +// } + +} diff --git a/算法新手班/class07/Code04_PathSumII.java b/算法新手班/class07/Code04_PathSumII.java new file mode 100644 index 0000000..50171eb --- /dev/null +++ b/算法新手班/class07/Code04_PathSumII.java @@ -0,0 +1,58 @@ +package class07; + +import java.util.ArrayList; +import java.util.List; + +public class Code04_PathSumII { + + // 测试链接:https://leetcode.com/problems/path-sum-ii + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + TreeNode(int val) { + this.val = val; + } + } + + public static List> pathSum(TreeNode root, int sum) { + List> ans = new ArrayList<>(); + if (root == null) { + return ans; + } + ArrayList path = new ArrayList<>(); + process(root, path, 0, sum, ans); + return ans; + } + + public static void process(TreeNode x, List path, int preSum, int sum, List> ans) { + if (x.left == null && x.right == null) { + if (preSum + x.val == sum) { + path.add(x.val); + ans.add(copy(path)); + path.remove(path.size() - 1); + } + return; + } + // x 非叶节点 + path.add(x.val); + preSum += x.val; + if (x.left != null) { + process(x.left, path, preSum, sum, ans); + } + if (x.right != null) { + process(x.right, path, preSum, sum, ans); + } + path.remove(path.size() - 1); + } + + public static List copy(List path) { + List ans = new ArrayList<>(); + for (Integer num : path) { + ans.add(num); + } + return ans; + } + +} diff --git a/算法新手班/class07/Code05_IsBinarySearchTree.java b/算法新手班/class07/Code05_IsBinarySearchTree.java new file mode 100644 index 0000000..fa761f5 --- /dev/null +++ b/算法新手班/class07/Code05_IsBinarySearchTree.java @@ -0,0 +1,85 @@ +package class07; + +public class Code05_IsBinarySearchTree { + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + TreeNode(int val) { + this.val = val; + } + } + + public static class Info { + public boolean isBST; + public int max; + public int min; + + public Info(boolean is, int ma, int mi) { + isBST = is; + max = ma; + min = mi; + } + } + +// public static Info process(TreeNode x) { +// if (x == null) { +// return null; +// } +// Info leftInfo = process(x.left); +// Info rightInfo = process(x.right); +// int max = x.val; +// int min = x.val; +// if (leftInfo != null) { +// max = Math.max(leftInfo.max, max); +// min = Math.min(leftInfo.min, min); +// } +// if (rightInfo != null) { +// max = Math.max(rightInfo.max, max); +// min = Math.min(rightInfo.min, min); +// } +// boolean isBST = true; +// if (leftInfo != null && !leftInfo.isBST) { +// isBST = false; +// } +// if (rightInfo != null && !rightInfo.isBST) { +// isBST = false; +// } +// boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.val); +// boolean rightMinMoreX = rightInfo == null ? true : (rightInfo.min > x.val); +// if (!(leftMaxLessX && rightMinMoreX)) { +// isBST = false; +// } +// return new Info(isBST, max, min); +// } + + public static Info process(TreeNode x) { + if (x == null) { + return null; + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int max = x.val; + int min = x.val; + if (leftInfo != null) { + max = Math.max(leftInfo.max, max); + min = Math.min(leftInfo.min, min); + } + if (rightInfo != null) { + max = Math.max(rightInfo.max, max); + min = Math.min(rightInfo.min, min); + } + boolean isBST = false; + boolean leftIsBst = leftInfo == null ? true : leftInfo.isBST; + boolean rightIsBst = rightInfo == null ? true : rightInfo.isBST; + boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.val); + boolean rightMinMoreX = rightInfo == null ? true : (rightInfo.min > x.val); + if (leftIsBst && rightIsBst && leftMaxLessX && rightMinMoreX) { + isBST = true; + } + return new Info(isBST, max, min); + } + +} diff --git a/算法新手班/class08/Code01_GetMax.java b/算法新手班/class08/Code01_GetMax.java new file mode 100644 index 0000000..19bf7df --- /dev/null +++ b/算法新手班/class08/Code01_GetMax.java @@ -0,0 +1,43 @@ +package class08; + +public class Code01_GetMax { + + public static int flip(int n) { + return n ^ 1; + } + + public static int sign(int n) { + return flip((n >> 31) & 1); + } + + public static int getMax1(int a, int b) { + int c = a - b; + int scA = sign(c); + int scB = flip(scA); + return a * scA + b * scB; + } + + public static int getMax2(int a, int b) { + int c = a - b; + int sa = sign(a); + int sb = sign(b); + int sc = sign(c); + int difSab = sa ^ sb; + int sameSab = flip(difSab); + int returnA = difSab * sa + sameSab * sc; + int returnB = flip(returnA); + return a * returnA + b * returnB; + } + + public static void main(String[] args) { + int a = -16; + int b = -19; + System.out.println(getMax1(a, b)); + System.out.println(getMax2(a, b)); + a = 2147483647; + b = -2147480000; + System.out.println(getMax1(a, b)); // wrong answer because of overflow + System.out.println(getMax2(a, b)); + } + +} diff --git a/算法新手班/class08/Code02_MergeSort.java b/算法新手班/class08/Code02_MergeSort.java new file mode 100644 index 0000000..b46401b --- /dev/null +++ b/算法新手班/class08/Code02_MergeSort.java @@ -0,0 +1,183 @@ +package class08; + +// 可以去体系学习班学习 +public class Code02_MergeSort { + + // 递归方法实现 + public static void mergeSort1(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + process(arr, 0, arr.length - 1); + } + + // arr[L...R]范围上,请让这个范围上的数,有序! + public static void process(int[] arr, int L, int R) { + if (L == R) { + return; + } + // int mid = (L + R) / 2 + int mid = L + ((R - L) >> 1); + process(arr, L, mid); + process(arr, mid + 1, 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; + int p1 = L; + int p2 = M + 1; + 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]; + } + } + + public static void mergeSort2(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int step = 1; + int N = arr.length; + while (step < N) { + int L = 0; + while (L < N) { + int M = 0; + if (N - L >= step) { + M = L + step - 1; + } else { + M = N - 1; + } + if (M == N - 1) { + break; + } + int R = 0; + if (N - 1 - M >= step) { + R = M + step; + } else { + R = N - 1; + } + merge(arr, L, M, R); + if (R == N - 1) { + break; + } else { + L = R + 1; + } + } + if (step > N / 2) { + break; + } + step *= 2; + } + + } + + // 非递归方法实现 +// 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) { +// 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; +// } +// } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + mergeSort1(arr1); + mergeSort2(arr2); + if (!isEqual(arr1, arr2)) { + System.out.println("出错了!"); + printArray(arr1); + printArray(arr2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法新手班/class08/Code03_PartitionAndQuickSort.java b/算法新手班/class08/Code03_PartitionAndQuickSort.java new file mode 100644 index 0000000..52635a9 --- /dev/null +++ b/算法新手班/class08/Code03_PartitionAndQuickSort.java @@ -0,0 +1,231 @@ +package class08; + +import java.util.Stack; + +// 可以去体系学习班学习 +public class Code03_PartitionAndQuickSort { + + public static void splitNum1(int[] arr) { + int lessEqualR = -1; + int index = 0; + int N = arr.length; + while (index < N) { + if (arr[index] <= arr[N - 1]) { + swap(arr, ++lessEqualR, index++); + } else { + index++; + } + } + } + + public static void splitNum2(int[] arr) { + int N = arr.length; + int lessR = -1; + int moreL = N - 1; + int index = 0; + // arr[N-1] + while (index < moreL) { + if (arr[index] < arr[N - 1]) { + swap(arr, ++lessR, index++); + } else if (arr[index] > arr[N - 1]) { + swap(arr, --moreL, index); + } else { + index++; + } + } + swap(arr, moreL, N - 1); + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // arr[L...R]范围上,拿arr[R]做划分值, + // L....R < = > + public static int[] partition(int[] arr, int L, int R) { + int lessR = L - 1; + int moreL = R; + int index = L; + while (index < moreL) { + if (arr[index] < arr[R]) { + swap(arr, ++lessR, index++); + } else if (arr[index] > arr[R]) { + swap(arr, --moreL, index); + } else { + index++; + } + } + swap(arr, moreL, R); + return new int[] { lessR + 1, moreL }; + } + + public static void quickSort1(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + process(arr, 0, arr.length - 1); + } + + public static void process(int[] arr, int L, int R) { + if (L >= R) { + return; + } + int[] equalE = partition(arr, L, R); + process(arr, L, equalE[0] - 1); + process(arr, equalE[1] + 1, R); + } + + public static class Job { + public int L; + public int R; + + public Job(int left, int right) { + L = left; + R = right; + } + } + + public static void quickSort2(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + Stack stack = new Stack<>(); + stack.push(new Job(0, arr.length - 1)); + while (!stack.isEmpty()) { + Job cur = stack.pop(); + int[] equals = partition(arr, cur.L, cur.R); + if (equals[0] > cur.L) { // 有< 区域 + stack.push(new Job(cur.L, equals[0] - 1)); + } + if (equals[1] < cur.R) { // 有 > 区域 + stack.push(new Job(equals[1] + 1, cur.R)); + } + } + } + + public static int[] netherlandsFlag(int[] arr, int L, int R) { + if (L > R) { + return new int[] { -1, -1 }; + } + if (L == R) { + return new int[] { L, R }; + } + int less = L - 1; + int more = R; + int index = L; + while (index < more) { + if (arr[index] == arr[R]) { + index++; + } else if (arr[index] < arr[R]) { + swap(arr, index++, ++less); + } else { + swap(arr, index, --more); + } + } + swap(arr, more, R); // <[R] =[R] >[R] + return new int[] { less + 1, more }; + } + + public static void quickSort3(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + process3(arr, 0, arr.length - 1); + } + + public static void process3(int[] arr, int L, int R) { + if (L >= R) { + return; + } + swap(arr, L + (int) (Math.random() * (R - L + 1)), R); + int[] equalArea = netherlandsFlag(arr, L, R); + process3(arr, L, equalArea[0] - 1); + process3(arr, equalArea[1] + 1, R); + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static int[] copyArray(int[] arr) { + if (arr == null) { + return null; + } + int[] res = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + res[i] = arr[i]; + } + return res; + } + + // for test + public static boolean isEqual(int[] arr1, int[] arr2) { + if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { + return false; + } + if (arr1 == null && arr2 == null) { + return true; + } + if (arr1.length != arr2.length) { + return false; + } + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + return true; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // for test + public static void main(String[] args) { +// int[] arr = { 7, 1, 3, 5, 4, 5, 1, 4, 2, 4, 2, 4 }; +// +// splitNum2(arr); +// for (int i = 0; i < arr.length; i++) { +// System.out.print(arr[i] + " "); +// } + + int testTime = 500000; + int maxSize = 100; + int maxValue = 100; + boolean succeed = true; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr1 = generateRandomArray(maxSize, maxValue); + int[] arr2 = copyArray(arr1); + int[] arr3 = copyArray(arr1); + quickSort1(arr1); + quickSort2(arr2); + quickSort3(arr3); + if (!isEqual(arr1, arr2) || !isEqual(arr1, arr3)) { + System.out.println("Oops!"); + succeed = false; + break; + } + } + System.out.println("test end"); + System.out.println(succeed ? "Nice!" : "Oops!"); + + } + +}