LeetCode-滑动窗口类题汇总

30 篇文章 0 订阅

模板

  • 初始左指针(left)和右指针(right)都指向0
  • 右指针不断向右移动
  • 当满足窗口大小时候,进行相应的处理,这个和过程中需要left向右移动
def sliding_window(window_size, array):
    left, right = 0, 0
    res = 0
    while right < len(array):
        if condition:
            pass
        while n > window_size or n == window_size:
            ...
            left += 1 # 右移left
            res = max(res, xxx)
            ...
        res = max(res, xxx)
        right += 1    # 右移right

    return res

1、无重复字符的最长子串

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/    

此题不限制窗口大小,窗口不含有重复字符就可以

def lengthOfLongestSubstring(self, s):
        left, right = -1, 0  # left = -1 是为了第一个字符计算方便
        maps = {}
        max_len = 0
        while right < len(s):
            # 初始或者当前字符在窗口左侧出现过 
            if maps.get(s[right]) is None or maps.get(s[right]) < left:
                max_len = max(max_len, right-left)
            else:
                left = maps.get(s[right])
            maps[s[right]] = right
            right += 1

        return max_len

2、替换后的最长重复字符

给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-repeating-character-replacement/

此题不限制窗口大小,窗口能替换成一样的字符就可以

def characterReplacement(self, s, k):
        if not s:
            return 0
        letter_num = {}
        left, right = 0, 0
        res = 0
        while right < len(s): 
            letter_num[s[right]] = letter_num.get(s[right], 0) + 1 # 统计字符个数
            max_letter = max(letter_num, key=letter_num.get)  # 计算字符数量最多的字符
            while right - left + 1 - letter_num[max_letter] > k: # 当替换次数大于阈值k,进入循环不断将窗口左端字符移除,直到满足要求
                letter_num[s[left]] -= 1
                left += 1
                max_letter = max(letter_num, key=letter_num.get)  # 此时要不断记录最大的窗口
            res = max(res, right - left + 1)
            right += 1

        return res

3、滑动窗口中位数

给出一个数组 nums,有一个大小为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-median/

排序数组的插入和查找用了个模块bisect ,用法参考:https://www.cnblogs.com/skydesign/archive/2011/09/02/2163592.html

    def medianSlidingWindow(self, nums, k):
        result = []
        if not nums or not k:
            return result
        # 计算中位数
        def calculate_median(window, k):
            if k % 2 == 1:
                median = window[k//2]
            else:
                median = (window[k//2 - 1] + window[k//2])/2.0
            return median

        n = len(nums)
        left, right = 0, k
        # 计算第一个窗口的中位数
        window = sorted(nums[left:right])
        result.append(calculate_median(window, k))
        while right < n:
            bisect.insort(window, nums[right]) # 将当前数插入到窗口中
            # 删除最左边的数
            index = bisect.bisect_left(window, nums[left])
            del window[index]
            result.append(calculate_median(window, k)) # 重新计算中位数并记录
            left += 1
            right += 1

        return result

4、字符串的排列

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。

换句话说,第一个字符串的排列之一是第二个字符串的子串。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-in-string/

    def checkInclusion(self, s1, s2):
        if s1 in s2:
            return True
        if len(s1)>len(s2):
            return False
        counter = collections.Counter(s1)  # 统计s1字符
        left, right = 0, 0
        temp_counter = dict(counter)
        while right < len(s2):
            if s2[right] not in temp_counter:  # 若s1中不包含,则重新统计,恢复counter
                right += 1
                left = right
                temp_counter = dict(counter)
                continue
            else:
                while temp_counter[s2[right]] == 0: # 窗口中right字符已经够数了,不断移除左字符,直到能放得下右字符
                    temp_counter[s2[left]] += 1
                    left += 1
                temp_counter[s2[right]] -= 1
                if temp_counter[s2[right]] > 0: # 窗口中字符数量不满足s1,继续移动,,此模块为了提高效率(针对下文的all)
                    right += 1
                    continue
            if all(v == 0 for v in temp_counter.values()):  # 满足则返回退出
                return True
            right += 1

        return False

5、K 个不同整数的子数组

给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定独立的子数组为好子数组。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subarrays-with-k-different-integers/

重复元素的数量要算在内

 def subarraysWithKDistinct(self, A, K):
        if not A or K == 0:
            return 0
        maps = collections.defaultdict(int)
        n = len(A)
        left, res, cnt, tmp = 0, 0, 0, 1
        for right in range(n):
            maps[A[right]] += 1
            if maps[A[right]] == 1: # 说明新来了一个字符,窗口不同字符数量+1
                cnt += 1   
            while cnt > K: # 不满足要求,移动左指针
                tmp = 1   
                cnt -= 1
                maps[A[left]] -= 1
                left += 1
            # 移动左指针的条件,直到满足条件,这里需要注意的是左指针移动一次,算新来一个数!这个时候才tmp++
            while maps[A[left]] > 1: # 大于1说明有重复元素,留下一个就可以,tmp记录有重复字符时候的子数组数量
                tmp += 1
                maps[A[left]] -= 1
                left += 1
            if cnt == K:
                res += tmp
        return res

6、滑动窗口最大值

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum/

应用单调双边队列(递减),保证最大值在第一个,实现双边队列用 from collections import deque

    def maxSlidingWindow(self, nums, k):
        # 单调双边队列
        if not nums or not k:
            return []
        res = []
        queue = []
        for i in range(len(nums)):
            while queue and nums[i] > nums[queue[-1]]:
                queue.pop()
            queue.append(i)
            if i >= k-1:
                res.append(nums[queue[0]])
            if i - queue[0] + 1 == k:
                queue.pop(0)
        return res

7、最长湍流子数组

当 A 的子数组 A[i], A[i+1], ..., A[j] 满足下列条件时,我们称其为湍流子数组:

若 i <= k < j,当 k 为奇数时, A[k] > A[k+1],且当 k 为偶数时,A[k] < A[k+1];
或 若 i <= k < j,当 k 为偶数时,A[k] > A[k+1] ,且当 k 为奇数时, A[k] < A[k+1]。
也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是湍流子数组。

返回 A 的最大湍流子数组的长度。
链接:https://leetcode-cn.com/problems/longest-turbulent-subarray/

def maxTurbulenceSize(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        if len(A) < 2:
            return len(A)
        max_len = 1
        left, right = 0, 2
        while right < len(A):
            if A[right - 1] == A[right]:
                left = right
                right += 1
            elif A[right - 2] > A[right - 1] < A[right] or A[right - 2] < A[right - 1] > A[right]:
                max_len = max(max_len, right - left + 1)
                right += 1
            elif A[right - 1] < A[right] or A[right - 1] > A[right]:
                max_len = max(max_len, 2)
                left = right - 1
                right += 1
        return max_len

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值