携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情 >>
每日三刷,剑指千题
计划简介:
- 每日三题,以中等题为主,简单题为辅进行搭配。保证质量题1道,数量题3道。
- 每日早通勤在LeetCode手机端选题,思考思路,没答案的直接看题解。
- 每日中午进行编码,时间控制在一小时之内。
- 下班前半小时进行整理总结,并发布到掘金每日更文活动。
说明:
- 基于以前的刷题基础,本次计划以中等题为主,大部分中等题都可以拆分为多个简单题,所以数量保证3,质量保证一道中等题即可。
- 刷题顺序按照先刷链表、二叉树、栈、堆、队列等基本数据结构,再刷递归、二分法、排序、双指针等基础算法,最后是动态规划、贪心、回溯、搜索等复杂算法。
- 刷题过程中整理相似题型,刷题模板。
- 目前进度 139/1000 。
[713]乘积小于 K 的子数组
给你一个整数数组 nums
和一个整数 k
,请你返回子数组内所有元素的乘积严格小于 k
的连续子数组的数目。
示例 1:
输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。
解析
滑动窗口经典题,附滑动窗口模板:
滑动窗口 + 变量计数模板:
class Solution {
public int slidingWindow(int[] nums, int k) {
//数组/字符串长度
int n = nums.length;
//双指针,表示当前遍历的区间[left, right],闭区间
int left = 0, right = 0;
//定义变量统计 子数组/子区间 是否有效
int sum = 0;
//定义变量动态保存最大 求和/计数
int res = 0;
//右指针遍历到数组尾
while (right < n) {
//增加当前右指针对应的数值
sum += nums[right];
//当在该区间内 sum 超出定义范围
while (sum > k) {
//先将左指针指向的数值减去
sum -= nums[left];
//左指针右移
left++;
}
//到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
res = Math.max(res, right - left + 1);
//移动右指针,去探索下一个区间
right++;
}
return res;
}
}
滑动窗口 + 哈希表存储模板:
class Solution {
public String slidingWindow(String s, String t) {
//创建两个哈希表,分别记录 [需要的] 和 [加入的]
Map<Character, Integer> need = new HashMap<>();
Map<Character, Integer> map = new HashMap<>();
//创建 [双指针] 和 [有效数量]
int left = 0, right = 0;
int valid = 0;
//外层循环,供右指针遍历
while(right < s.length()){
//创建临时 c 字符,是移入 窗口 内的字符
char c = s.charAt(right);
//进行窗口一系列逻辑更新
...
//判断左指针是否要右移即窗口收缩:有效数量足够满足条件
/* 可能是规定的窗口大小超出了,可能是有效值数量达成了
1. while(valid == need.size())
2. while(right - left + 1 >= s1.length())
*/
while(windows need shrink){
// 创建 d 是要移除窗口的字符
char d = s.charAt(left);
left++;
//进行窗口一系列逻辑更新
...
}
//右指针右移
right++;
}
}
}
Code
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) {
return 0;
}
int n = nums.length;
int left = 0, right = 0;
int ans = 0;
int temp = 1;
while (right < n) {
temp *= nums[right];
while (temp >= k) {
// 重构窗口
temp /= nums[left];
left++;
}
// 计算该窗口内以left开头的子数组有多少
ans += right - left + 1;
right++; // 扩大窗口
}
return ans;
}
}
[1984]学生分数的最小差值
给你一个 下标从 0 开始 的整数数组 nums
,其中 nums[i]
表示第 i
名学生的分数。另给你一个整数 k
。
从数组中选出任意 k
名学生的分数,使这 k
个分数间 最高分 和 最低分 的 差值 达到 最小化 。
返回可能的 最小差值 。
示例 1:
输入:nums = [90], k = 1
输出:0
解释:选出 1 名学生的分数,仅有 1 种方法:
- [90] 最高分和最低分之间的差值是 90 - 90 = 0
可能的最小差值是 0
解析
关键得出排序后结果一定是连续的,不连续就无法用滑动窗口。
Code
class Solution {
public int minimumDifference(int[] nums, int k) {
Arrays.sort(nums);
int ans = nums[k - 1] - nums[0];
for (int i = k; i < nums.length; i++) {
ans = Math.min(nums[i] - nums[i - k + 1], ans);
}
return ans;
}
}
[219]存在重复元素 II
给你一个整数数组 nums
和一个整数 k
,判断数组中是否存在两个 不同的索引 i
和 j
,满足 nums[i] == nums[j]
且 abs(i - j) <= k
。如果存在,返回 true
;否则,返回 false
。
示例 1:
输入:nums = [1,2,3,1], k = 3
输出:true
解析
套需要记录模板。
Code
class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
// 窗口最大长度为 k
int n = nums.length;
int left = 0, right = 0;
Set<Integer> set = new HashSet<>();
while (right < n) {
while (right - left > k) {
set.remove(nums[left]);
left++;
}
if (!set.add(nums[right])) {
return true;
}
right++;
}
return false;
}
}
\