6.12 二刷hot100 滑动窗口+子串

This commit is contained in:
zhangsan 2025-06-12 10:42:31 +08:00
parent d3cfe89d74
commit 97564746ff
5 changed files with 74 additions and 14 deletions

View File

@ -28,6 +28,7 @@ import java.util.List;
*/ */
//没做出来 //没做出来
//二刷会做
public class FindAnagrams { public class FindAnagrams {
public List<Integer> findAnagrams(String s, String p) { public List<Integer> findAnagrams(String s, String p) {
List<Integer> result = new ArrayList<>(); List<Integer> result = new ArrayList<>();

View File

@ -1,6 +1,5 @@
package slidingwindow; package slidingwindow;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
/** /**
@ -23,7 +22,7 @@ import java.util.HashSet;
解释: 因为无重复字符的最长子串是 "wke"所以其长度为 3 解释: 因为无重复字符的最长子串是 "wke"所以其长度为 3
请注意你的答案必须是 子串 的长度"pwke" 是一个子序列不是子串 请注意你的答案必须是 子串 的长度"pwke" 是一个子序列不是子串
*/ */
//二刷会做
public class LengthOfLongestSubstring { public class LengthOfLongestSubstring {
public int lengthOfLongestSubstring(String s) { public int lengthOfLongestSubstring(String s) {
int left=0,right=0,max_length=0; int left=0,right=0,max_length=0;

View File

@ -28,31 +28,55 @@ import java.util.*;
输出[1] 输出[1]
*/ */
//普通方法超时 需重做 //普通方法超时 需重做
//二刷也没想到低复杂度方法
public class MaxSlidingWindow { public class MaxSlidingWindow {
//优先队列 /**
* 思路说明用优先队列大顶堆维护滑动窗口中的最大值
* 滑动窗口长度为 k我们每次都想拿到窗口内的最大值
* PriorityQueue<int[]> 维护一个大顶堆
* 存入的是一个数组 new int[]{, 下标}
* 排序规则按值降序值相同时按下标降序保证堆顶是最大值的最新位置
* 每加入一个新元素我们就把它压入堆
* 然后检查堆顶如果它的下标已经滑出窗口左边 i - k就移除它
* 每次窗口滑完一个位置后堆顶就是当前窗口最大值
* @param nums
* @param k
* @return
*/
public int[] maxSlidingWindow1(int[] nums, int k) { public int[] maxSlidingWindow1(int[] nums, int k) {
int n = nums.length; int n = nums.length;
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) { // 优先队列大顶堆按值降序如果值相同则按下标降序保证更靠右的元素在上面
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1]; PriorityQueue<int[]> 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) { for (int i = 0; i < k; ++i) {
pq.offer(new int[]{nums[i], i}); pq.offer(new int[]{nums[i], i});
} }
// 初始化结果数组 n - k + 1 个窗口
int[] ans = new int[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) { for (int i = k; i < n; ++i) {
// 把新元素加入窗口
pq.offer(new int[]{nums[i], i}); pq.offer(new int[]{nums[i], i});
while (pq.peek()[1] <= i - k) { //如果堆顶元素不在滑动窗口内部就移除 非堆顶元素不管
// 如果堆顶元素的下标不在当前窗口内说明它已经滑出去了弹出
while (pq.peek()[1] <= i - k) {
pq.poll(); pq.poll();
} }
// 当前窗口的最大值就是堆顶元素的值
ans[i - k + 1] = pq.peek()[0]; ans[i - k + 1] = pq.peek()[0];
} }
return ans; return ans;
} }
//单调队列
//单调队列(推荐
/** /**
* 维护一个单调队列 * 维护一个单调队列
在这个滑动窗口最大值的算法中双端队列的两端各自承担不同的职责 在这个滑动窗口最大值的算法中双端队列的两端各自承担不同的职责
@ -64,26 +88,44 @@ public class MaxSlidingWindow {
*/ */
public int[] maxSlidingWindow(int[] nums, int k) { public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length; int n = nums.length;
Deque<Integer> deque = new LinkedList<>(); Deque<Integer> deque = new LinkedList<>(); // 存放下标维护一个单调递减队列从队头到队尾
// 1. 初始化前 k 个元素的单调队列
for (int i = 0; i < k; ++i) { for (int i = 0; i < k; ++i) {
// 从队尾开始移除比当前元素小的元素下标它们不可能成为后续最大值
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast(); deque.pollLast();
} }
// 当前元素下标加入队尾
deque.offerLast(i); deque.offerLast(i);
} }
// 2. 初始化结果数组总共 n - k + 1 个窗口
int[] ans = new int[n - k + 1]; int[] ans = new int[n - k + 1];
// 当前窗口的最大值就是队头所指的下标对应的元素
ans[0] = nums[deque.peekFirst()]; ans[0] = nums[deque.peekFirst()];
// 3. 开始滑动窗口从第 k 个元素开始往右滑
for (int i = k; i < n; ++i) { 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.pollLast();
} }
// 把当前元素下标加入队尾
deque.offerLast(i); deque.offerLast(i);
// 如果队头的下标已经滑出窗口左边就移除它
while (deque.peekFirst() <= i - k) { while (deque.peekFirst() <= i - k) {
deque.pollFirst(); deque.pollFirst();
} }
// 队头就是当前窗口的最大值
ans[i - k + 1] = nums[deque.peekFirst()]; ans[i - k + 1] = nums[deque.peekFirst()];
} }
return ans; return ans;
} }
} }

View File

@ -16,8 +16,26 @@ import java.util.HashMap;
输入nums = [1,2,3], k = 3 输入nums = [1,2,3], k = 3
输出2 输出2
*/ */
//只会暴力解 //只会暴力解
//二刷也是暴力解
public class SubarraySum { 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) { public int subarraySum(int[] nums, int k) {
int count = 0; int count = 0;

View File

@ -11,7 +11,7 @@ public class SubarraySumTest {
int[] nums={1,1,1}; int[] nums={1,1,1};
int k=2; int k=2;
SubarraySum solution = new SubarraySum(); SubarraySum solution = new SubarraySum();
int cnt=solution.subarraySum(nums,k); int cnt=solution.subarraySum2(nums,k);
System.out.println(cnt); System.out.println(cnt);
} }