力扣爆刷第1567之TOP100五连刷51-55(滑动窗口、零钱兑换、最小覆盖子串)
文章目录
一、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);
}
}