总结leetcode75中滑动窗口的算法题解题思路。
上一篇:力扣75——双指针
以下代码大部分为本人所写,少部分为官方示例代码。
1 子数组最大平均数 I
题目:
给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。
请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。
任何误差小于 10-5 的答案都将被视为正确答案。
题解:
这道题没什么难度,滑动窗口,然后看看当前窗口值的和是否更大,是则更新result,最后的返回值是result/k
。
class Solution {
public:
double findMaxAverage(vector<int>& nums, int k) {
double result = 0,tmp=0;
int rlen = 0;
for (int i = 0; i < nums.size(); ++i) {
if (rlen < k) {
rlen++;
tmp += nums[i];
result = tmp;
}
else {
tmp -= nums[i - k];
tmp += nums[i];
if (result < tmp) result = tmp;
}
}
return result / k;
}
};
2 定长子串中元音的最大数目
题目:
给你字符串 s 和整数 k 。
请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。
英文中的 元音字母 为(a, e, i, o, u)。
题解:解题方法和题1基本一样。
class Solution {
public:
int maxVowels(string s, int k) {
unordered_set<char> vSet= { 'a','e','i','o','u' };
int rlen = 0, result = 0, tmp = 0;
for (int i = 0; i < (int)s.size(); ++i) {
if (rlen < k) {
rlen++;
if (vSet.count(s[i])) {
tmp++;
result++;
}
}
else {
if (vSet.count(s[i])) {
tmp++;
}
if (vSet.count(s[i-k])) {
tmp--;
}
if (result < tmp) result = tmp;
}
}
return result;
}
};
3 最大连续1的个数 III
题目:
给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。
题解1:
题目设定可以翻转k个0,然后找出最长的连续1长度,
所以可以转化为找出哪些是0,
然后用vector类型的P计算出前缀0的个数(P[i+1]
为0~i中0的个数),
然后遍历每一个P[right],以其为右端点,找到最小的左端点left,使得 P[right]-P[left]<=k
,
结果:(right-1)-(left-1)
。
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size();
vector<int> P(n + 1);
for (int i = 1; i <= n; ++i) {
P[i] = P[i - 1] + (1 - nums[i - 1]);
}
int ans = 0;
for (int right = 1; right < n + 1; ++right) {
int left = lower_bound(P.begin(), P.end(), P[right] - k) - P.begin();
ans = max(ans, (right-1) - (left-1));
}
return ans;
}
};
题解2:解题1的优化,不需要额外占用一个vector。
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size();
int left = 0, lsum = 0, rsum = 0;
int ans = 0;
for (int right = 0; right < n; ++right) {
rsum += 1 - nums[right];
while (lsum < rsum - k) {
lsum += 1 - nums[left];
++left;
}
ans = max(ans, right - left + 1);
}
return ans;
}
};
4 删掉一个元素以后全为 1 的最长子数组
题目:
给你一个二进制数组 nums ,你需要从中删掉一个元素。
请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。
如果不存在这样的子数组,请返回 0 。
题解1:维护2个vector:pre和suf。其中pre[i]表示以第 i 位结尾的最长连续全 1 子数组长度,suf[i]表示以第 i 位开头的最长连续全 1 子数组长度。
class Solution {
public:
int longestSubarray(vector<int>& nums) {
int n = nums.size();
vector<int> pre(n), suf(n);
pre[0] = nums[0];
for (int i = 1; i < n; ++i) {
pre[i] = nums[i] ? pre[i - 1] + 1 : 0;
}
suf[n - 1] = nums[n - 1];
for (int i = n - 2; i >= 0; --i) {
suf[i] = nums[i] ? suf[i + 1] + 1 : 0;
}
int ans = 0;
for (int i = 0; i < n; ++i) {
int preSum = i == 0 ? 0 : pre[i - 1];
int sufSum = i == n - 1 ? 0 : suf[i + 1];
ans = max(ans, preSum + sufSum);
}
return ans;
}
};
题解2:对题解1的优化。
保留pre,并命名为p0,
定义vector类型的p1,p1[i]表示「以第 i 位结尾,并且可以在某个位置删除一个 0 的最长连续全 1 子数组」。之所以定义这个,是因为删除1时得到的长度不可能是最大长度。
当nums[i]==0时,p1[i] = p0[i-1],因为此时可以删除nums[i],然后p1[i]就变成了以第i-1位结尾的最长连续全 1 子数组长度。
当nums[i]==1时,p1[i] = p1[i-1]+1。
最后的返回值就是p1中的最大值。
由于任意的p0[i] 和p1[i]都只和 p0[i-1] 和p1[i-1]有关,所以可以将vector类型改为int类型,减少空间复杂度。
(特殊情况:如果nums中全是1,则返回值是p1-1)。
class Solution {
public:
int longestSubarray(vector<int>& nums) {
int ans = 0;
int p0 = 0, p1 = 0;
for (int num: nums) {
if (num == 0) {
p1 = p0;
p0 = 0;
}
else {
++p0;
++p1;
}
ans = max(ans, p1);
}
if (ans == nums.size()) {
--ans;
}
return ans;
}
};
1-4 解题总结
题目特点:固定窗口长度求解某结果;根据条件求解符合的窗口。
解题方法1:固定窗口长度的题比较简单,基本上遍历一遍就好了。
解题方法2:对于求解窗口的,一般需要动态求解,如双指针(题3),递推(题4)。