一、滑动窗口的最大值
方法一:暴力解法(两种代码实现)
今天做题发现这道n久之前做的题,现在似乎还是只能想到暴力解法,然而写了两种形式,果真是没什么进步
解法一:
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#暴力解法
if nums == [] or k == 0:
return nums
n = len(nums)
window = nums[:k]
res = []
res.append(max(window))
for i in range(k,n):
window.append(nums[j])
window.pop(0)
res.append(max(window)
return res
解法二:
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
lens = len(nums)
if nums == [] or k==0:
return nums
results = []
for i in range(0,lens-k+1):
windows = nums[i:i+k]
max_num = max(windows)
results.append(max_num)
return results
这种方法的时间复杂度是o(nk)
这种每滑动一次就求一次当前窗口的最大值方法,非常低效因为每次滑动只有两个元素发生了变动(出去一个进来一个),而为了这两个元素的变动就要重新遍历一次求最大值的开销非常昂贵的。
方法二:保存窗口最大值减少计算量
解题思路:
可以考虑将当前窗口的最大值maxVal保存下来,滑动时用新增的元素num与maxVal进行bijiao :
maxVal >= num 输出 maxVal ,且maxVal不变
maxVal < num 输出 num, 更新 maxVal = num
当然,需要考虑一种特殊情况:保存下来的maxVal刚好等于上一个窗口的最左侧边界值,需要重新求当前窗口的最大值。
示例:
输入数组nums = [1,3,-1,-3,5,3,6,5,-1,2], k = 4
初始窗口元素:[1,3,-1,-3] maxVal = 3
第一次滑动后:[3,-1,-3,5] maxVal = 5
第二次滑动后:[-1,-3,5,3] maxVal = 5(最大值不变,减少计算量)
第三次滑动后:[-3,5,3,6] maxVal = 6
第四次滑动后:[5,3,6,5] maxVal = 6(最大值不变,减少计算量)
第五次滑动后:[3,6,5,-1] maxVal = 6(最大值不变,减少计算量)
第五次滑动后:[6,5,-1,2] maxVal = 6(最大值不变,减少计算量
可以看到,针对上面的示例,总共7个窗口,只有3个窗口进行求最大值的操作,从而大大减少时间复杂度(计算量)
复杂度分析
时间复杂度:
最坏时间复杂度:O(n) * k
空间复杂度:
只需要常数空间来存储最大值,所以时间复杂度是:O(1)
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/chao-99bao-cun-chuang-kou-zui-da-zhi-jia-1gsn/
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if len(nums) == 0 or k == 1: # 异常值判断
return nums
def get_max(arr): # 自定义获取某个数组最大值的函数
result = arr[0]
for a in arr:
if a > result:
result = a
return result
max_num = get_max(nums[:k]) # 获取初始最大值
result = [max_num] # 填入初始最大值
sid = 1 # 初始窗口左边界索引值
fid = k # 初始窗口右边界索引值
while fid < len(nums): # 只要右边界未超出输入数组长度都进行窗口滑动
if max_num == nums[sid - 1]: # 核心判断逻辑:如果当前最大值等于上一个窗口左边界值
max_num = get_max(nums[sid:fid]) # 重新判断计算当前窗口最大值
if nums[fid] >= max_num: # 当前窗口新增的右边界值大于当前窗口最大值
result.append(nums[fid]) # 将当前窗口的最大值追加到最终结果集
max_num = nums[fid] # 重置当前窗口最大值
else: # 否则直接追加当前窗口的最大值
result.append(max_num)
sid += 1 # 窗口滑动
fid += 1 # 窗口滑动
return result
二、无重复字符的最长字串
class Solution:
def lengthOfLongestSubstring(self,s:str):
# 哈希集合:记录每个字符是否出现过
occ = set()
rindex ,ans = -1,0 # 初始右指针索引为-1,相当于我们在字符串的左边界左侧,还没有开始移动
n = len(s)
for i in range(n):
if i!=0:
# 左指针向右移动一格,移除一个字符
occ.remove(s[i-1])
while rindex+1<n and s[rindex+1] not in occ:
# 不断移动右指针
occ.add(s[rindex+1])
rindex+=1
# 第i到第rindex字符是一个长无重复字符子串
ans = max(ans, rindex-i+1)
return ans
三、最小覆盖字串
class Solution:
def minWindow(self, s:str, t:str) :
need = collections.defaultdict(int)
for c in t:
need[c] +=1
needCnt = len(t)
i = 0
res = (0, float('inf'))
# python中的正无穷:float('inf'),负无穷:float('-inf')
for j,c in enumerate(s):
if need[c]>0:
needCnt-=1
need[c] -=1
if needCnt == 0:# 步骤一:滑动窗口包含了所有T元素
while True:# 步骤二:增加i,排除多余元素
c = s[i]
if need[c]==0:
break
need[c]+=1
i+=1
if j-i< res[1]-res[0]:
res = (i,j)
need[s[i]] +=1 # 步骤三:i增加一个位置,寻找新的满足条件滑动窗口
needCnt+=1
i+=1
# 如果res始终没被更新过,代表无满足条件的结果
return '' if res[1]>len(s) else s[res[0]: res[1]+1]