力扣75——滑动窗口

总结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)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小欣CZX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值