hot100-子串

560. 和为k的子数组

560. 和为 K 的子数组 - 力扣(LeetCode)

python 复制代码
class Solution(object):
    def subarraySum(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        # 求的是连续子串,所以排序不可以用
        # 又数字有正有负,所以快慢指针会失效!
        # 使用前缀和的思想,如果有一个子串j~i相加为k,则i的前缀和减去(j-1)的前缀和等于k
        d = {0:1}  # j=0,j-1=-1,我们定义一个0的前缀和
        pre = 0
        res = 0
        for i in range(len(nums)):
            pre += nums[i]  # i的前缀和
            res += d.get(pre-k,0)
            d[pre] = d.get(pre,0) + 1
        return res

时间复杂度:O(N)

空间复杂度:O(N)

239. 滑动窗口最大值

239. 滑动窗口最大值 - 力扣(LeetCode)

题目看起来很简单,但是我们如果在每个子数组中都去统计最大值,那么时间复杂度就会很高O(kN),最终会超时!

那么遍历数组的O(N)是不可少的,我们希望让获取max的操作变成O(1);假如我们仅仅是每遍历一个数组,仅仅记录最大值,但是我们就不知道这个最大值什么时候从当前窗口弹出!

最终实现使用的是单调队列!【这里单调队列更加适合,因为在滑动窗口的移动中,先进入的先弹出!】

python 复制代码
class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        res = []
        dq = []
        for i in range(k):
            # 单调队列:队首元素最大!
            # 其实我们不在乎队列里面到底存多少个元素,我们仅仅是留下较大的元素的下标
            # 如果新入队的元素下标和队首差距只要不超过k,那么当前的队首就还是最大元素
            # 如何保证单调性呢?从队尾开始比,比如7 3 5如果仅仅和队首比,就会有问题,中间那些小的依然在
            # 我们要保证的单调性就是队首到队尾的单调递减
            while dq and nums[i]>nums[dq[-1]]:
                dq.pop()  # 比的是队尾,弹的是队尾
            dq.append(i)
        res.append(nums[dq[0]])
        for i in range(k,len(nums)):
            while dq and nums[i]>nums[dq[-1]]:
                dq.pop()
            dq.append(i)
            while i-dq[0]>=k:
                dq.pop(0)  # 距离过大弹队首
            res.append(nums[dq[0]])
        return res
操作 Python list collections.deque 说明
append() 尾部入队 O(1) O(1) 两者都高效
pop() 尾部出队 O(1) O(1) 两者都高效
pop(0) 头部出队 O(n) O(1) 列表需要移动元素
insert(0, item) 头部入队 O(n) O(1) 列表需要移动元素
随机访问 queue[i] O(1) O(n) deque随机访问慢
python 复制代码
class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        res = []
        dq = collections.deque()
        for i in range(k):
            while dq and nums[i]>nums[dq[-1]]:
                dq.pop()  # 比的是队尾,弹的是队尾
            dq.append(i)
        res.append(nums[dq[0]])
        for i in range(k,len(nums)):
            while dq and nums[i]>nums[dq[-1]]:
                dq.pop()
            dq.append(i)
            while i-dq[0]>=k:
                dq.popleft()  # 距离过大弹队首
            res.append(nums[dq[0]])
        return res

但是如果是栈的话,用list差不多!【查看栈顶或者弹出栈顶,都是对列表最后一个元素操作,时间复杂度差不多!】

时间复杂度:O(N) 【每个元素除被遍历外,最多入对一次,出队一次 最多3N】

空间复杂度:O(k) 【队列的最大长度就是k】

76. 最小覆盖子串

76. 最小覆盖子串 - 力扣(LeetCode)

python 复制代码
class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        m = len(s)
        n = len(t)
        if not s or not t or m<n:
            return ""
        dt = collections.Counter(t)
        ds = collections.Counter()
        min_len = m+1
        min_left = left = 0
        required = len(dt)
        cur = 0  # 记录一下有多少满足了
        for right in range(m):
            char = s[right]
            ds[char] += 1
            if char in dt and ds[char] == dt[char]:
                cur += 1
            # 既然找到了一个子串,我们其实可以尽可能的缩小左边界【注意,并非直接判断】
            # 比如最开始的几个字符可能都是其他字符,我们要while 缩小
            while left<=right and cur == required:
                win_len = right-left+1
                if win_len<min_len:
                    min_len = win_len
                    min_left = left
                left_char = s[left]
                ds[left_char] -= 1
                # 这里除了比较在不在,还需要比较大小,因为在之前的代码中,我们一旦满足某一个字符,但是之后可能会继续增加该字符
                if left_char in dt and ds[left_char] < dt[left_char]:
                    cur -=1
                left += 1
        return "" if min_len == m+1 else s[min_left:min_left+min_len]

时间复杂度:O(N) 最多相当于left和right两遍遍历!

空间复杂度:O(N) 两个字典

相关推荐
灵感__idea3 小时前
Hello 算法:贪心的世界
前端·javascript·算法
知行合一。。。4 小时前
Python--04--数据容器(总结)
开发语言·python
架构师老Y4 小时前
008、容器化部署:Docker与Python应用打包
python·容器·架构
澈2075 小时前
深入浅出C++滑动窗口算法:原理、实现与实战应用详解
数据结构·c++·算法
lifewange5 小时前
pytest-类中测试方法、多文件批量执行
开发语言·python·pytest
ambition202425 小时前
从暴力搜索到理论最优:一道任务调度问题的完整算法演进历程
c语言·数据结构·c++·算法·贪心算法·深度优先
pluvium275 小时前
记对 xonsh shell 的使用, 脚本编写, 迁移及调优
linux·python·shell·xonsh
cmpxr_5 小时前
【C】原码和补码以及环形坐标取模算法
c语言·开发语言·算法
qiqsevenqiqiqiqi5 小时前
前缀和差分
算法·图论
代码旅人ing5 小时前
链表算法刷题指南
数据结构·算法·链表