LeetCode刷题day20

简介: LeetCode刷题day20

今日刷题重点----滑动窗口法

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。


找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。


示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2

解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0


方法一:暴力法

使用两个for循环,依次判断寻找最小的子序列

参考代码1


//方法一:暴力解法
int minSubArrayLen(int target, vector<int>& nums) {
  int result = INT_MAX;
  int str_len = 0;//子序列长度
  int sum = 0;//子序列的和
  for(int i = 0; i < nums.size(); i++) {
    sum = 0;
    for(int j = i; j < nums.size(); j++) {
      sum+=nums[j];
      if(sum>=target) {
        str_len = j-i+1;
        result = result>=str_len?str_len :result ;
        break;//找到了则一定要break一下.因为需要的是最小长度.
      }
    }
  }
  result = result==INT_MAX ? 0 : result;//如果没有找到, 则result赋值为0
  return result;
}

方法二:滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。

算法步骤:

使用两个变量i,j分别表示窗口的两个边界.

当窗口内的值< target时,右边界j进行后移,直至>=target.此时就是以i为左边界的窗口,且满足条件—是最小的子序列.

然后i继续后移,寻找其他最小的窗口,对比判断选择更加符合题意的.

这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:

image.gif

参考代码2

//滑动窗口法 
int minSubArrayLen(int target, vector<int>& nums) {
  int result = INT_MAX;//结果 
  int subLength = 0;//滑动窗口的长度
  int sum = 0;//子序列的长度. 
  int i = 0;//滑动窗口的起始位置 
  for(int j = 0;j < nums.size();j++){
    sum+=nums[j];//如果不满足条件,则不断增大窗口的右边界. 
    while(sum>=target){
      subLength = j-i+1;
      result = result>=subLength ? subLength : result;
      sum -=nums[i++];//如果满足,则窗口右移动,即起始位置后移. 
    }
  } 
  result = result==INT_MAX ? 0 : result;//如果没有找到, 则result赋值为0
  return result;
}

76. 最小覆盖子串


给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。


注意:


对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

如果 s 中存在这样的子串,我们保证它是唯一的答案。


示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"


示例 2:

输入:s = "a", t = "a"
输出:"a"


示例 3:

输入: s = "a", t = "aa"
输出: ""


解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,

因此没有符合条件的子字符串,返回空字符串。


方法一:滑动窗口法


滑动窗口的思想:

用i,j表示滑动窗口的左边界和右边界,通过改变i,j来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度j-i+1,这些长度中的最小值就是要求的结果。


1.步骤一

不断增加j使滑动窗口增大,直到窗口包含了T的所有元素


2.步骤二

不断增加i使滑动窗口缩小,因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素,这个时候不能再扔了,再扔就不满足条件了,记录此时滑动窗口的长度,并保存最小值


3.步骤三

让i再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从步骤一开始执行,寻找新的满足条件的滑动窗口,如此反复,直到j超出了字符串S范围。


面临的问题:

如何判断滑动窗口包含了T的所有元素???

我们用一个vector need / map来表示当前滑动窗口中需要的各元素的数量,一开始滑动窗口为空,用T中各元素来初始化这个need,当滑动窗口扩展或者收缩的时候,去维护这个need,例如当滑动窗口包含某个元素,我们就让need中这个元素的数量减1,代表所需元素减少了1个;当滑动窗口移除某个元素,就让need中这个元素的数量加1。

记住一点:need始终记录着当前滑动窗口下,我们还需要的元素数量,我们在改变i,j时,需同步维护need。

值得注意的是,只要某个元素包含在滑动窗口中,我们就会在need中存储这个元素的数量,如果某个元素存储的是负数代表这个元素是多余的。比如当need等于{‘A’:-2,‘C’:1}时,表示当前滑动窗口中,我们有2个A是多余的,同时还需要1个C。这么做的目的就是为了步骤二中,排除不必要的元素,数量为负的就是不必要的元素,而数量为0表示刚刚好。

回到问题中来,那么如何判断滑动窗口包含了T的所有元素?

结论就是当need中所有元素的数量都小于等于0时,表示当前滑动窗口不再需要任何元素。


优化

如果每次判断滑动窗口是否包含了T的所有元素,都去遍历need看是否所有元素数量都小于等于0,这个会耗费O(k)的时间复杂度,k代表need长度,最坏情况下,k可能等于len(S)。

其实这个是可以避免的,我们可以维护一个额外的 变量needCnt来记录所需元素的总数量,当我们碰到一个所需元素c,不仅need[c]的数量减少1,同时needCnt也要减少1,这样我们通过needCnt就可以知道是否满足条件,而无需遍历整个数组了。


前面也提到过,need记录了遍历到的所有元素,而只有need[c]>0大于0时,代表c就是所需元素


参考代码

string minWindow(string s, string t) {
  vector<int> need(128,0);//因为是字符,所以都可以找到一个数字进行对应,故可以使用vector类型.同样ordered_map也可以 
  //ordered_map<char,int> m; 
  int cnt = 0;
  for(char c : t){//对need进行初始化 
    need[c]++;
  }
  cnt = t.size();
  int i ,j,result = INT_MAX,str_len,start=0;//start记录满足条件的窗口的起始位置. 
  for(i = 0,j = 0;j < s.size();j++){
    char c = s[j];
    if(need[c]>0){
      cnt--;//如果当前的字符是t需要的字符并且还没有凑够,则 cnt-- 
    }
    need[c]--;//将右边的字符加入窗口
    if(cnt==0){//窗口已经包含所需要的全部字符 
      while(i<j&&need[s[i]]<0){//排除不必要的字符 
        need[s[i++]]++;
      } 
      str_len = j-i+1;
      if(str_len<result){//更新result和start 
        result = str_len;
        start = i;
      }
      need[s[i++]]++; //左边界进行右移动,寻找下一个窗口..
      cnt++;//需要的目标字符进行增加,因为之前那个是最小的.
    } 
  }
  return result==INT_MAX>"":s.substr(start,result); 
}


904. 水果成篮


由于本题翻译不准确,我们直接看下英文题目,英文直译如下:

您正在参观一个农场,该农场有一排从左到右排列的果树。

树由整数数组fruits表示,其中水果[i]是第i棵树产生的水果类型。

你想收集尽可能多的水果。但是,所有者有一些严格的规则,您必须遵守:


你只有两个篮子,每个篮子只能装一种水果。每篮水果的数量没有限制。

从您选择的任何一棵树开始,您必须在向右移动时从每棵树(包括起始树)中恰好摘下一个水果,摘下的水果必须放在你的一个篮子里。 一旦你到了一棵树上,树上的果实没法放入你的篮子(两个篮子已经满了),你必须停下来。

给定整数数组水果,返回可以拾取的最大水果数。


本题,其实就是 选只有两个元素的最长连续子序列,比如1,2,3,2,2最长就是2,3,2,2(只包括2或者3,而且是最长的)。


示例 1:

输入:[1,2,1]
输出:3

解释:我们可以收集 [1,2,1]。

示例 2:

输入:[0,1,2,2]
输出:3

解释:我们可以收集 [1,2,2]

如果我们从第一棵树开始,我们将只能收集到 [0, 1]。

示例 3:

输入:[1,2,3,2,2]
输出:4

解释:我们可以收集 [2,3,2,2]

如果我们从第一棵树开始,我们将只能收集到 [1, 2]。

示例 4:

输入:[3,3,3,1,2,1,1,2,3,3,4]
输出:5

解释:我们可以收集 [1,2,1,1,2]

如果我们从第一棵树或第八棵树开始,我们将只能收集到 4 棵水果树。

思路分析

  • 使用i,j分别指向窗口的左右两端.
  • 窗口的右边界开始后移直到窗口内元素的个数大于2,
  • 然后左窗口进行右移动,直到窗口内只有两种元素.
  • 然后更新最大的水果树.最终进行返回.

参考代码

//滑动窗口法 
int totalFruit(vector<int>& fruits) {
  unordered_map<int,int> window;
  int k = 2;
  int result = 0;
  int i = 0;//窗口左边界 
  for(int j = 0;j < fruits.size();j++){ // j:窗口右边界 
    window[fruits[j]]++;//窗口右边界扩大
    while(window.size()>k){//如果当前窗口中元素类型的个数大于k ,则想办法减小到k
      window[fruits[i]]--;
      if(!window[fruits[i]]) {//如果当前元素的个数变为了0,则从窗口中移除该元素. 
        window.erase(fruits[i]);
      }
      i++;//每一次while,窗口右移动 
    } 
    result = max(result,j-i+1); //更新result 
  }
  return result;
}
相关文章
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
57 6
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
52 4
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
118 2
|
26天前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
21 1
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
3月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
61 7
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
54 5
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
28 4
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
Leetcode题目"剑指 Offer 21. 调整数组顺序使奇数位于偶数前面"的两种Python解决方案,一种是使用双端队列调整数组顺序,另一种是使用双指针法将奇数移到数组前半部分,偶数移到后半部分。
28 4