文章目录
题目解析
题目链接:904.水果成篮
转化问题:寻找最长的一段只有两种水果的连续的子数组,并返回它的长度。
算法原理
(PS:这里先想暴力枚举是因为刚开始我们并不知道这道题用什么算法,所以滑动窗口的解法是从暴力枚举的基础上优化出来的)
- 解法一的暴力枚举就是借用哈希表来枚举每一个符合条件的子数组。
- 解法二的滑动窗口优化了right不必每次枚举完再回到left的情况,这里要注意的是我们的hash表需要记录水果的种类和数量,让left出窗口的时候可以知道该移动多少次。
- 最后就是对代码的进一步优化时间复杂度,在滑动窗口的解法中,要多次对hash表插入删除,虽然都是O(1)的时间复杂度,但是如果我们用数组来代替哈希表,借用的是哈希表的思想,可以将时间复杂度优化不少。
代码实现
解法一:暴力枚举+哈希表(超时)
class Solution {
public:
int totalFruit(vector<int>& f) {
unordered_map<int,int> hash;//记录水果的种类和数量
int n = f.size(),ret = 0;//ret记录结果
for(int left = 0;left < n;left++)
{
for(int right = left;right < n;right++)
{
hash[f[right]]++;
if(hash.size() > 2)break;
ret = max(ret,right - left + 1);
}
while(hash.size() > 2)
{
hash[f[left]]--;
if(hash[f[left]] == 0)hash.erase(f[left]);
}
}
return ret;
}
};
解法二:滑动窗口+哈希表(时间复杂度为O(N),空间复杂度为O(1),哈希表中最多会有三个键值对,可以看成使用了常数级别的空间)
class Solution {
public:
int totalFruit(vector<int>& f) {
//滑动窗口
unordered_map<int,int> hash;//记录水果的种类和数量
int ret = 0;//ret记录结果
for(int left = 0,right = 0;right < f.size();right++)//1.left=0,right=0;
{
hash[f[right]]++;//2.进窗口
while(hash.size() > 2)//3.判断
{
//4.出窗口
hash[f[left]]--;
if(hash[f[left]] == 0)hash.erase(f[left]);
left++;
}
ret = max(ret,right - left + 1);//5.更新结果
}
return ret;
}
};
代码优化(时间复杂度为O(N),空间复杂度为O(1))
class Solution {
public:
int totalFruit(vector<int>& f) {
//滑动窗口
int hash[100001] = { 0 };//记录水果的数量
int ret = 0;//ret记录结果
for(int left = 0,right = 0,kinds = 0;right < f.size();right++)//1.left=0,right=0;
{
if(hash[f[right]] == 0)kinds++;//维护水果的种类
hash[f[right]]++;//2.进窗口
while(kinds > 2)//3.判断
{
//4.出窗口
hash[f[left]]--;
if(hash[f[left]] == 0)kinds--;
left++;
}
ret = max(ret,right - left + 1);//5.更新结果
}
return ret;
}
};