力扣爆刷第1567之TOP100五连刷51-55(滑动窗口、零钱兑换、最小覆盖子串)
文章目录
- 力扣爆刷第1567之TOP100五连刷51-55(滑动窗口、零钱兑换、最小覆盖子串)
- 一、239. 滑动窗口最大值
- 二、41. 缺失的第一个正数
- 三、LCR 140. 训练计划 II
- 四、322. 零钱兑换
- 五、76. 最小覆盖子串
一、239. 滑动窗口最大值
题目链接:https://leetcode.cn/problems/sliding-window-maximum/description/
思路:求滑动窗口最大值,需要维护一个队列,队列大小就是窗口大小,要能保证每次都可以获取最大值,只需要让入队时弹出小于当前元素的元素,这样可以保证队列一段添加元素,从另一端获取最大值。
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {int[] res = new int[nums.length - k + 1];MyQueue queue = new MyQueue();int j = 0;for(int i = 0; i < nums.length; i++) {queue.add(nums[i]);if(i + 1 >= k) {res[j++] = queue.getMax();queue.remove(nums[i-k+1]);}}return res;}}class MyQueue {LinkedList<Integer> queue = new LinkedList<>();public MyQueue() {}void add(int v) {while(!queue.isEmpty() && v > queue.peekLast()) queue.pollLast();queue.addLast(v);}void remove(int v) {if(!queue.isEmpty() && queue.peekFirst() == v) queue.pollFirst();}int getMax() {return queue.peekFirst();}
}
二、41. 缺失的第一个正数
题目链接:https://leetcode.cn/problems/first-missing-positive/description/
思路:求缺失的第一个正数,要求时间复杂度o(n),本身不能排序,但是可以借助数组的索引来做一个映射关系,对于如何求缺失的第一个正数,对于固定长度的数组来说,如果数组中的元素值存在超出数组长度的情况,那么数组中就一定在[1, len]不是连续的,那么我们只需要把超出范围的给排除掉,然后把位于范围内的与数组的索引进行映射,但凡能映射上的,也给进行区别标识,比如设置为负数,然后再从头到尾遍历,第一个大于0的元素所对应的索引就是缺失的第一个正数。
class Solution {public int firstMissingPositive(int[] nums) {int k = nums.length+1;for(int i = 0; i < nums.length; i++) {if(nums[i] <= 0) nums[i] = k;}for(int i = 0; i < nums.length; i++) {int t = Math.abs(nums[i]);if(t < k) {nums[t-1] = -Math.abs(nums[t-1]);}}for(int i = 0; i < nums.length; i++) {if(nums[i] > 0) return i+1;}return k;}
}
三、LCR 140. 训练计划 II
题目链接:https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/description/
思路:本题为求倒数第N个节点,很简单,经典快慢指针,快指针先走N步,然后快慢同步走,当快指针抵达结尾时,慢指针指向的就是倒数第N个节点。
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode trainingPlan(ListNode head, int cnt) {ListNode slow = head, fast = head;while(cnt > 0) {fast = fast.next;cnt--;}while(fast != null) {slow = slow.next;fast = fast.next;}return slow;}
}
四、322. 零钱兑换
题目链接:https://leetcode.cn/problems/coin-change/
思路:本题是完全背包,求装满背包所需物品的最少数量,完全背包不求排列组合数时是不讲究物品和背包的内外顺序的,求最小数量,定义dp[j]表示装满容量为j的背包所需的最少物品数量,那么对于每一个物品nums[i]来说,将其放入后,填满背包的最少数量为dp[j] = min(dp[j], dp[j - nums[i]] + 1),但要注意依赖的前一个位置,是否填充了物品。
**关于背包问题的小总结:**
01背包:先遍历物品后遍历背包,物品正序,背包逆序。正常背包装物品为dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i])。求排列组合数为:p[j] += dp[j - nums[i]]。
完全背包:完全背包正常装物品不讲究物品和背包的顺序,求组合数:物品在外背包在内。求排列数:背包在外,物品在内。
class Solution {public int coinChange(int[] coins, int amount) {int[] dp = new int[amount+1];Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;for(int i = 0; i < coins.length; i++) {for(int j = coins[i]; j <= amount; j++) {if(dp[j - coins[i]] == Integer.MAX_VALUE) continue;dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);}}return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];}
}
五、76. 最小覆盖子串
题目链接:https://leetcode.cn/problems/minimum-window-substring/description/
思路:求一个字符串在另一个字符串中的最小覆盖子串,也是非常经典的题目,典型的使用滑动窗口的题,先用一个needmap记录下来所需的元素个数,然后维护一个滑动窗口,该窗口也有windowmap,然后一直扩大窗口,直到size相同开始缩小窗口并记录下来最小窗口即可。
class Solution {public String minWindow(String s, String t) {Map<Character, Integer> need = new HashMap<>();Map<Character, Integer> window = new HashMap<>();for(int i = 0; i < t.length(); i++) {need.put(t.charAt(i), need.getOrDefault(t.charAt(i), 0) + 1);}int valid = 0, min = Integer.MAX_VALUE, init = 0;int left = 0, right = 0;while(right < s.length()) {char rc = s.charAt(right++);if(need.containsKey(rc)) {window.put(rc, window.getOrDefault(rc, 0)+1);if(window.get(rc).equals(need.get(rc))) valid++;}while(valid == need.size()) {if(right - left < min) {min = right - left;init = left;}char lc = s.charAt(left++);if(need.containsKey(lc)) {if(need.get(lc).equals(window.get(lc))) valid--;window.put(lc, window.get(lc)-1);}}}return min == Integer.MAX_VALUE ? "" : s.substring(init, init+min);}
}