diff --git a/src/main/java/slidingwindow/FindAnagrams.java b/src/main/java/slidingwindow/FindAnagrams.java index c549028..ebff381 100644 --- a/src/main/java/slidingwindow/FindAnagrams.java +++ b/src/main/java/slidingwindow/FindAnagrams.java @@ -28,6 +28,7 @@ import java.util.List; */ //没做出来 +//二刷会做 public class FindAnagrams { public List findAnagrams(String s, String p) { List result = new ArrayList<>(); diff --git a/src/main/java/slidingwindow/LengthOfLongestSubstring.java b/src/main/java/slidingwindow/LengthOfLongestSubstring.java index 2bdfd43..904c46e 100644 --- a/src/main/java/slidingwindow/LengthOfLongestSubstring.java +++ b/src/main/java/slidingwindow/LengthOfLongestSubstring.java @@ -1,6 +1,5 @@ package slidingwindow; -import java.util.HashMap; import java.util.HashSet; /** @@ -23,7 +22,7 @@ import java.util.HashSet; 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 */ - +//二刷会做 public class LengthOfLongestSubstring { public int lengthOfLongestSubstring(String s) { int left=0,right=0,max_length=0; diff --git a/src/main/java/substring/MaxSlidingWindow.java b/src/main/java/substring/MaxSlidingWindow.java index 6ce645f..36ba9fc 100644 --- a/src/main/java/substring/MaxSlidingWindow.java +++ b/src/main/java/substring/MaxSlidingWindow.java @@ -28,31 +28,55 @@ import java.util.*; 输出:[1] */ //普通方法超时 需重做 +//二刷也没想到低复杂度方法 public class MaxSlidingWindow { - //优先队列 + /** + * 思路说明:用优先队列(大顶堆)维护滑动窗口中的最大值 + * 滑动窗口长度为 k,我们每次都想拿到窗口内的最大值。 + * 用 PriorityQueue 维护一个大顶堆: + * 存入的是一个数组 new int[]{值, 下标}; + * 排序规则:按值降序,值相同时按下标降序(保证堆顶是最大值的最新位置)。 + * 每加入一个新元素,我们就把它压入堆; + * 然后检查堆顶:如果它的下标已经滑出窗口左边(≤ i - k),就移除它; + * 每次窗口滑完一个位置后,堆顶就是当前窗口最大值。 + * @param nums + * @param k + * @return + */ public int[] maxSlidingWindow1(int[] nums, int k) { int n = nums.length; - PriorityQueue pq = new PriorityQueue(new Comparator() { - public int compare(int[] pair1, int[] pair2) { - return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1]; - } - }); + + // 优先队列(大顶堆),按值降序;如果值相同,则按下标降序(保证更靠右的元素在上面) + PriorityQueue pq = new PriorityQueue<>( + (a, b) -> a[0] != b[0] ? b[0] - a[0] : b[1] - a[1] + ); + + // 预处理第一个窗口:放入前 k 个元素 for (int i = 0; i < k; ++i) { pq.offer(new int[]{nums[i], i}); } + + // 初始化结果数组(共 n - k + 1 个窗口) int[] ans = new int[n - k + 1]; - ans[0] = pq.peek()[0]; + ans[0] = pq.peek()[0]; // 第一个窗口的最大值 + + // 开始滑动窗口 for (int i = k; i < n; ++i) { + // 把新元素加入窗口 pq.offer(new int[]{nums[i], i}); - while (pq.peek()[1] <= i - k) { //如果堆顶元素不在滑动窗口内部,就移除 ,非堆顶元素不管 + + // 如果堆顶元素的下标不在当前窗口内,说明它已经滑出去了,弹出 + while (pq.peek()[1] <= i - k) { pq.poll(); } + + // 当前窗口的最大值就是堆顶元素的值 ans[i - k + 1] = pq.peek()[0]; } return ans; } - //单调队列 + //单调队列(推荐) /** * 维护一个单调队列。 在这个滑动窗口最大值的算法中,双端队列的两端各自承担不同的职责: @@ -64,26 +88,44 @@ public class MaxSlidingWindow { */ public int[] maxSlidingWindow(int[] nums, int k) { int n = nums.length; - Deque deque = new LinkedList<>(); + Deque deque = new LinkedList<>(); // 存放下标,维护一个单调递减队列(从队头到队尾) + + // 1. 初始化前 k 个元素的单调队列 for (int i = 0; i < k; ++i) { + // 从队尾开始移除比当前元素小的元素下标,它们不可能成为后续最大值 while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { deque.pollLast(); } + // 当前元素下标加入队尾 deque.offerLast(i); } + // 2. 初始化结果数组(总共 n - k + 1 个窗口) int[] ans = new int[n - k + 1]; + // 当前窗口的最大值就是队头所指的下标对应的元素 ans[0] = nums[deque.peekFirst()]; + + // 3. 开始滑动窗口:从第 k 个元素开始往右滑 for (int i = k; i < n; ++i) { - while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { //队尾元素本来就比队头小,如果新元素比队尾还大,那就没有保留的价值了 + + // 维护队列的单调性:移除所有比当前元素小的队尾元素 + while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { deque.pollLast(); } + + // 把当前元素下标加入队尾 deque.offerLast(i); + + // 如果队头的下标已经滑出窗口左边,就移除它 while (deque.peekFirst() <= i - k) { deque.pollFirst(); } + + // 队头就是当前窗口的最大值 ans[i - k + 1] = nums[deque.peekFirst()]; } + return ans; } + } diff --git a/src/main/java/substring/SubarraySum.java b/src/main/java/substring/SubarraySum.java index aeef557..cbde606 100644 --- a/src/main/java/substring/SubarraySum.java +++ b/src/main/java/substring/SubarraySum.java @@ -16,8 +16,26 @@ import java.util.HashMap; 输入:nums = [1,2,3], k = 3 输出:2 */ + + //只会暴力解 +//二刷也是暴力解 public class SubarraySum { + public int subarraySum2(int[] nums, int k) { + int[] prefix_sum=new int[nums.length+1]; + int cnt=0; + prefix_sum[0]=0; + for (int i = 1; i <= nums.length; i++) { + prefix_sum[i]=nums[i-1]+prefix_sum[i-1]; + } + for (int i = 0; i < nums.length; i++) { + for (int j = i+1; j <= nums.length; j++) { + if(prefix_sum[j]-prefix_sum[i]==k) + cnt++; + } + } + return cnt; + } //枚举 public int subarraySum(int[] nums, int k) { int count = 0; diff --git a/src/test/java/substring/SubarraySumTest.java b/src/test/java/substring/SubarraySumTest.java index 9150e12..28098ed 100644 --- a/src/test/java/substring/SubarraySumTest.java +++ b/src/test/java/substring/SubarraySumTest.java @@ -11,7 +11,7 @@ public class SubarraySumTest { int[] nums={1,1,1}; int k=2; SubarraySum solution = new SubarraySum(); - int cnt=solution.subarraySum(nums,k); + int cnt=solution.subarraySum2(nums,k); System.out.println(cnt); }