当前位置:   article > 正文

LeetCode刷题之——滑动窗口

LeetCode刷题之——滑动窗口

滑动窗口三步走:

第一步确定尾指针++的条件,往右扩张;第二步确定头指针++的条件,往右收缩,第三步更新所求目标值(一般都是极值)

滑动窗口伪代码

  1. start = 0
  2. end = 0 //初始化
  3. while(扩张条件)
  4. {
  5. while(收缩条件)
  6. {
  7. FindTarget()//收缩时一般求极小值
  8. start++//收缩,有可能是++,也有可能是跳跃式移动
  9. }
  10. FindTarget()//扩张时一般求极大值
  11. end++//扩张,一般都是++
  12. }
  13. if(一次都没进入过收缩条件)
  14. {
  15. 特殊处理一下
  16. }

目前来看,滑动窗口主要有两种题型,一种是找最长/最短子数组,这种使用上述的三步走策略。另一种是固定长度的窗口内的极值,此时需要借助单调队列

题目一:209. 长度最小的子数组

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

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

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

思路:

  1. for(遍历整个字符串)
  2. {
  3. for(和值大于target)
  4. {
  5. 计算一次最短长度
  6. 头指针右移收缩
  7. }
  8. 尾指针右移扩张
  9. }
  10. 没收缩过需要特殊处理

代码:

  1. int minSubArrayLen(int target, int* nums, int numsSize)
  2. {
  3. int start = 0, end = 0;
  4. int sum = 0, minlen = INT_MAX, tmp = 0;
  5. while (end < numsSize)
  6. {
  7. sum += nums[end];
  8. while (sum >= target)
  9. {
  10. tmp = end - start + 1;
  11. if (minlen > tmp)
  12. {
  13. minlen = tmp;
  14. }
  15. sum -= nums[start];
  16. start++;
  17. }
  18. end++;
  19. }
  20. return minlen == INT_MAX ? 0 : minlen;
  21. }

题目二:3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

思路:

  1. for(遍历字符串)
  2. {
  3. for(出现了重复字符)
  4. {
  5. 头指针右移內缩,右移到重复字符后面一位的地方,保证新的串口内没有重复字符
  6. }
  7. 尾指针右移扩张
  8. 每次扩张计算一次最大距离
  9. }

代码:

  1. int lengthOfLongestSubstring(char * s){
  2. int start = 0, end = 0, len = 0, output = 0;
  3. int chs[128] = {0};
  4. if (0 == strlen(s) || NULL == s)
  5. {
  6. return 0;
  7. }
  8. for (end = 0; s[end] != '\0'; end++)
  9. {
  10. //难点1:chs[s[end]]内保存的是目标字符最近一次出现的位置,方便头指针右移找位置
  11. if (chs[s[end]] > start)
  12. {
  13. //难点1相关处理
  14. start = chs[s[end]];
  15. //目标是求最长距离,它更可能出现在扩张的过程中
  16. //本题收缩时计算和扩张时计算完全重复,则可以省略
  17. //len = end - start + 1;
  18. //output = output < len ? len : output;
  19. }
  20. //难点1相关处理
  21. chs[s[end]] = end + 1;
  22. len = end - start + 1;
  23. output = output < len ? len : output;
  24. }
  25. return output;
  26. }

题目三:76. 最小覆盖子串

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

示例 1:

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

思路:

  1. for(遍历整个字符串)
  2. {
  3. for(s中出现了t中的所有元素)
  4. {
  5. 计算最小覆盖长度
  6. 头指针右移收缩
  7. }
  8. 尾指针右移扩张
  9. }

代码:

  1. char * minWindow(char * s, char * t){
  2. int start = 0, end = 0;
  3. int slen = strlen(s);
  4. int tlen = strlen(t);
  5. int chst[128] = {0};
  6. int minlen = INT_MAX, minlenindex;
  7. int tmplen;
  8. //统计t中所有字符出现的次数
  9. for (end = 0; end < tlen; end++)
  10. {
  11. chst[t[end]]++;
  12. }
  13. end = 0;
  14. while (end < slen)
  15. {
  16. if (chst[s[end]] > 0)
  17. {
  18. //此时tlen的大小代表s已经出现了(strlen(t) - tlen)个t中的元素
  19. tlen--;
  20. }
  21. //chst[i]为正,代表s中还需出现chst[i]次字符i,才会使s与t中出现i的次数相同
  22. //chst[i]为负,代表字符i只在s中出现
  23. chst[s[end]]--;
  24. //难点:tlen==0 时代表s中已经出现了t中所有的元素(包括重复的元素)
  25. //同时,也作为头指针收缩的条件
  26. while (0 == tlen)
  27. {
  28. //更新最小覆盖距离
  29. tmplen = end - start + 1;
  30. if (minlen > tmplen)
  31. {
  32. minlen = tmplen;
  33. minlenindex = start;
  34. }
  35. chst[s[start]]++;
  36. if (chst[s[start]] > 0)
  37. {
  38. //代表收缩后的窗口不满足条件了,需要扩张
  39. tlen++;
  40. }
  41. start++;
  42. }
  43. end++;
  44. }
  45. //收缩过至少一次
  46. if(minlen != INT_MAX)
  47. {
  48. char* t = (char*)malloc(sizeof(char)*(minlen+1));
  49. *t = '\0';
  50. strncat(t, s+minlenindex, minlen);
  51. return t;
  52. }
  53. //一次都没收缩过
  54. return "";
  55. }

题目四:239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

思路:滑动窗口找极值,采用单调队列思想

  1. //由于窗口长度被固定了,所以没有动态的收缩扩张过程
  2. for(维护滑动窗口的移动,直到遍历结束)
  3. {
  4. 维护一个单调队列,保证队头指针指向的元素一定是窗口内的最大值,且整个队列保持从左到右单调递减
  5. 对于每一次窗口向前移动,对应删除一个旧元素,增加一个新的元素(必须先判断出队,再判断入队)
  6. 每移动一次就把队头元素输出一次
  7. }
  8. //难点1:判断出队的元素是不是队头元素
  9. //难点2:队头元素出队后如何使队头指针指向新的队头
  10. //难点3:入队的元素找到位置后直接覆盖旧元素,可以省略数组插入新元素的过程

代码:

  1. typedef struct Queue
  2. {
  3. int head;
  4. int tail;
  5. int * data;
  6. }MyQueue;
  7. int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize)
  8. {
  9. int* output = malloc(sizeof(int) * numsSize);
  10. MyQueue q;
  11. int i, j = 0;
  12. q.head = 0;
  13. q.tail = 0;
  14. q.data = malloc(sizeof(int) * numsSize);
  15. *returnSize = 0;
  16. for (i = 0; i < numsSize; i++)
  17. {
  18. //data内存的是数组下标,而非数组元素,目的是方便队头元素出队后,队头指针head容易找到下一个队头
  19. //data[head]处于窗口外了(被出队了),更新队头指针
  20. if (q.data[q.head] <= i - k && q.head < q.tail)
  21. {
  22. q.head++; // 出队后的下一个元素就是最大的
  23. }
  24. //给新入队的元素找在单调队列中的位置(在队尾指针指向的位置直接覆盖)
  25. for (j = q.tail; j > q.head; j--)
  26. {
  27. if (nums[q.data[j - 1]] < nums[i])
  28. {
  29. q.tail--;
  30. }
  31. //这里用for循环会超时的原因
  32. //1. 多给j进行了++操作
  33. //2. 如果不加beark,每次会进行多余的循环
  34. else
  35. {
  36. break;
  37. }
  38. }
  39. #if 0
  40. if (q.tail > q.head)
  41. {
  42. while (q.tail > q.head && nums[q.data[q.tail - 1]] < nums[i])
  43. {
  44. q.tail--;
  45. }
  46. }
  47. #endif
  48. q.data[q.tail++] = i;
  49. //前k个元素不输出(等待窗口成型)
  50. if (i >= k - 1)
  51. {
  52. output[(*returnSize)++] = nums[q.data[q.head]];
  53. }
  54. }
  55. return output;
  56. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/276838
推荐阅读
相关标签
  

闽ICP备14008679号