剑指Offer and Leetcode刷题总结之常见策略(3):滑动窗口

目录

 

剑指48:最长不含重复字符的子字符串(题解参考) /Leetcode03

剑指57-I:和为s的两个数字

剑指57-II:和为s的连续正数序列

剑指59-I:滑动窗口的最大值/Leetcode239

剑指59-II:队列的最大值

 


剑指48:最长不含重复字符的子字符串(题解参考) /Leetcode03

基本思路:双指针+hashmap

# 如果j不在dict中,增加该元素进入dict;

# 如果j已经在dict中,更新i的位置;同时保证i是向右移动的,所以要加和原来的i比较的逻辑;同时更新重复元素的最新位置

class Solution03():
    """
    方法1:滑动窗口+遍历
    时间复杂度为O(n)--勘误:这个时间复杂度应该不为O(n),每次在子字符串中要查找一下正在迭代的元素是否在里面
    空间复杂度为O(1)
    """
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        if not s: return 0
        i, j, l, ans = 0, 0, 1, 1
        for j in range(len(s) - 1):
            if s.find(s[j+1], i, j+1) == -1:
                j += 1
                l += 1
                ans = max(ans, l)
                print('ans = %d and l = %d' % (ans, l))
            else:
                i = s.find(s[j+1], i, j+1) + 1
                l = j + 1 - i + 1
                print('---ans = %d and l = %d' % (ans, l))
        return ans
    """
    方法2:滑动窗口+hashmap
    时间复杂度O(n)+空间复杂度O(M):M为最长的子字符串长度
    """
    def lengthOfLongestSubstring_02(self, s):
        i, ans = 0, 0
        dict = {}
        for j in range(len(s)):
            if s[j] in dict:
#             if dict.get(s[j]): # ??? 为啥用这种写法就不行???
                i = max(dict[s[j]] + 1, i)
            dict[s[j]] = j
            ans = max(ans, j-i+1)    
        return ans

剑指57-I:和为s的两个数字

题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例输入:nums = [2,7,11,15], target = 9 输出:[2,7] 或者 [7,2]

基本思路:左右双指针

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        i = 0
        j = len(nums) - 1
        while i < j:
            if nums[i] + nums[j] > target:
                j -= 1
            elif nums[i] + nums[j] < target:
                i += 1
            else:
                return [nums[i], nums[j]]
        return []

剑指57-II:和为s的连续正数序列

题目:输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

基本思路:双指针,sum<target: 右边界+;sum>target:左边界-;

    def findContinuousSequence03(self, target):
        """
        方法三:采用滑动窗口,这个思路很牛逼!
        if 窗口中的sum < target, 那么右边界往右移动一格;
        if 窗口中的sum > target, 那么左边界往右移动一格;
        if 窗口中的sum = target, 那么记录下来之后, 左和右边界同时往右移动一格
        
        总结:
        只要是递增求和满足条件的情景 都可以用这种滑动窗口求解
        !巨坑!:开始写了i for i in(...), 这里算内层循环了,使用了外层循环算子,给搞乱套了
        """
        ans = []
        i = 1 # 左边界
        j = 2 # 右边界
        sum = i + j
        while(i <= target/2):
            if sum < target:
                j += 1
                sum += j
            elif sum > target:
                i += 1
                sum -= (i-1)
            else: # sum == target
                ans.append([m for m in (range(i, j+1))])    # !巨坑!:这里开始写了i for i in(...), 这里算内层循环了,使用了外层循环算子,给搞乱套了
                i += 1
                j += 1
                sum = sum - (i-1) + j
        return ans

剑指59-I:滑动窗口的最大值/Leetcode239

基本思路:暴力即可

class Solution59(object):
    """
    方法1:
    小优化:如果新进来的element>前一个窗口的max_value,直接赋值,不用再搜索一遍。
    """
    def maxSlidingWindow(self, nums, k):
        if not nums: return []
        ans = []
        i = 0
        while i <= len(nums) - k:
            ans.append(max(nums[i: i + k]))
            i += 1
        return ans
    
    """方法2"""
    def maxSlidingWindow(self, nums, k):
        deque = collections.deque()
        res = []
        for i, num in enumerate(nums):
            if deque and deque[0] <= i - k: deque.popleft()
            while deque and num > nums[deque[-1]]: deque.pop()
            deque.append(i)
            if i >= k - 1:
                res.append(nums[deque[0]])
        return res

剑指59-II:队列的最大值题解参考

-->同类型题目How在O(1)能取到栈stack里面的最大值-->维护一个递增的栈,辅助stack中只有新元素大于栈顶元素时,才会push进来;在pop时,如果主stack pop掉元素与辅助stack栈顶元素相等,则辅助站也执行pop操作;

题目:请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。若队列为空,pop_front 和 max_value 需要返回 -1

基本思路:引入队列结构collections.deque();要在O(1)找到max_value, 需要一个辅助队列,保存max_value这个值;维护一个递减的队列

    """
    方法2:
    引入队列结构collections.deque()
    要在O(1)找到max_value, 需要一个辅助队列,保存max_value这个值
    """
from collections import deque
class MaxQueue_02(object):
    def __init__(self):
        from collections import deque
        self.que = deque()
        self.sort_que = deque()
        
    def max_value(self):
        """
        时间复杂度为O(1)
        """
        return -1 if not self.sort_que else self.sort_que[0]
        
    def push_back(self, value):
        """
        均摊时间复杂度为O(1)???如何分析呢???
        总共只有n个元素,某一次pop()的元素多了, 那么下一次就是常数级别的时间复杂度, 均摊到每一次也就是O(1)
        """
        self.que.append(value)
        while self.sort_que and self.sort_que[-1] < value:
            self.sort_que.pop()
        self.sort_que.append(value)
        
    def pop_front(self):
        """
        时间复杂度为O(1)
        """
        pop = -1 if not self.que else self.que.popleft()
        if pop != -1 and self.sort_que[0] == pop:
            self.sort_que.popleft()
        return pop

class MaxQueue(object):
    """
    方法1:用list实现
    求最大值的时间复杂度是O(n)
    push_back的时间复杂度是O(1)
    pop_front的空间复杂度为O(n)
    """
    def __init__(self):
        self.val = []
    
    def __str__(self):
        return str([i for i in self.val])
    
    def max_value(self):
        """
        :rtype: int
        """
        if not self.val: return -1
        max_val = self.val[0]
        for i in range(1, len(self.val)):
            if self.val[i] > max_val:
                max_val = self.val[i]
        return max_val

    def push_back(self, value):
        """
        :type value: int
        :rtype: None
        """
        self.val.append(value)


    def pop_front(self):
        """
        :rtype: int
        """
        pop = -1 if not self.val else self.val[0]
        self.val = [] if not self.val else self.val[1: ]
        return pop

Leetcode76:最小覆盖子串参考题解

题目:给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。

示例输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC"

基本思路:见代码逻辑

class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        import collections
        need = collections.Counter(t) # 记录需要的t中的元素的个数
        needCnt = len(t)   # 记录需要的t中元素的总数,避免每次都要check need
        # start = end = 0
        ans = (0, float('inf')) # 记录最短字符串位置
        i = 0              # 指针首部
        for j, char in enumerate(s):
            if need[char] > 0:  # 如果need中没有char这个元素,不会进入到这个分支中;而且need[char] > 0只可能是在t中的元素,非相关元素在need中的val只会为非正数;
                needCnt -= 1
            need[char] -= 1     # 即使s[j]这个元素不在t中,也需要-1(自动添加元素,然后-1)
            if needCnt == 0:    # 满足t中的元素已经全部在滑动窗口中
                while True:     
                    if need[s[i]] == 0: # 缩小窗口直到找到第一个t中的元素且为需要次数为0跳出
                        break
                    need[s[i]] += 1     # 否则更新s[i]
                    i += 1
                if ans[1] - ans[0] > j - i: # 保留符合条件的最小长度
                    ans = (i, j)
                need[s[i]] += 1   # 跳出循环后,更新窗口
                needCnt += 1
                i += 1
        return '' if ans[1] > len(s) else s[ans[0] : ans[1]+1] # 考虑ans没有被更新过的情况

Leetcode15:三数之和

题目:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

示例:给定数组 nums = [-1, 0, 1, 2, -1, -4],满足要求的三元组集合为:[[-1, 0, 1], [-1, -1, 2]]

基本思路:先排序,滑动窗口,遇到重复的值跳过;

class Solution(object):
    def threeSum(self, nums):
        size = len(nums)
        if size < 3: return []
        nums.sort()
        ans = []
        for k in range(size-2):
            if k > 0 and nums[k] == nums[k-1]: continue
            i, j = k + 1, size - 1 
            while i < j:
                if nums[k] + nums[i] + nums[j] == 0:
                    ans.append([nums[k], nums[i], nums[j]])
                    i += 1
                    j -= 1
                    while i < j and nums[i] == nums[i-1]: i += 1
                    while i < j and nums[j] == nums[j+1]: j -= 1
                elif nums[k] + nums[i] + nums[j] < 0:
                    i += 1
                    while i < j and nums[i] == nums[i-1]: i += 1
                else:
                    j -= 1
                    while i < j and nums[j] == nums[j+1]: j -= 1
        return ans

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值