560. 和为k的子数组
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. 滑动窗口最大值
题目看起来很简单,但是我们如果在每个子数组中都去统计最大值,那么时间复杂度就会很高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. 最小覆盖子串
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) 两个字典