滑动窗口算法用法及实题详解

在力扣常用解题法中,我们常常会看到这些:

  1. 滑动窗口
  2. 双指针
  3. 快慢指针/ 链表题目
  4. 原地链表翻转
  5. 区间合并
  6. 无序限定范围的数组元素查找O(N)
  7. BFS
  8. 树的DFS
  9. DFS/递归/回溯法
  10. 双堆模式
  11. 2分变种
  12. 前K大的数模式HEAP
  13. K路归并
  14. DP 动态规划
  15. 排序算法
  16. 树和链表结合
  17. 树的重新构建
  18. 位运算
  19. 字符串
  20. stack
  21. math
  22. array
  23. 二叉搜索树

快慢指针、动态规划、链表、树的遍历,这些已经是烂大街的概念。
而偏偏是最常用的,也排在首位的滑动窗口算法,却并不为人熟知,什么是滑动窗口算法呢??

滑动窗口算法可以用以解决数组/字符串的子元素问题,它可以将嵌套的循环问题,转换为单循环问题,降低时间复杂度。

力扣上最熟悉的题目应该就是两数之和、三数之和,这些题目如果我们要使用暴力解法,一般需要n方甚至是立方的复杂度。而如果采用滑动窗口来解决这些问题,我们就会避免两层三层的循环嵌套,从而在非常大的程度上降低时间复杂度。


滑动窗口的应用场景:如上,我们需要获得数组中、字符串中符合条件的字元素时,我们就可以采用滑动窗口。
简单介绍一下滑动窗口的用法:依次遍历数组,此时的下标即右指针,向后依次挪动。另外,我们会定义一个左指针,当左指针和右指针区间内的元素不符合条件时,我们便会移动左指针,直至条件符合,继续挪动右指针。其间统计每一次的结果。

实际应用举例:

leetcode 1438. 绝对值不超过限制的最长连续子数组

题目描述:

给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0 。

示例 1:
输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。

示例 2:
输入:nums = [10,1,2,4,7,2], limit = 5
输出:4
解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。

示例 3:
输入:nums = [4,2,2,2,4,4,2,2], limit = 0
输出:3

提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
0 <= limit <= 10^9

public class _1438_绝对值不超过限制的最长连续子数组 {

    /**
     * 我们可以枚举每一个位置作为右端点,找到其对应的最靠左的左端点,满足区间中最大值与最小值的差不超过 
     * limit
     * limit。
     *
     * 注意到随着右端点向右移动,左端点也将向右移动,于是我们可以使用滑动窗口解决本题。
     *
     * 为了方便统计当前窗口内的最大值与最小值,我们可以使用平衡树:
     *
     * 语言自带的红黑树,例如
     * Java
     * Java 中的 
     * TreeMap
     * 来维护窗口内元素构成的有序集合。
     *
     * @param nums
     * @param limit
     * @return
     */
    public int longestSubarray(int[] nums, int limit) {

        TreeMap<Integer,Integer> treeMap = new TreeMap<>();
        int left = 0;
        int res = 0;
        int n = nums.length;
        int right = 0;
        while (right < n)
        {
            // 1 1 4  5  1
              treeMap.put(nums[right], treeMap.getOrDefault(nums[right],0) + 1);
              while (treeMap.lastKey() - treeMap.firstKey() > limit)
              {
                  treeMap.put(nums[left], treeMap.get(nums[left]) - 1);
                  if (treeMap.get(nums[left]) == 0)
                  {
                      treeMap.remove(nums[left]);
                  }
                  left++;
              }
              res = Math.max(res, right - left + 1);
              right++;
        }
        return res;
    }
}

你可能感兴趣的:(算法练习-进阶篇,指针,算法,数据结构,java,滑动窗口)