diff --git a/src/class24/Code01_SlidingWindowMaxArray.java b/src/class24/Code01_SlidingWindowMaxArray.java new file mode 100644 index 0000000..23671dc --- /dev/null +++ b/src/class24/Code01_SlidingWindowMaxArray.java @@ -0,0 +1,103 @@ +package class24; + +import java.util.LinkedList; + +public class Code01_SlidingWindowMaxArray { + + public static int[] getMaxWindow(int[] arr, int w) { + if (arr == null || w < 1 || arr.length < w) { + return null; + } + // 其中放的是位置,头代表 (大->小)尾 + LinkedList qmax = new LinkedList(); + int[] res = new int[arr.length - w + 1]; + int index = 0; + // L...R + // i + for (int R = 0; R < arr.length; R++) { // 当前让 i -> [i] 进窗口 , i 就是 r + // R -> 值 可以放在比他大的数后,或者空 + while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]) { + qmax.pollLast(); + } + qmax.addLast(R); + // 数进来了 + // 如果窗口没有形成W的长度之前,不弹出数字的 + if (qmax.peekFirst() == R - w) { + qmax.pollFirst(); + } + // 以上窗口更新做完了 + if (R >= w - 1) { + res[index++] = arr[qmax.peekFirst()]; + } + } + return res; + } + + // for test + public static int[] rightWay(int[] arr, int w) { + if (arr == null || w < 1 || arr.length < w) { + return null; + } + int[] res = new int[arr.length - w + 1]; + int index = 0; + int L = 0; + int R = w - 1; + while (R < arr.length) { + 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; + } + + // 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 = rightWay(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..40f6107 --- /dev/null +++ b/src/class24/Code02_AllLessNumSubArray.java @@ -0,0 +1,85 @@ +package class24; + +import java.util.LinkedList; + +public class Code02_AllLessNumSubArray { + + public static int getNum(int[] arr, int num) { + if (arr == null || arr.length == 0) { + return 0; + } + LinkedList qmin = new LinkedList(); + LinkedList qmax = new LinkedList(); + int L = 0; + int R = 0; + // [L..R) -> [0,0) 窗口内无数 [1,1) + // [0,1) -> [0~0] + int res = 0; + while (L < arr.length) { // L是开头位置,尝试每一个开头 + + // 如果此时窗口的开头是L,下面的while工作:R向右扩到违规为止 + + while (R < arr.length) { // R是最后一个达标位置的再下一个 + while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[R]) { + qmin.pollLast(); + } + qmin.addLast(R); + // R -> arr[R], + while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]) { + qmax.pollLast(); + } + qmax.addLast(R); + + if (arr[qmax.getFirst()] - arr[qmin.getFirst()] > num) { + break; + } + R++; + } + + // R是最后一个达标位置的再下一个,第一个违规的位置 + res += R - L; + + if (qmin.peekFirst() == L) { + qmin.pollFirst(); + } + if (qmax.peekFirst() == L) { + qmax.pollFirst(); + } + + L++; + + } + return res; + } + + // for test + public static int[] getRandomArray(int len) { + if (len < 0) { + return null; + } + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * 10); + } + 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[] arr = getRandomArray(30); + int num = 5; + printArray(arr); + System.out.println(getNum(arr, num)); + + } + +} 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/class23/Code04_MinCoinsOnePaper.java b/src/class24/Code04_MinCoinsOnePaper.java similarity index 99% rename from src/class23/Code04_MinCoinsOnePaper.java rename to src/class24/Code04_MinCoinsOnePaper.java index bf48243..a0a73b8 100644 --- a/src/class23/Code04_MinCoinsOnePaper.java +++ b/src/class24/Code04_MinCoinsOnePaper.java @@ -1,4 +1,4 @@ -package class23; +package class24; import java.util.HashMap; import java.util.Map.Entry; diff --git a/src/class25/Code01_MonotonousStack.java b/src/class25/Code01_MonotonousStack.java new file mode 100644 index 0000000..c9bb357 --- /dev/null +++ b/src/class25/Code01_MonotonousStack.java @@ -0,0 +1,158 @@ +package class25; + +import java.util.List; +import java.util.ArrayList; +import java.util.Stack; + +public class Code01_MonotonousStack { + + 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++) { + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + int popIndex = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); + res[popIndex][0] = leftLessIndex; + res[popIndex][1] = i; + } + stack.push(i); + } + while (!stack.isEmpty()) { + int popIndex = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); + res[popIndex][0] = leftLessIndex; + res[popIndex][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; + 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; + } + } + } +} diff --git a/src/class25/Code02_AllTimesMinToMax.java b/src/class25/Code02_AllTimesMinToMax.java new file mode 100644 index 0000000..d0cdef8 --- /dev/null +++ b/src/class25/Code02_AllTimesMinToMax.java @@ -0,0 +1,67 @@ +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"); + } + +} diff --git a/src/class25/Code03_SumOfSubarrayMinimums.java b/src/class25/Code03_SumOfSubarrayMinimums.java new file mode 100644 index 0000000..12e4741 --- /dev/null +++ b/src/class25/Code03_SumOfSubarrayMinimums.java @@ -0,0 +1,153 @@ +package class25; + +import java.util.Stack; + +// 测试链接:https://leetcode.com/problems/sum-of-subarray-minimums/ +// subArrayMinSum1是暴力解 +// subArrayMinSum2是最优解的思路 +// sumSubarrayMins是最优解思路下的单调栈优化 +// Leetcode上只提交sumSubarrayMins方法,时间复杂度O(N),可以直接通过 +public class Code03_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) { + int[] left = leftNearLessEqual2(arr); + 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[] left = nearLessEqualLeft(arr); + int[] right = nearLessRight(arr); + 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 N = arr.length; + int[] left = new int[N]; + Stack stack = new Stack<>(); + for (int i = N - 1; i >= 0; i--) { + while (!stack.isEmpty() && arr[i] <= arr[stack.peek()]) { + left[stack.pop()] = i; + } + stack.push(i); + } + while (!stack.isEmpty()) { + left[stack.pop()] = -1; + } + return left; + } + + public static int[] nearLessRight(int[] arr) { + int N = arr.length; + int[] right = new int[N]; + Stack stack = new Stack<>(); + for (int i = 0; i < N; i++) { + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + right[stack.pop()] = i; + } + stack.push(i); + } + while (!stack.isEmpty()) { + right[stack.pop()] = 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/class25/Code04_LargestRectangleInHistogram.java b/src/class25/Code04_LargestRectangleInHistogram.java new file mode 100644 index 0000000..1123ef7 --- /dev/null +++ b/src/class25/Code04_LargestRectangleInHistogram.java @@ -0,0 +1,58 @@ +package class25; + +import java.util.Stack; + +// 测试链接:https://leetcode.com/problems/largest-rectangle-in-histogram +public class Code04_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; + } + +}