diff --git a/src/main/java/binary_search/FindMedianSortedArrays.java b/src/main/java/binary_search/FindMedianSortedArrays.java index 336da1d..e4d0f02 100644 --- a/src/main/java/binary_search/FindMedianSortedArrays.java +++ b/src/main/java/binary_search/FindMedianSortedArrays.java @@ -17,6 +17,7 @@ package binary_search; //二刷不会 public class FindMedianSortedArrays { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { int length1 = nums1.length, length2 = nums2.length; int totalLength = length1 + length2; diff --git a/src/main/java/heap/FindKthLargest.java b/src/main/java/heap/FindKthLargest.java index 12cc731..52c1858 100644 --- a/src/main/java/heap/FindKthLargest.java +++ b/src/main/java/heap/FindKthLargest.java @@ -15,8 +15,10 @@ import java.util.PriorityQueue; * 链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/ */ +//二刷不会 背一下堆的创建 快速选择 public class FindKthLargest { - //完全填满堆再删除元素 最大堆 + + //完全填满堆再删除元素 最大堆 O(nlogn) public int findKthLargest1(int[] nums, int k) { PriorityQueuemaxheap=new PriorityQueue<>(Comparator.reverseOrder()); for (int num : nums) { @@ -28,6 +30,7 @@ public class FindKthLargest { return maxheap.peek(); } //使用最小堆来存储前 k 个最大的元素。堆顶元素是 k 个最大元素中的最小值。这样,堆的大小始终保持为 k。当堆的大小大于 k 时,弹出堆顶元素。最终堆顶元素就是第 k 大的元素。不需要完全填满堆再删除元素: + // O(nlogk) public int findKthLargest2(int[] nums, int k) { // 使用最小堆 PriorityQueue minHeap = new PriorityQueue<>(); @@ -47,7 +50,7 @@ public class FindKthLargest { // 返回堆顶元素,即为第 k 大的元素 return minHeap.peek(); } - //快速选择 + //快速选择 O(n) public int quickselect(int[] nums, int l, int r, int k) { // 当区间内只有一个元素时,直接返回该元素 if (l >= r) { diff --git a/src/main/java/heap/KSmallestPairs.java b/src/main/java/heap/KSmallestPairs.java index 42837a8..87ac03a 100644 --- a/src/main/java/heap/KSmallestPairs.java +++ b/src/main/java/heap/KSmallestPairs.java @@ -19,17 +19,16 @@ import java.util.PriorityQueue; * 链接:https://leetcode.cn/problems/find-k-pairs-with-smallest-sums/ */ //有思路 但是超出时间限制,需要优化 +//二刷不会优化 public class KSmallestPairs { + /** * 可以把所有可能的组合看作一个虚拟的二维矩阵: - * * 行代表 nums1[i] * 列代表 nums2[j] * 每个点 (i,j) 对应的值是 nums1[i] + nums2[j] * 这个矩阵的特点是:从左到右、从上到下递增,因为两个数组都升序。 - * * 我们不需要全部生成矩阵,而是只维护当前“最小的候选对”,类似 Dijkstra 算法或 BFS: - * * 初始时,把前 k 行的第一列(即 (nums1[i], nums2[0]))放入堆中。因为后面的列比前面的大,我们先选列头。 * 每次弹出堆顶(最小的和),把它加入结果列表。 * 如果该对还有下一列(即 (i, j+1) 存在),就把 (i, j+1) 加入堆,作为下一个候选。 @@ -45,26 +44,25 @@ public class KSmallestPairs { // 边界处理:任一数组为空 或 k==0,直接返回空结果 if (nums1.length == 0 || nums2.length == 0 || k == 0) return res; - // 小顶堆,存储的是每对数的索引 [i, j],按对应的和升序排列 + // 小顶堆,存储的是每对数的索引 [i, j],按对应的和 升序排列 PriorityQueue minHeap = new PriorityQueue<>( (a, b) -> nums1[a[0]] + nums2[a[1]] - nums1[b[0]] - nums2[b[1]] ); - // 初始化:将 nums1 的前 k 个元素与 nums2[0] 组成的数对放入堆中 - // 因为只推进列,所以只需要每一行的第一个元素 + // 初始:把每一行的第 0 列(最多 k 行)加入堆 for (int i = 0; i < Math.min(nums1.length, k); i++) { minHeap.offer(new int[]{i, 0}); // i 表示 nums1 的索引,j=0 是固定的起点 } - // 取出堆顶最小的数对,直到取满 k 个或者堆空为止 + // 取最小的 k 对 while (k-- > 0 && !minHeap.isEmpty()) { - int[] pos = minHeap.poll(); // 当前最小的数对在 nums1[pos[0]], nums2[pos[1]] - int i = pos[0], j = pos[1]; + int[] idx = minHeap.poll(); + int i = idx[0], j = idx[1]; // 把数对值加入结果集 res.add(List.of(nums1[i], nums2[j])); - // 如果当前数对还有下一列,就推进下一列(仍然在第 i 行) + // 推入同一行的下一列 若这一行所有可能的列都已被消费完了,就不再往堆里推新的 (i, j+1) if (j + 1 < nums2.length) { minHeap.offer(new int[]{i, j + 1}); } diff --git a/src/main/java/heap/TopKFrequent.java b/src/main/java/heap/TopKFrequent.java index 7a1c9e0..eab21a7 100644 --- a/src/main/java/heap/TopKFrequent.java +++ b/src/main/java/heap/TopKFrequent.java @@ -14,10 +14,46 @@ import java.util.PriorityQueue; * 链接:https://leetcode.cn/problems/top-k-frequent-elements/ */ +// 二刷不会 public class TopKFrequent { + public int[] topKFrequent(int[] nums, int k) { + HashMap map=new HashMap<>(); + int cnt=0; + PriorityQueue minHeap=new PriorityQueue<>( //小根堆 + (a,b) -> a[1]-b[1] + ); + int[] temp; + int[] res=new int[k]; + for (int i = 0; i < nums.length; i++) { + int cur=nums[i]; + map.put(cur, map.getOrDefault(cur,0)+1); + } + for (Map.Entry entry : map.entrySet()) { + if(cnttemp[1]) { + minHeap.poll(); + minHeap.add(new int[]{entry.getKey(),entry.getValue()}); + } + } + } + cnt=0; + for (int[] ints : minHeap) { + res[cnt++]=ints[0]; + } + return res; + } + + + + + public int[] topKFrequent2(int[] nums, int k) { HashMapmap=new HashMap<>(); - PriorityQueue minHeap = new PriorityQueue<>( + PriorityQueue maxHeap = new PriorityQueue<>( //大根堆 (a, b) -> b[1] - a[1]); int[] res=new int[k]; int i=0; @@ -28,11 +64,11 @@ public class TopKFrequent { map.put(num,map.get(num)+1); } for (Map.Entry entry : map.entrySet()) { - minHeap.add(new int[]{entry.getKey(),entry.getValue()}); + maxHeap.add(new int[]{entry.getKey(),entry.getValue()}); } - while (!minHeap.isEmpty()) { + while (!maxHeap.isEmpty()) { if(i stack = new ArrayDeque<>(); + + for (int i = 0; i < n; i++) { + // 只要栈不空,且当前温度更高,就把栈顶结算出来 + while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { + int prev = stack.pop(); + ans[prev] = i - prev; + } + stack.push(i); + } + + // 剩下的索引对应的 ans 已经是 0 了(Java 数组默认值) + return ans; + } + + + public int[] dailyTemperatures1(int[] temperatures) { // 结果数组,初始化为全零 int[] result = new int[temperatures.length]; diff --git a/src/main/java/stack/DecodeString.java b/src/main/java/stack/DecodeString.java index 626758a..183316f 100644 --- a/src/main/java/stack/DecodeString.java +++ b/src/main/java/stack/DecodeString.java @@ -3,6 +3,7 @@ package stack; import java.util.ArrayDeque; import java.util.Deque; import java.util.LinkedList; +import java.util.Stack; /** * 题目: 394. 字符串解码 (decodeString) @@ -18,8 +19,10 @@ import java.util.LinkedList; * 链接:https://leetcode.cn/problems/search-in-rotated-sorted-array/ */ //不会 有难度! +//二刷不会 public class DecodeString { - //s = "3[a]2[bc]" "3[a2[c]]" + +// s = "3[a]2[bc]" "3[a2[c]]" public String decodeString(String s) { // 定义两个栈 Deque countStack = new LinkedList<>(); diff --git a/src/main/java/stack/IsValid.java b/src/main/java/stack/IsValid.java index 8fcb1da..b73555e 100644 --- a/src/main/java/stack/IsValid.java +++ b/src/main/java/stack/IsValid.java @@ -16,7 +16,10 @@ import java.util.HashMap; * * 链接:https://leetcode.cn/problems/valid-parentheses/ */ +//二刷会做 public class IsValid { + + public boolean isValid(String s) { // 如果长度为奇数,直接返回 false int sz = s.length(); diff --git a/src/main/java/stack/LargestRectangleArea.java b/src/main/java/stack/LargestRectangleArea.java index e61bdcc..a09a1ad 100644 --- a/src/main/java/stack/LargestRectangleArea.java +++ b/src/main/java/stack/LargestRectangleArea.java @@ -16,94 +16,151 @@ import java.util.Deque; * 链接:https://leetcode.cn/problems/largest-rectangle-in-histogram/ */ //初见不会 +//二刷不会 public class LargestRectangleArea { - //暴力法 依次遍历柱形的高度,对于每一个高度分别向两边扩散,求出以当前高度为矩形的最大宽度多少。 - public int largestRectangleArea1(int[] heights) { - int len = heights.length; - // 特判 - if (len == 0) { - return 0; - } - int res = 0; - for (int i = 0; i < len; i++) { +// public int largestRectangleArea(int[] heights) { +// int len=heights.length; +// int[] leftIndex=new int[len],rightIndex=new int[len]; +// leftIndex[0]=-1; +// rightIndex[len-1]=len; +// for (int i = 1; i < len; i++) { +// int cur=heights[i]; +// int left=i-1; +// //向左找到第一个小于cur的index +// while (left>=0 && cur<=heights[left]){ +// left=leftIndex[left]; +// } +// leftIndex[i]=left; +// } +// for (int i = len-2; i >=0 ; i--) { +// int cur=heights[i]; +// int right=i+1; +// while (right 0 && heights[left - 1] >= curHeight) { - left--; - } - - // 找右边最后 1 个大于等于 heights[i] 的索引 - int right = i; - while (right < len - 1 && heights[right + 1] >= curHeight) { - right++; - } - - int width = right - left + 1; - res = Math.max(res, width * curHeight); - } - return res; - } - //动态规划 暴力解优化 - public int largestRectangleArea2(int[] heights) { - int size = heights.length; - int[] minLeftIndex = new int[size]; - int[] minRightIndex = new int[size]; - - // 记录每个柱子左边第一个小于该柱子的下标 - minLeftIndex[0] = -1; // 初始化,防止死循环 - for (int i = 1; i < size; i++) { - int t = i - 1; - // 不断向左寻找,直到找到第一个小于当前柱子的柱子 - while (t >= 0 && heights[t] >= heights[i]) { - t = minLeftIndex[t]; // 更新为左边第一个小于当前柱子的下标 - } - minLeftIndex[i] = t; - } - - // 记录每个柱子右边第一个小于该柱子的下标 - minRightIndex[size - 1] = size; // 初始化,防止死循环 - for (int i = size - 2; i >= 0; i--) { - int t = i + 1; - // 不断向右寻找,直到找到第一个小于当前柱子的柱子 - while (t < size && heights[t] >= heights[i]) { - t = minRightIndex[t]; // 更新为右边第一个小于当前柱子的下标 - } - minRightIndex[i] = t; - } - - // 计算最大矩形面积 - int result = 0; - for (int i = 0; i < size; i++) { - int width = minRightIndex[i] - minLeftIndex[i] - 1; - int area = heights[i] * width; - result = Math.max(result, area); - } - - return result; - } - //单调栈 + /** + * 暴力法 + * 对于每一个柱子 i,当做“最低高度”,向左扫描找到第一个高度比它小的位置 left, + * 向右扫描找到第一个比它小的位置 right,那么以它为高的最大矩形宽度就是 right–left–1,面积就是 heights[i] * (right - left - 1)。 + */ +// public int largestRectangleArea1(int[] heights) { +// int len = heights.length; +// // 特判 +// if (len == 0) { +// return 0; +// } +// +// int res = 0; +// for (int i = 0; i < len; i++) { +// +// // 找左边最后 1 个大于等于 heights[i] 的下标 +// int left = i; +// int curHeight = heights[i]; +// while (left > 0 && heights[left - 1] >= curHeight) { +// left--; +// } +// +// // 找右边最后 1 个大于等于 heights[i] 的索引 +// int right = i; +// while (right < len - 1 && heights[right + 1] >= curHeight) { +// right++; +// } +// +// int width = right - left + 1; +// res = Math.max(res, width * curHeight); +// } +// return res; +// } +// +// +// /** +// * 动态规划 暴力解优化 +// * 预先为每根柱子 i 计算出: +// * minLeftIndex[i]:右边第一个小于它的柱子的下标 +// * minRightIndex[i]:左边第一个小于它的柱子的下标 +// * 计算完后,对于每个 i,它能扩展的最大宽度就是 (minRightIndex[i] - minLeftIndex[i] - 1),面积同理用高度乘宽度即可。 +// +// */ +// public int largestRectangleArea2(int[] heights) { +// int size = heights.length; +// int[] minLeftIndex = new int[size]; +// int[] minRightIndex = new int[size]; +// +// // 记录每个柱子左边第一个小于该柱子的下标 +// minLeftIndex[0] = -1; // 初始化,防止死循环 +// for (int i = 1; i < size; i++) { +// int t = i - 1; +// // 不断向左寻找,直到找到第一个小于当前柱子的柱子 +// while (t >= 0 && heights[t] >= heights[i]) { +// t = minLeftIndex[t]; // 更新为左边第一个小于当前柱子的下标 +// } +// minLeftIndex[i] = t; +// } +// +// // 记录每个柱子右边第一个小于该柱子的下标 +// minRightIndex[size - 1] = size; // 初始化,防止死循环 +// for (int i = size - 2; i >= 0; i--) { +// int t = i + 1; +// // 不断向右寻找,直到找到第一个小于当前柱子的柱子 +// while (t < size && heights[t] >= heights[i]) { +// t = minRightIndex[t]; // 更新为右边第一个小于当前柱子的下标 +// } +// minRightIndex[i] = t; +// } +// +// // 计算最大矩形面积 +// int result = 0; +// for (int i = 0; i < size; i++) { +// int width = minRightIndex[i] - minLeftIndex[i] - 1; +// int area = heights[i] * width; +// result = Math.max(result, area); +// } +// +// return result; +// } +// + /** + * 单调栈解法思路: + * - 使用一个栈保存柱子的索引,栈内对应的高度单调递增。 + * - 遍历每个柱子 i: + * - 当当前柱子高度小于栈顶高度时,说明以栈顶柱子为高的矩形在 i 处结束: + * pop 出栈顶 prev,计算宽度为 i - stack.peek() - 1,面积 = heights[prev] * width。 + * - 重复上述步骤,直到栈空或栈顶高度不超过当前高度。 + * - 将当前索引 i 压入栈中。 + * - 遍历结束后,栈中仍有索引,依次弹出并计算: + * 宽度 = n - stack.peek() - 1。 + * - 时间复杂度 O(n),空间复杂度 O(n)。 + */ public int largestRectangleArea(int[] heights) { - Dequestack=new ArrayDeque<>(); - int max=Integer.MIN_VALUE; + int n = heights.length; + Deque stack = new ArrayDeque<>(); + // 先放一个哨兵,方便计算宽度 stack.push(-1); - for (int i = 0; i < heights.length; i++) { - while(stack.size()>=2 && heights[i]=2){ - int cur=stack.pop(); - int width=heights.length-stack.peek()-1; - if(max stack; // 主栈