个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创滑动窗口(3)_最大连续1的数组个数III
收录于专栏【经典算法练习】
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌
目录
1. 题目链接:
2. 题目描述 :
3. 解法 :
解法一(暴力枚举) :
算法思路 :
具体步骤 :
代码展示 :
结果分析 :
对暴力算法的反思与优化
解法二(滑动窗口) :
算法思路 :
图解流程 :
代码展示 :
结果分析 :
1. 题目链接:
OJ链接: 最大连续1的个数III
2. 题目描述 :
给定一个二进制数组 nums
和一个整数 k
,如果可以翻转最多 k
个 0
,则返回 数组中连续 1
的最大个数 。
示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2 输出:6 解释:[1,1,1,0,0,1,1,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3 输出:10 解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 10。
提示:
1 <= nums.length <= 105
nums[i]
不是0
就是1
0 <= k <= nums.length
3. 解法 :
解法一(暴力枚举) :
算法思路 :
这道题将0转换成1的操作实现起来非常不方便,对于暴力算法来说,每次转换完后,下次一又得恢复原样,那我们可不可以不要转换,直接通过判断0的个数不超过k来找到最长子数组呢?
通过上面的分析,我们就可以把题目转换为 : 找出最长的子数组,0的个数不超过k个
那么我们就可以使用暴力枚举,在使用一个变量记录0的个数,找出数组中所有可能的子数组,然后用max取最大值,即题目所需要的最长的子数组.
具体步骤 :
- i,j两层循环遍历整个数组
- 用tmp记录遍历时0的个数
- 当tmp > k时当前遍历完成
- i++,j = i进入下一轮遍历
代码展示 :
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size(), ret = 0, tmp = 0;
for(int i = 0; i < n; i++)
{
for(int j = i; j < n; j++)
{
if(nums[j] == 0) tmp++;
if(tmp > k) break;
ret = max(ret, j -i + 1);
}
tmp = 0;
}
return ret;
}
};
结果分析 :
这道题比上到题狠,这道题数组中数据的个数在10^5之内,我们暴力算法的时间复杂度为O(N^2),总的数据级别在10^10之上,计算机无法的1s内完成,所以判定超时!!!
对暴力算法的反思与优化
当我们的暴力循环到这种情况时,会让i++,j=i,重新下一轮循环
但是我们发现,i后面的 1 1 0都会在j这个位置截停,无需多余的遍历
所以我们就可以使用滑动窗口,用left和right框住一个动态区间,内面的0的个数小于K
解法二(滑动窗口) :
算法思路 :
借用暴力算法的思想,不要去想怎么翻转,不要把问题想的很复杂,这道题的结果无非是一段连续的1中间塞了k个0嘛~
因此,我们可以把问题转化成: 求数组中一段最长的连续区间,要求这段区间内0的个数不超过k个
既然是连续区间,可以考虑使用[滑动窗口]来解决问题.
图解流程 :
代码展示 :
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int left = 0, right = 0, ret = 0, tmp = 0;
while(right < nums.size())
{
if(nums[right] == 0) tmp++;
while(tmp > k)
if(nums[left++] == 0) tmp--;
ret = max(ret, right - left + 1);
right++;
}
return ret;
}
};
结果分析 :
时间复杂度: O(N)
空间复杂度: O(1)
总的来说,用滑动窗口去解决这道题是非常高效的一种方法!