commit cbc319092fbdf897f4ca3497be5c6fb9c623e5f7 Author: caixinwang Date: Wed May 25 18:41:35 2022 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..267c321 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# 项目排除路径 +/out/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/libraries/Java_EE_6_Java_EE_6.xml b/.idea/libraries/Java_EE_6_Java_EE_6.xml new file mode 100644 index 0000000..82b06f1 --- /dev/null +++ b/.idea/libraries/Java_EE_6_Java_EE_6.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..abf7648 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7222057 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/AlgorithmZuo.iml b/AlgorithmZuo.iml new file mode 100644 index 0000000..35d749d --- /dev/null +++ b/AlgorithmZuo.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/javax.annotation.jar b/lib/javax.annotation.jar new file mode 100644 index 0000000..52dca7f Binary files /dev/null and b/lib/javax.annotation.jar differ diff --git a/lib/javax.ejb.jar b/lib/javax.ejb.jar new file mode 100644 index 0000000..4ebf5ec Binary files /dev/null and b/lib/javax.ejb.jar differ diff --git a/lib/javax.jms.jar b/lib/javax.jms.jar new file mode 100644 index 0000000..d31451a Binary files /dev/null and b/lib/javax.jms.jar differ diff --git a/lib/javax.persistence.jar b/lib/javax.persistence.jar new file mode 100644 index 0000000..21d80e0 Binary files /dev/null and b/lib/javax.persistence.jar differ diff --git a/lib/javax.resource.jar b/lib/javax.resource.jar new file mode 100644 index 0000000..696a234 Binary files /dev/null and b/lib/javax.resource.jar differ diff --git a/lib/javax.servlet.jar b/lib/javax.servlet.jar new file mode 100644 index 0000000..0519e4a Binary files /dev/null and b/lib/javax.servlet.jar differ diff --git a/lib/javax.servlet.jsp.jar b/lib/javax.servlet.jsp.jar new file mode 100644 index 0000000..9c0631c Binary files /dev/null and b/lib/javax.servlet.jsp.jar differ diff --git a/lib/javax.servlet.jsp.jstl.jar b/lib/javax.servlet.jsp.jstl.jar new file mode 100644 index 0000000..7be17cc Binary files /dev/null and b/lib/javax.servlet.jsp.jstl.jar differ diff --git a/lib/javax.transaction.jar b/lib/javax.transaction.jar new file mode 100644 index 0000000..729c695 Binary files /dev/null and b/lib/javax.transaction.jar differ diff --git a/src/class01/Code01_SelectionSort.java b/src/class01/Code01_SelectionSort.java new file mode 100644 index 0000000..c9b5738 --- /dev/null +++ b/src/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/src/class01/Code02_BubbleSort.java b/src/class01/Code02_BubbleSort.java new file mode 100644 index 0000000..14b6e47 --- /dev/null +++ b/src/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/src/class01/Code03_InsertionSort.java b/src/class01/Code03_InsertionSort.java new file mode 100644 index 0000000..36664d4 --- /dev/null +++ b/src/class01/Code03_InsertionSort.java @@ -0,0 +1,130 @@ +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); + } + } + } + + public static void insertionSort_02(int[] arr) { + if (arr == null || arr.length < 2) { + return; + } + int p,i; + for (p=1;p0&&temp [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);//利用java内部的排序算法保证一定对 + 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_02(arr); + printArray(arr); + } + +} diff --git a/src/class01/Code04_BSExist.java b/src/class01/Code04_BSExist.java new file mode 100644 index 0000000..d89be00 --- /dev/null +++ b/src/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/src/class01/Code05_BSNearLeft.java b/src/class01/Code05_BSNearLeft.java new file mode 100644 index 0000000..eaf5804 --- /dev/null +++ b/src/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/src/class01/Code05_BSNearRight.java b/src/class01/Code05_BSNearRight.java new file mode 100644 index 0000000..f3b6f25 --- /dev/null +++ b/src/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/src/class01/Code06_BSAwesome.java b/src/class01/Code06_BSAwesome.java new file mode 100644 index 0000000..8df78e4 --- /dev/null +++ b/src/class01/Code06_BSAwesome.java @@ -0,0 +1,79 @@ +package class01; + +import java.util.Arrays; + +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); +// Arrays.sort(arr); + int ans = getLessIndex(arr); + if (!isRight(arr, ans)) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/src/class01/Code07_EvenTimesOddTimes.java b/src/class01/Code07_EvenTimesOddTimes.java new file mode 100644 index 0000000..12ec590 --- /dev/null +++ b/src/class01/Code07_EvenTimesOddTimes.java @@ -0,0 +1,35 @@ +package class01; + +public class Code07_EvenTimesOddTimes { + public static void printOddTimesOneNumber(int[] arr){ + int eor=0; + for(int i:arr) eor^=i;//出来之后eor就是我们要的 + System.out.println(eor); + } + + public static void printOddTimesTwoNumber(int[] arr){ + int eor=0; + for(int i:arr) eor^=i;//出来之后eor=a^b + /** + * 我们异或了所有的数得到了eor=a^b,现在要分别得到a和b还缺少一个条件。 + * 一个思路就是不去异或所有的数,我们只异或一部分的数,并且这一部分只包含 a和b的其中一个 + * 方法是找出a与b不同的那一位,利用掩码将a和b划分成两个子集,每个子集异或进去可以达到a或者是b。 + * 问题关键来到如何找出a和b不同的那一位,事实上a^b为1的那些位都是a和b不同的位,这里我们找a^b最右边的1 + * 小结论:一个数和它的补码的与运算得到的就是最右边的那一位---想想补码是怎么求的 + */ + int mask=eor&(~eor+1);//得到了掩码 + int aORb=0,another=0 ; + for(int i:arr) { + if((i&mask)==mask) aORb^=i;//该位置为1的全部异或起来 + } + another=eor^aORb; + System.out.println(another+" "+aORb); + } + + public static void main(String[] args) { + int [] arr1={1,1,1,6,6,2,2}; + int [] arr2={1,1,1,6,6,6,2,2}; + printOddTimesOneNumber(arr1); + printOddTimesTwoNumber(arr2); + } +} diff --git a/src/class01/Code08_GetMax.java b/src/class01/Code08_GetMax.java new file mode 100644 index 0000000..1e46437 --- /dev/null +++ b/src/class01/Code08_GetMax.java @@ -0,0 +1,19 @@ +package class01; + +public class Code08_GetMax { + + public static int getMax(int[] arr) { + return process(arr, 0, arr.length - 1); + } + + public static int process(int[] arr, int L, int R) { + if (L == R) { + return arr[L]; + } + int mid = L + ((R - L) >> 1); + int leftMax = process(arr, L, mid); + int rightMax = process(arr, mid + 1, R); + return Math.max(leftMax, rightMax); + } + +} diff --git a/src/class01/Code09_FindOneLessValueIndex.java b/src/class01/Code09_FindOneLessValueIndex.java new file mode 100644 index 0000000..1c1793d --- /dev/null +++ b/src/class01/Code09_FindOneLessValueIndex.java @@ -0,0 +1,46 @@ +package class01; + +public class Code09_FindOneLessValueIndex { + + public static int getLessIndex(int[] arr) { + if (arr == null || arr.length == 0) { + return -1; // no exist + } + 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 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 = { 6, 5, 3, 4, 6, 7, 8 }; + printArray(arr); + int index = getLessIndex(arr); + System.out.println("index: " + index + ", value: " + arr[index]); + + } + +} diff --git a/src/class02/Code01_Swap.java b/src/class02/Code01_Swap.java new file mode 100644 index 0000000..c625614 --- /dev/null +++ b/src/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/src/class02/Code02_EvenTimesOddTimes.java b/src/class02/Code02_EvenTimesOddTimes.java new file mode 100644 index 0000000..13ac877 --- /dev/null +++ b/src/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/src/class02/Code03_KM.java b/src/class02/Code03_KM.java new file mode 100644 index 0000000..a530445 --- /dev/null +++ b/src/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/src/class03/Code01_ReverseList.java b/src/class03/Code01_ReverseList.java new file mode 100644 index 0000000..4b0318a --- /dev/null +++ b/src/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/src/class03/Code02_DeleteGivenValue.java b/src/class03/Code02_DeleteGivenValue.java new file mode 100644 index 0000000..647047a --- /dev/null +++ b/src/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/src/class03/Code03_DoubleEndsQueueToStackAndQueue.java b/src/class03/Code03_DoubleEndsQueueToStackAndQueue.java new file mode 100644 index 0000000..e9b30a9 --- /dev/null +++ b/src/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/src/class03/Code04_RingArray.java b/src/class03/Code04_RingArray.java new file mode 100644 index 0000000..cad73b4 --- /dev/null +++ b/src/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/src/class03/Code05_GetMinStack.java b/src/class03/Code05_GetMinStack.java new file mode 100644 index 0000000..91b20ae --- /dev/null +++ b/src/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/src/class03/Code06_TwoStacksImplementQueue.java b/src/class03/Code06_TwoStacksImplementQueue.java new file mode 100644 index 0000000..483d7a7 --- /dev/null +++ b/src/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/src/class03/Code07_TwoQueueImplementStack.java b/src/class03/Code07_TwoQueueImplementStack.java new file mode 100644 index 0000000..a2a3c54 --- /dev/null +++ b/src/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/src/class03/Code08_GetMax.java b/src/class03/Code08_GetMax.java new file mode 100644 index 0000000..81661d8 --- /dev/null +++ b/src/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/src/class03/HashMapAndSortedMap.java b/src/class03/HashMapAndSortedMap.java new file mode 100644 index 0000000..99f67fc --- /dev/null +++ b/src/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/src/class04/Code01_MergeSort.java b/src/class04/Code01_MergeSort.java new file mode 100644 index 0000000..c0ddb6b --- /dev/null +++ b/src/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/src/class04/Code02_SmallSum.java b/src/class04/Code02_SmallSum.java new file mode 100644 index 0000000..1167a64 --- /dev/null +++ b/src/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/src/class04/Code03_ReversePair.java b/src/class04/Code03_ReversePair.java new file mode 100644 index 0000000..0e6e531 --- /dev/null +++ b/src/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/src/class04/Code04_BiggerThanRightTwice.java b/src/class04/Code04_BiggerThanRightTwice.java new file mode 100644 index 0000000..7eb4f7e --- /dev/null +++ b/src/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/src/class05/Code01_CountOfRangeSum.java b/src/class05/Code01_CountOfRangeSum.java new file mode 100644 index 0000000..559a7d8 --- /dev/null +++ b/src/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/src/class05/Code02_PartitionAndQuickSort.java b/src/class05/Code02_PartitionAndQuickSort.java new file mode 100644 index 0000000..e8a9d7a --- /dev/null +++ b/src/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/src/class05/Code03_QuickSortRecursiveAndUnrecursive.java b/src/class05/Code03_QuickSortRecursiveAndUnrecursive.java new file mode 100644 index 0000000..57a7657 --- /dev/null +++ b/src/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/src/class05/Code04_DoubleLinkedListQuickSort.java b/src/class05/Code04_DoubleLinkedListQuickSort.java new file mode 100644 index 0000000..a3014dc --- /dev/null +++ b/src/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/src/class06/Code01_Comparator.java b/src/class06/Code01_Comparator.java new file mode 100644 index 0000000..af23b64 --- /dev/null +++ b/src/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/src/class06/Code02_Heap.java b/src/class06/Code02_Heap.java new file mode 100644 index 0000000..28140a1 --- /dev/null +++ b/src/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/src/class06/Code03_HeapSort.java b/src/class06/Code03_HeapSort.java new file mode 100644 index 0000000..49e743a --- /dev/null +++ b/src/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/src/class06/Code04_SortArrayDistanceLessK.java b/src/class06/Code04_SortArrayDistanceLessK.java new file mode 100644 index 0000000..0d1b1ea --- /dev/null +++ b/src/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/src/class07/Code01_CoverMax.java b/src/class07/Code01_CoverMax.java new file mode 100644 index 0000000..6260f97 --- /dev/null +++ b/src/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/src/class07/Code02_EveryStepShowBoss.java b/src/class07/Code02_EveryStepShowBoss.java new file mode 100644 index 0000000..a4c123b --- /dev/null +++ b/src/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/src/class07/HeapGreater.java b/src/class07/HeapGreater.java new file mode 100644 index 0000000..eab68a4 --- /dev/null +++ b/src/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/src/class07/Inner.java b/src/class07/Inner.java new file mode 100644 index 0000000..d3d5326 --- /dev/null +++ b/src/class07/Inner.java @@ -0,0 +1,9 @@ +package class07; + +public class Inner { + public T value; + + public Inner(T v) { + value = v; + } +} diff --git a/src/class08/Code01_TrieTree.java b/src/class08/Code01_TrieTree.java new file mode 100644 index 0000000..f7f9cf1 --- /dev/null +++ b/src/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/src/class08/Code02_TrieTree.java b/src/class08/Code02_TrieTree.java new file mode 100644 index 0000000..f1227ae --- /dev/null +++ b/src/class08/Code02_TrieTree.java @@ -0,0 +1,314 @@ +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; + //如果你只要加入小写的26个字母,那么你最多只需要26条路就行了。这个路其实就是一个指针。利用null来标记路存不存在 + // 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++; + + } + + /** + * 如果到某一个地方pass为0了,那么说明后面的pass值一定都为0.因为pass为0说明只有一个字符串会走这条路, + * 而现在这个字符串你要删掉,那么只需要把后面的路彻底放弃就行了。也就是pass=0给了我们额外信息。 + * @param word + */ + 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;//26个大小的空间不够就用这个,Integer表示这条路的ASCII码值 + + 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/src/class08/Code03_CountSort.java b/src/class08/Code03_CountSort.java new file mode 100644 index 0000000..d7864ec --- /dev/null +++ b/src/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/src/class08/Code04_RadixSort.java b/src/class08/Code04_RadixSort.java new file mode 100644 index 0000000..af35db3 --- /dev/null +++ b/src/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/src/class09/Code01_LinkedListMid.java b/src/class09/Code01_LinkedListMid.java new file mode 100644 index 0000000..87ac28a --- /dev/null +++ b/src/class09/Code01_LinkedListMid.java @@ -0,0 +1,164 @@ +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 头。1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点 + 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; + } + //2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点 + 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; + } + + //3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个 + 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; + } + + //4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个 + 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/src/class09/Code02_IsPalindromeList.java b/src/class09/Code02_IsPalindromeList.java new file mode 100644 index 0000000..63d6eed --- /dev/null +++ b/src/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/src/class09/Code03_SmallerEqualBigger.java b/src/class09/Code03_SmallerEqualBigger.java new file mode 100644 index 0000000..33203e4 --- /dev/null +++ b/src/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/src/class09/Code04_CopyListWithRandom.java b/src/class09/Code04_CopyListWithRandom.java new file mode 100644 index 0000000..f42454f --- /dev/null +++ b/src/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/src/class10/Code01_FindFirstIntersectNode.java b/src/class10/Code01_FindFirstIntersectNode.java new file mode 100644 index 0000000..8cec65d --- /dev/null +++ b/src/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/src/class10/Code02_RecursiveTraversalBT.java b/src/class10/Code02_RecursiveTraversalBT.java new file mode 100644 index 0000000..c7f2b2c --- /dev/null +++ b/src/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/src/class10/Code03_UnRecursiveTraversalBT.java b/src/class10/Code03_UnRecursiveTraversalBT.java new file mode 100644 index 0000000..a101d7c --- /dev/null +++ b/src/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/src/class11/Code01_LevelTraversalBT.java b/src/class11/Code01_LevelTraversalBT.java new file mode 100644 index 0000000..12e8ef0 --- /dev/null +++ b/src/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/src/class11/Code02_SerializeAndReconstructTree.java b/src/class11/Code02_SerializeAndReconstructTree.java new file mode 100644 index 0000000..6e6da7e --- /dev/null +++ b/src/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/src/class11/Code03_EncodeNaryTreeToBinaryTree.java b/src/class11/Code03_EncodeNaryTreeToBinaryTree.java new file mode 100644 index 0000000..aa9f2ab --- /dev/null +++ b/src/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/src/class11/Code04_PrintBinaryTree.java b/src/class11/Code04_PrintBinaryTree.java new file mode 100644 index 0000000..75125f7 --- /dev/null +++ b/src/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/src/class11/Code05_TreeMaxWidth.java b/src/class11/Code05_TreeMaxWidth.java new file mode 100644 index 0000000..787bd01 --- /dev/null +++ b/src/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/src/class11/Code06_SuccessorNode.java b/src/class11/Code06_SuccessorNode.java new file mode 100644 index 0000000..b33d4b5 --- /dev/null +++ b/src/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/src/class11/Code07_PaperFolding.java b/src/class11/Code07_PaperFolding.java new file mode 100644 index 0000000..3f3b0db --- /dev/null +++ b/src/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/src/class12/Code01_IsCBT.java b/src/class12/Code01_IsCBT.java new file mode 100644 index 0000000..19b2f48 --- /dev/null +++ b/src/class12/Code01_IsCBT.java @@ -0,0 +1,147 @@ +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/src/class12/Code02_IsBST.java b/src/class12/Code02_IsBST.java new file mode 100644 index 0000000..c3c55a8 --- /dev/null +++ b/src/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 = 1000; + 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/src/class12/Code03_IsBalanced.java b/src/class12/Code03_IsBalanced.java new file mode 100644 index 0000000..e8a6c0f --- /dev/null +++ b/src/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/src/class12/Code04_IsFull.java b/src/class12/Code04_IsFull.java new file mode 100644 index 0000000..a83c952 --- /dev/null +++ b/src/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/src/class12/Code05_MaxSubBSTSize.java b/src/class12/Code05_MaxSubBSTSize.java new file mode 100644 index 0000000..bd17329 --- /dev/null +++ b/src/class12/Code05_MaxSubBSTSize.java @@ -0,0 +1,240 @@ +package class12; + +import java.util.ArrayList; + +public class Code05_MaxSubBSTSize { + + 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 int maxSubBSTSize1(Node head) { + if (head == null) { + return 0; + } + int h = getBSTSize(head); + if (h != 0) { + return h; + } + return Math.max(maxSubBSTSize1(head.left), maxSubBSTSize1(head.right)); + } + +// public static int maxSubBSTSize2(Node head) { +// if (head == null) { +// return 0; +// } +// return process(head).maxSubBSTSize; +// } +// +// +// +// +// +// +// +// +// +// // 任何子树 +// public static class Info { +// public boolean isAllBST; +// public int maxSubBSTSize; +// public int min; +// public int max; +// +// public Info(boolean is, int size, int mi, int ma) { +// isAllBST = is; +// 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; +// +// if(leftInfo != null) { +// min = Math.min(min, leftInfo.min); +// max = Math.max(max, leftInfo.max); +// } +// if(rightInfo != null) { +// min = Math.min(min, rightInfo.min); +// max = Math.max(max, rightInfo.max); +// } +// +// +// +// +// +// +// +// int maxSubBSTSize = 0; +// if(leftInfo != null) { +// maxSubBSTSize = leftInfo.maxSubBSTSize; +// } +// if(rightInfo !=null) { +// maxSubBSTSize = Math.max(maxSubBSTSize, rightInfo.maxSubBSTSize); +// } +// boolean isAllBST = false; +// +// +// if( +// // 左树整体需要是搜索二叉树 +// ( leftInfo == null ? true : leftInfo.isAllBST ) +// && +// ( rightInfo == null ? true : rightInfo.isAllBST ) +// && +// // 左树最大值 X.value) +// +// +// ) { +// +// maxSubBSTSize = +// (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) +// + +// (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) +// + +// 1; +// isAllBST = true; +// +// +// } +// return new Info(isAllBST, maxSubBSTSize, min, max); +// } + + public static int maxSubBSTSize2(Node 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(Node x) { + if (x == null) { + return null; + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int max = x.value; + int min = x.value; + 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.value); + boolean rightMinMoreX = rightInfo == null ? true : (x.value < 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); + } + + // 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 (maxSubBSTSize1(head) != maxSubBSTSize2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/src/class12/Code06_MaxDistance.java b/src/class12/Code06_MaxDistance.java new file mode 100644 index 0000000..8c82c0e --- /dev/null +++ b/src/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/src/class13/Code01_IsCBT.java b/src/class13/Code01_IsCBT.java new file mode 100644 index 0000000..da365d1 --- /dev/null +++ b/src/class13/Code01_IsCBT.java @@ -0,0 +1,117 @@ +package class13; + +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) { + 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 (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 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/src/class13/Code02_MaxSubBSTHead.java b/src/class13/Code02_MaxSubBSTHead.java new file mode 100644 index 0000000..e64c37a --- /dev/null +++ b/src/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/src/class13/Code03_lowestAncestor.java b/src/class13/Code03_lowestAncestor.java new file mode 100644 index 0000000..e2a9d5f --- /dev/null +++ b/src/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/src/class13/Code04_MaxHappy.java b/src/class13/Code04_MaxHappy.java new file mode 100644 index 0000000..3cb21e7 --- /dev/null +++ b/src/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/src/class13/Code05_LowestLexicography.java b/src/class13/Code05_LowestLexicography.java new file mode 100644 index 0000000..13ceedd --- /dev/null +++ b/src/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/src/class14/Code01_Light.java b/src/class14/Code01_Light.java new file mode 100644 index 0000000..22428a9 --- /dev/null +++ b/src/class14/Code01_Light.java @@ -0,0 +1,85 @@ +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; + } + + // 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 = 1000; + for (int i = 0; i < testTime; i++) { + String test = randomString(len); + int ans1 = minLight1(test); + int ans2 = minLight2(test); + if (ans1 != ans2) { + System.out.println("oops!"); + } + } + System.out.println("finish!"); + } +} diff --git a/src/class14/Code02_LessMoneySplitGold.java b/src/class14/Code02_LessMoneySplitGold.java new file mode 100644 index 0000000..682fa12 --- /dev/null +++ b/src/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/src/class14/Code03_BestArrange.java b/src/class14/Code03_BestArrange.java new file mode 100644 index 0000000..ed7ecdb --- /dev/null +++ b/src/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/src/class14/Code04_IPO.java b/src/class14/Code04_IPO.java new file mode 100644 index 0000000..60e1edb --- /dev/null +++ b/src/class14/Code04_IPO.java @@ -0,0 +1,59 @@ +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/src/class14/Code05_UnionFind.java b/src/class14/Code05_UnionFind.java new file mode 100644 index 0000000..f08ace1 --- /dev/null +++ b/src/class14/Code05_UnionFind.java @@ -0,0 +1,76 @@ +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; + } + } + + /** + * nodes表用来记录每一个结点 + * parents表用来记录每一个结点所在集合的代表结点 + * size表只有当一个结点是代表结点的时候才会有记录 + * @param + */ + 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/src/class15/Code01_FriendCircles.java b/src/class15/Code01_FriendCircles.java new file mode 100644 index 0000000..dd4253d --- /dev/null +++ b/src/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/src/class15/Code02_NumberOfIslands.java b/src/class15/Code02_NumberOfIslands.java new file mode 100644 index 0000000..6065d42 --- /dev/null +++ b/src/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/src/class15/Code03_NumberOfIslandsII.java b/src/class15/Code03_NumberOfIslandsII.java new file mode 100644 index 0000000..d109276 --- /dev/null +++ b/src/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/src/class16/Code01_BFS.java b/src/class16/Code01_BFS.java new file mode 100644 index 0000000..c2d4d9f --- /dev/null +++ b/src/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/src/class16/Code02_DFS.java b/src/class16/Code02_DFS.java new file mode 100644 index 0000000..fa85ca4 --- /dev/null +++ b/src/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/src/class16/Code03_TopologicalOrderBFS.java b/src/class16/Code03_TopologicalOrderBFS.java new file mode 100644 index 0000000..5e54581 --- /dev/null +++ b/src/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/src/class16/Code03_TopologicalOrderDFS1.java b/src/class16/Code03_TopologicalOrderDFS1.java new file mode 100644 index 0000000..57f1bb7 --- /dev/null +++ b/src/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/src/class16/Code03_TopologicalOrderDFS2.java b/src/class16/Code03_TopologicalOrderDFS2.java new file mode 100644 index 0000000..4c90d1d --- /dev/null +++ b/src/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/src/class16/Code03_TopologySort.java b/src/class16/Code03_TopologySort.java new file mode 100644 index 0000000..f783474 --- /dev/null +++ b/src/class16/Code03_TopologySort.java @@ -0,0 +1,38 @@ +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/src/class16/Code04_Kruskal.java b/src/class16/Code04_Kruskal.java new file mode 100644 index 0000000..cb0ea62 --- /dev/null +++ b/src/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/src/class16/Code05_Prim.java b/src/class16/Code05_Prim.java new file mode 100644 index 0000000..b2f6299 --- /dev/null +++ b/src/class16/Code05_Prim.java @@ -0,0 +1,91 @@ +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/src/class16/Code06_Dijkstra.java b/src/class16/Code06_Dijkstra.java new file mode 100644 index 0000000..15ae446 --- /dev/null +++ b/src/class16/Code06_Dijkstra.java @@ -0,0 +1,171 @@ +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; + } + } + + /** + * 这里我们使用了Node数组,回想堆排序时候的堆,我们是一个int数组,因为现在是Node数组,所以我们不能直接根据Node来比较 + * 所以这里我们需要一个distanceMap来提供一个比较的途径。然后因为我们还需要更新堆中的某一个结点,更改完之后需要调整,所以 + * 这里我们还需要一个heapIndexMao,这个map有两个作用,一个是判断一个结点是否进入过堆中--通过index是否为-1判断。另外一个 + * 作用就是得到一个结点在堆中的下标,这样方便我们调整。 + */ + public static class NodeHeap { + private Node[] nodes; // 实际的堆结构 + // key 某一个node, value 上面堆中的位置.如果这里的位置为-1说明这结点曾经进来过但是现在不在堆上了。 + 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(node, heapIndexMap.get(node)); + } + if (!isEntered(node)) { + nodes[size] = node; + heapIndexMap.put(node, size); + distanceMap.put(node, distance); + insertHeapify(node, size++); + } + } + + /** + * 这个方法是用来服务外面的Dijkstra函数的。Dijkstra需要的是结点和距离的一个二元组信息,所以这里我们把node和distance + * 封装在一起弹出给外面。 + * @return + */ + 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(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) {//判断一个结点是否进来过堆,不管是-1还是一些其它的值 + return heapIndexMap.containsKey(node); + } + + private boolean inHeap(Node node) {//一个结点进来过并且在位置不等于-1它就在堆上。 + 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()) {//从小跟堆里面弹一条记录我就加一条在result里面 + 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/src/class16/Code06_NetworkDelayTime.java b/src/class16/Code06_NetworkDelayTime.java new file mode 100644 index 0000000..0be520a --- /dev/null +++ b/src/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/src/class16/Edge.java b/src/class16/Edge.java new file mode 100644 index 0000000..7c56891 --- /dev/null +++ b/src/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/src/class16/Graph.java b/src/class16/Graph.java new file mode 100644 index 0000000..933e43b --- /dev/null +++ b/src/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/src/class16/GraphGenerator.java b/src/class16/GraphGenerator.java new file mode 100644 index 0000000..fe3387d --- /dev/null +++ b/src/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/src/class16/Node.java b/src/class16/Node.java new file mode 100644 index 0000000..a468aaf --- /dev/null +++ b/src/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/src/class17/Code01_Dijkstra.java b/src/class17/Code01_Dijkstra.java new file mode 100644 index 0000000..0b39615 --- /dev/null +++ b/src/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/src/class17/Code02_Hanoi.java b/src/class17/Code02_Hanoi.java new file mode 100644 index 0000000..3d9ba6e --- /dev/null +++ b/src/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/src/class17/Code03_PrintAllSubsquences.java b/src/class17/Code03_PrintAllSubsquences.java new file mode 100644 index 0000000..05b6a03 --- /dev/null +++ b/src/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/src/class17/Code04_PrintAllPermutations.java b/src/class17/Code04_PrintAllPermutations.java new file mode 100644 index 0000000..c01729e --- /dev/null +++ b/src/class17/Code04_PrintAllPermutations.java @@ -0,0 +1,107 @@ +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(); + 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/src/class17/Code05_ReverseStackUsingRecursive.java b/src/class17/Code05_ReverseStackUsingRecursive.java new file mode 100644 index 0000000..731056a --- /dev/null +++ b/src/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/src/class17/Edge.java b/src/class17/Edge.java new file mode 100644 index 0000000..eacd58b --- /dev/null +++ b/src/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/src/class17/Graph.java b/src/class17/Graph.java new file mode 100644 index 0000000..f9314a2 --- /dev/null +++ b/src/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/src/class17/Node.java b/src/class17/Node.java new file mode 100644 index 0000000..1056ead --- /dev/null +++ b/src/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/src/class18/Code01_RobotWalk.java b/src/class18/Code01_RobotWalk.java new file mode 100644 index 0000000..f9ee3f2 --- /dev/null +++ b/src/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/src/class18/Code02_CardsInLine.java b/src/class18/Code02_CardsInLine.java new file mode 100644 index 0000000..919a4cb --- /dev/null +++ b/src/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/src/class19/Code01_Knapsack.java b/src/class19/Code01_Knapsack.java new file mode 100644 index 0000000..ab355a2 --- /dev/null +++ b/src/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/src/class19/Code02_ConvertToLetterString.java b/src/class19/Code02_ConvertToLetterString.java new file mode 100644 index 0000000..224375d --- /dev/null +++ b/src/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/src/class19/Code03_StickersToSpellWord.java b/src/class19/Code03_StickersToSpellWord.java new file mode 100644 index 0000000..90b3a4d --- /dev/null +++ b/src/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/src/class19/Code04_LongestCommonSubsequence.java b/src/class19/Code04_LongestCommonSubsequence.java new file mode 100644 index 0000000..5e49551 --- /dev/null +++ b/src/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/src/class20/Code01_PalindromeSubsequence.java b/src/class20/Code01_PalindromeSubsequence.java new file mode 100644 index 0000000..dd38429 --- /dev/null +++ b/src/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/src/class20/Code02_HorseJump.java b/src/class20/Code02_HorseJump.java new file mode 100644 index 0000000..f96f4a1 --- /dev/null +++ b/src/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/src/class20/Code03_Coffee.java b/src/class20/Code03_Coffee.java new file mode 100644 index 0000000..eb2b620 --- /dev/null +++ b/src/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/src/class21/Code01_MinPathSum.java b/src/class21/Code01_MinPathSum.java new file mode 100644 index 0000000..c537438 --- /dev/null +++ b/src/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/src/class21/Code02_CoinsWayEveryPaperDifferent.java b/src/class21/Code02_CoinsWayEveryPaperDifferent.java new file mode 100644 index 0000000..a162b06 --- /dev/null +++ b/src/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/src/class21/Code03_CoinsWayNoLimit.java b/src/class21/Code03_CoinsWayNoLimit.java new file mode 100644 index 0000000..7759d9c --- /dev/null +++ b/src/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/src/class21/Code04_CoinsWaySameValueSamePapper.java b/src/class21/Code04_CoinsWaySameValueSamePapper.java new file mode 100644 index 0000000..f45c919 --- /dev/null +++ b/src/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/src/class21/Code05_BobDie.java b/src/class21/Code05_BobDie.java new file mode 100644 index 0000000..439fffe --- /dev/null +++ b/src/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/src/class22/Code01_KillMonster.java b/src/class22/Code01_KillMonster.java new file mode 100644 index 0000000..77fae7b --- /dev/null +++ b/src/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/src/class22/Code02_MinCoinsNoLimit.java b/src/class22/Code02_MinCoinsNoLimit.java new file mode 100644 index 0000000..06e4843 --- /dev/null +++ b/src/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/src/class22/Code03_SplitNumber.java b/src/class22/Code03_SplitNumber.java new file mode 100644 index 0000000..23193bc --- /dev/null +++ b/src/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/src/class23/Code01_SplitSumClosed.java b/src/class23/Code01_SplitSumClosed.java new file mode 100644 index 0000000..6f82c49 --- /dev/null +++ b/src/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/src/class23/Code02_SplitSumClosedSizeHalf.java b/src/class23/Code02_SplitSumClosedSizeHalf.java new file mode 100644 index 0000000..0da40fb --- /dev/null +++ b/src/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/src/class23/Code03_NQueens.java b/src/class23/Code03_NQueens.java new file mode 100644 index 0000000..8d1548a --- /dev/null +++ b/src/class23/Code03_NQueens.java @@ -0,0 +1,97 @@ +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.... 后续有多少合法的方法数 + + /** + * + * @param i:表示我现在要决定放置的皇后的位置。 + * @param record:记录每一个皇后放的位置 + * @param n:总共要放多少个皇后 + * @return + */ + 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/src/class24/Code01_SlidingWindowMaxArray.java b/src/class24/Code01_SlidingWindowMaxArray.java new file mode 100644 index 0000000..d115951 --- /dev/null +++ b/src/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/src/class24/Code02_AllLessNumSubArray.java b/src/class24/Code02_AllLessNumSubArray.java new file mode 100644 index 0000000..7bbd5de --- /dev/null +++ b/src/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/src/class24/Code03_GasStation.java b/src/class24/Code03_GasStation.java new file mode 100644 index 0000000..b80906f --- /dev/null +++ b/src/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/src/class24/Code04_MinCoinsOnePaper.java b/src/class24/Code04_MinCoinsOnePaper.java new file mode 100644 index 0000000..cfda95a --- /dev/null +++ b/src/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/src/class25/Code01_MonotonousStack.java b/src/class25/Code01_MonotonousStack.java new file mode 100644 index 0000000..b823a30 --- /dev/null +++ b/src/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/src/class25/Code02_AllTimesMinToMax.java b/src/class25/Code02_AllTimesMinToMax.java new file mode 100644 index 0000000..2f26ac1 --- /dev/null +++ b/src/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/src/class25/Code03_LargestRectangleInHistogram.java b/src/class25/Code03_LargestRectangleInHistogram.java new file mode 100644 index 0000000..d2d1ce5 --- /dev/null +++ b/src/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/src/class25/Code04_MaximalRectangle.java b/src/class25/Code04_MaximalRectangle.java new file mode 100644 index 0000000..e2b9d37 --- /dev/null +++ b/src/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/src/class25/Code05_CountSubmatricesWithAllOnes.java b/src/class25/Code05_CountSubmatricesWithAllOnes.java new file mode 100644 index 0000000..f24d02e --- /dev/null +++ b/src/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/src/class26/Code01_SumOfSubarrayMinimums.java b/src/class26/Code01_SumOfSubarrayMinimums.java new file mode 100644 index 0000000..343247b --- /dev/null +++ b/src/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/src/class26/Code02_FibonacciProblem.java b/src/class26/Code02_FibonacciProblem.java new file mode 100644 index 0000000..248c731 --- /dev/null +++ b/src/class26/Code02_FibonacciProblem.java @@ -0,0 +1,186 @@ +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 = muliMatrix(res, t); + } + t = muliMatrix(t, t); + } + return res; + } + + // 两个矩阵乘完之后的结果返回 + public static int[][] muliMatrix(int[][] m1, int[][] m2) { + int[][] res = new int[m1.length][m2[0].length]; + for (int i = 0; i < m1.length; i++) { + for (int j = 0; j < m2[0].length; j++) { + for (int k = 0; k < m2.length; k++) { + res[i][j] += m1[i][k] * m2[k][j]; + } + } + } + return res; + } + + 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/src/class26/Code03_ZeroLeftOneStringNumber.java b/src/class26/Code03_ZeroLeftOneStringNumber.java new file mode 100644 index 0000000..b21906e --- /dev/null +++ b/src/class26/Code03_ZeroLeftOneStringNumber.java @@ -0,0 +1,109 @@ +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 = muliMatrix(res, tmp); + } + tmp = muliMatrix(tmp, tmp); + } + return res; + } + + public static int[][] muliMatrix(int[][] m1, int[][] m2) { + int[][] res = new int[m1.length][m2[0].length]; + for (int i = 0; i < m1.length; i++) { + for (int j = 0; j < m2[0].length; j++) { + for (int k = 0; k < m2.length; k++) { + res[i][j] += m1[i][k] * m2[k][j]; + } + } + } + return res; + } + + 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/src/class27/Code01_KMP.java b/src/class27/Code01_KMP.java new file mode 100644 index 0000000..68a51de --- /dev/null +++ b/src/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/src/class27/Code02_TreeEqual.java b/src/class27/Code02_TreeEqual.java new file mode 100644 index 0000000..657373a --- /dev/null +++ b/src/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/src/class27/Code03_IsRotation.java b/src/class27/Code03_IsRotation.java new file mode 100644 index 0000000..605cb81 --- /dev/null +++ b/src/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/src/class28/Code01_Manacher.java b/src/class28/Code01_Manacher.java new file mode 100644 index 0000000..c75975f --- /dev/null +++ b/src/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/src/class28/Code02_AddShortestEnd.java b/src/class28/Code02_AddShortestEnd.java new file mode 100644 index 0000000..00d961c --- /dev/null +++ b/src/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/src/class29/Code01_FindMinKth.java b/src/class29/Code01_FindMinKth.java new file mode 100644 index 0000000..e317550 --- /dev/null +++ b/src/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/src/class29/Code02_MaxTopK.java b/src/class29/Code02_MaxTopK.java new file mode 100644 index 0000000..b300171 --- /dev/null +++ b/src/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/src/class29/Code03_ReservoirSampling.java b/src/class29/Code03_ReservoirSampling.java new file mode 100644 index 0000000..c2ce9e3 --- /dev/null +++ b/src/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/src/class30/Code01_MorrisTraversal.java b/src/class30/Code01_MorrisTraversal.java new file mode 100644 index 0000000..2e3a4a4 --- /dev/null +++ b/src/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/src/class30/Code05_MinHeight.java b/src/class30/Code05_MinHeight.java new file mode 100644 index 0000000..9814825 --- /dev/null +++ b/src/class30/Code05_MinHeight.java @@ -0,0 +1,118 @@ +package class30; + +public class Code05_MinHeight { + + public static class Node { + public int val; + public Node left; + public Node right; + + public Node(int x) { + val = x; + } + } + + public static int minHeight1(Node head) { + if (head == null) { + return 0; + } + return p(head); + } + + // 返回x为头的树,最小深度是多少 + public static int p(Node 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 minHeight2(Node head) { + if (head == null) { + return 0; + } + Node cur = head; + Node 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; + } + + // 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 treeLevel = 7; + int nodeMaxValue = 5; + int testTimes = 100000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + Node head = generateRandomBST(treeLevel, nodeMaxValue); + int ans1 = minHeight1(head); + int ans2 = minHeight2(head); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test finish!"); + + } + +} diff --git a/src/class31/Code01_SegmentTree.java b/src/class31/Code01_SegmentTree.java new file mode 100644 index 0000000..282c20a --- /dev/null +++ b/src/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/src/class31/Code02_FallingSquares.java b/src/class31/Code02_FallingSquares.java new file mode 100644 index 0000000..2f3b2a2 --- /dev/null +++ b/src/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/src/class32/Code01_IndexTree.java b/src/class32/Code01_IndexTree.java new file mode 100644 index 0000000..4c4bdab --- /dev/null +++ b/src/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/src/class32/Code02_IndexTree2D.java b/src/class32/Code02_IndexTree2D.java new file mode 100644 index 0000000..12bb6e2 --- /dev/null +++ b/src/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/src/class32/Code03_AC1.java b/src/class32/Code03_AC1.java new file mode 100644 index 0000000..a6874e5 --- /dev/null +++ b/src/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/src/class32/Code04_AC2.java b/src/class32/Code04_AC2.java new file mode 100644 index 0000000..e684de3 --- /dev/null +++ b/src/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/src/class33/Hash.java b/src/class33/Hash.java new file mode 100644 index 0000000..18eb58a --- /dev/null +++ b/src/class33/Hash.java @@ -0,0 +1,48 @@ +//package class33; +// +//import java.security.MessageDigest; +//import java.security.NoSuchAlgorithmException; +//import java.security.Security; +// +//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 DatatypeConerter.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/src/class34/ReadMe.java b/src/class34/ReadMe.java new file mode 100644 index 0000000..16e42b9 --- /dev/null +++ b/src/class34/ReadMe.java @@ -0,0 +1,2 @@ +// 本章并无code,因为资源限制类题目输入需要的条件较多并且真的实现代码量巨大 +// 面试中这类题目出现也就和面试官聊解法,不会有代码实现的要求 \ No newline at end of file diff --git a/src/class35/Code01_AVLTreeMap.java b/src/class35/Code01_AVLTreeMap.java new file mode 100644 index 0000000..caafd46 --- /dev/null +++ b/src/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/src/class36/Code01_SizeBalancedTreeMap.java b/src/class36/Code01_SizeBalancedTreeMap.java new file mode 100644 index 0000000..5077b7a --- /dev/null +++ b/src/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/src/class36/Code02_SkipListMap.java b/src/class36/Code02_SkipListMap.java new file mode 100644 index 0000000..a450e78 --- /dev/null +++ b/src/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/src/class37/Code01_CountofRangeSum.java b/src/class37/Code01_CountofRangeSum.java new file mode 100644 index 0000000..d79fa02 --- /dev/null +++ b/src/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/src/class37/Code02_SlidingWindowMedian.java b/src/class37/Code02_SlidingWindowMedian.java new file mode 100644 index 0000000..2f3fa63 --- /dev/null +++ b/src/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/src/class37/Code03_AddRemoveGetIndexGreat.java b/src/class37/Code03_AddRemoveGetIndexGreat.java new file mode 100644 index 0000000..2c2436a --- /dev/null +++ b/src/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/src/class37/Code04_QueueReconstructionByHeight.java b/src/class37/Code04_QueueReconstructionByHeight.java new file mode 100644 index 0000000..2513402 --- /dev/null +++ b/src/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/src/class37/Compare.java b/src/class37/Compare.java new file mode 100644 index 0000000..5bbfd0d --- /dev/null +++ b/src/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/src/class38/Code01_AppleMinBags.java b/src/class38/Code01_AppleMinBags.java new file mode 100644 index 0000000..e1b5f21 --- /dev/null +++ b/src/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/src/class38/Code02_EatGrass.java b/src/class38/Code02_EatGrass.java new file mode 100644 index 0000000..8b400fb --- /dev/null +++ b/src/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/src/class38/Code03_MSumToN.java b/src/class38/Code03_MSumToN.java new file mode 100644 index 0000000..50c72be --- /dev/null +++ b/src/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/src/class38/Code04_MoneyProblem.java b/src/class38/Code04_MoneyProblem.java new file mode 100644 index 0000000..722c5a6 --- /dev/null +++ b/src/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/src/class39/Code01_SubsquenceMaxModM.java b/src/class39/Code01_SubsquenceMaxModM.java new file mode 100644 index 0000000..9890bf4 --- /dev/null +++ b/src/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/src/class39/Code02_SnacksWays.java b/src/class39/Code02_SnacksWays.java new file mode 100644 index 0000000..bb2c538 --- /dev/null +++ b/src/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/src/class39/Code02_SnacksWaysMain.java b/src/class39/Code02_SnacksWaysMain.java new file mode 100644 index 0000000..a224dc7 --- /dev/null +++ b/src/class39/Code02_SnacksWaysMain.java @@ -0,0 +1,125 @@ +//不要拷贝包信息的内容 +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_SnacksWaysMain { + + 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/src/class39/Code03_10Ways.java b/src/class39/Code03_10Ways.java new file mode 100644 index 0000000..1afffb7 --- /dev/null +++ b/src/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/src/class39/Code04_DifferentBTNum.java b/src/class39/Code04_DifferentBTNum.java new file mode 100644 index 0000000..3c48911 --- /dev/null +++ b/src/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/src/class39/IsSum.java b/src/class39/IsSum.java new file mode 100644 index 0000000..9ff7a3c --- /dev/null +++ b/src/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/src/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java b/src/class40/Code01_LongestSumSubArrayLengthInPositiveArray.java new file mode 100644 index 0000000..a2389fa --- /dev/null +++ b/src/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/src/class40/Code02_LongestSumSubArrayLength.java b/src/class40/Code02_LongestSumSubArrayLength.java new file mode 100644 index 0000000..a89fb14 --- /dev/null +++ b/src/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/src/class40/Code03_LongestLessSumSubArrayLength.java b/src/class40/Code03_LongestLessSumSubArrayLength.java new file mode 100644 index 0000000..c4ebc56 --- /dev/null +++ b/src/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/src/class40/Code04_AvgLessEqualValueLongestSubarray.java b/src/class40/Code04_AvgLessEqualValueLongestSubarray.java new file mode 100644 index 0000000..c56eef9 --- /dev/null +++ b/src/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/src/class40/Code05_PrintMatrixSpiralOrder.java b/src/class40/Code05_PrintMatrixSpiralOrder.java new file mode 100644 index 0000000..cb3f80c --- /dev/null +++ b/src/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/src/class40/Code06_RotateMatrix.java b/src/class40/Code06_RotateMatrix.java new file mode 100644 index 0000000..4e9534f --- /dev/null +++ b/src/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/src/class40/Code07_ZigZagPrintMatrix.java b/src/class40/Code07_ZigZagPrintMatrix.java new file mode 100644 index 0000000..01b9921 --- /dev/null +++ b/src/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/src/class40/Code08_PrintStar.java b/src/class40/Code08_PrintStar.java new file mode 100644 index 0000000..77d41ef --- /dev/null +++ b/src/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/src/class41/Code01_BestSplitForAll.java b/src/class41/Code01_BestSplitForAll.java new file mode 100644 index 0000000..a4eddb9 --- /dev/null +++ b/src/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/src/class41/Code02_BestSplitForEveryPosition.java b/src/class41/Code02_BestSplitForEveryPosition.java new file mode 100644 index 0000000..d98b389 --- /dev/null +++ b/src/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/src/class41/Code03_StoneMerge.java b/src/class41/Code03_StoneMerge.java new file mode 100644 index 0000000..793d7d2 --- /dev/null +++ b/src/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/src/class41/Code04_SplitArrayLargestSum.java b/src/class41/Code04_SplitArrayLargestSum.java new file mode 100644 index 0000000..8d644e6 --- /dev/null +++ b/src/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/src/class42/Code01_PostOfficeProblem.java b/src/class42/Code01_PostOfficeProblem.java new file mode 100644 index 0000000..c6fa893 --- /dev/null +++ b/src/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/src/class42/Code02_ThrowChessPiecesProblem.java b/src/class42/Code02_ThrowChessPiecesProblem.java new file mode 100644 index 0000000..d996c70 --- /dev/null +++ b/src/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/src/class43/Code01_CanIWin.java b/src/class43/Code01_CanIWin.java new file mode 100644 index 0000000..608b1f1 --- /dev/null +++ b/src/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/src/class43/Code02_TSP.java b/src/class43/Code02_TSP.java new file mode 100644 index 0000000..bf984a6 --- /dev/null +++ b/src/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/src/class43/Code03_PavingTile.java b/src/class43/Code03_PavingTile.java new file mode 100644 index 0000000..c4bf363 --- /dev/null +++ b/src/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/src/class44/Code01_LastSubstringInLexicographicalOrder.java b/src/class44/Code01_LastSubstringInLexicographicalOrder.java new file mode 100644 index 0000000..27ac147 --- /dev/null +++ b/src/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/src/class44/DC3.java b/src/class44/DC3.java new file mode 100644 index 0000000..47e7601 --- /dev/null +++ b/src/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/src/class44/DC3_Algorithm.pdf b/src/class44/DC3_Algorithm.pdf new file mode 100644 index 0000000..f053a67 Binary files /dev/null and b/src/class44/DC3_Algorithm.pdf differ diff --git a/src/class45/Code01_InsertS2MakeMostAlphabeticalOrder.java b/src/class45/Code01_InsertS2MakeMostAlphabeticalOrder.java new file mode 100644 index 0000000..0962781 --- /dev/null +++ b/src/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/src/class45/Code02_CreateMaximumNumber.java b/src/class45/Code02_CreateMaximumNumber.java new file mode 100644 index 0000000..1e9cc28 --- /dev/null +++ b/src/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/src/class45/Code03_LongestCommonSubstringConquerByHeight.java b/src/class45/Code03_LongestCommonSubstringConquerByHeight.java new file mode 100644 index 0000000..2b92fff --- /dev/null +++ b/src/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/src/class46/Code01_BurstBalloons.java b/src/class46/Code01_BurstBalloons.java new file mode 100644 index 0000000..71d2602 --- /dev/null +++ b/src/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/src/class46/Code02_RemoveBoxes.java b/src/class46/Code02_RemoveBoxes.java new file mode 100644 index 0000000..8db4d8f --- /dev/null +++ b/src/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/src/class46/Code03_DeleteAdjacentSameCharacter.java b/src/class46/Code03_DeleteAdjacentSameCharacter.java new file mode 100644 index 0000000..0b1e7f3 --- /dev/null +++ b/src/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/src/class46/Code04_MaxSumLengthNoMore.java b/src/class46/Code04_MaxSumLengthNoMore.java new file mode 100644 index 0000000..be50615 --- /dev/null +++ b/src/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/src/class46/Code05_HuffmanTree.java b/src/class46/Code05_HuffmanTree.java new file mode 100644 index 0000000..65b6be8 --- /dev/null +++ b/src/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/src/class47/Code01_StrangePrinter.java b/src/class47/Code01_StrangePrinter.java new file mode 100644 index 0000000..b7172db --- /dev/null +++ b/src/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/src/class47/Code02_RestoreWays.java b/src/class47/Code02_RestoreWays.java new file mode 100644 index 0000000..c9c583a --- /dev/null +++ b/src/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/src/class47/Code03_DinicAlgorithm.java b/src/class47/Code03_DinicAlgorithm.java new file mode 100644 index 0000000..efed284 --- /dev/null +++ b/src/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