暑假算法日记第一天
目标:刷完灵神专题训练算法题单
阶段目标📌:【算法题单】滑动窗口与双指针
LeetCode题目:
- 1456. 定长子串中元音的最大数目
- 643. 子数组最大平均数 I
- 1343. 大小为 K 且平均值大于等于阈值的子数组数目
- 2090. 半径为 k 的子数组平均值
- 2379. 得到 K 个黑块的最少涂色次数
- 2841. 几乎唯一子数组的最大和
其他:
今日总结
1456. 定长子串中元音的最大数目
跳转: 1456. 定长子串中元音的最大数目
学习: 灵神:教你解决定长滑窗!
问题:
给你字符串 s
和整数 k
。
请返回字符串 s
中长度为 k
的单个子字符串中可能包含的最大元音字母数。
英文中的 元音字母 为(a
, e
, i
, o
, u
)。
思路:
定长滑动窗口,先装入元素,根据题目要求更新,窗口满员则下次装入前先退出末尾元素。
复杂度:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution:def maxVowels(self, s: str, k: int) -> int:ans = vowel = 0for i,c in enumerate(s):if c in "aeiou":vowel += 1if i < k - 1:continueans = max(ans,vowel)if s[i - k + 1] in "aeiou":vowel -= 1return ans
643. 子数组最大平均数 I
跳转: 643. 子数组最大平均数 I
问题:
给你一个由 n
个元素组成的整数数组 nums
和一个整数 k
。
请你找出平均数最大且 长度为 k
的连续子数组,并输出该最大平均数。
任何误差小于 10-5
的答案都将被视为正确答案。
思路:
滑动窗口求最大值,题目条件保证 k < = n k <= n k<=n 。可以先初始化为前k个元素作为初始窗口,然后边移动边比较
本质上还是入-更新-出,相当于初始化并更新,然后倒出-装入-更新。倒出-装入一步完成
复杂度:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution:def findMaxAverage(self, nums: List[int], k: int) -> float:maxArgv = temp = sum(nums[:k])for idx in range(0,len(nums)-k):temp += nums[idx+k] - nums[idx]maxArgv = max(maxArgv,temp)return maxArgv / k
1343. 大小为 K 且平均值大于等于阈值的子数组数目
跳转: 1343. 大小为 K 且平均值大于等于阈值的子数组数目
问题:
给你一个整数数组 arr
和两个整数 k
和 threshold
。
请你返回长度为 k
且平均值大于等于 threshold
的子数组数目。
思路:
这里要求计数,首先threshold与k是整数,商和除数为整数的情况下求被除数不用考虑精度,所以可以直接用threshold * k卡计数条件,初始化-更新,出入-更新滑动窗口即可。
复杂度:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution:def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int:ans = 0Bound = threshold * ktemp = sum(arr[:k])if temp >= Bound:ans += 1for idx in range(0,len(arr)-k):temp += arr[idx + k] - arr[idx]if temp >= Bound:ans += 1return ans
2090. 半径为 k 的子数组平均值
跳转:2090. 半径为 k 的子数组平均值
问题:
给你一个下标从 0 开始的数组 nums
,数组中有 n
个整数,另给你一个整数 k
。
半径为 k 的子数组平均值 是指:nums
中一个以下标 i
为 中心 且 半径 为 k
的子数组中所有元素的平均值,即下标在 i - k
和 i + k
范围(含 i - k
和 i + k
)内所有元素的平均值。如果在下标 i
前或后不足 k
个元素,那么 半径为 k 的子数组平均值 是 -1
。
构建并返回一个长度为 n
的数组 avgs
,其中 avgs[i]
是以下标 i
为中心的子数组的 半径为 k 的子数组平均值 。
x
个元素的 平均值 是 x
个元素相加之和除以 x
,此时使用截断式 整数除法 ,即需要去掉结果的小数部分。
- 例如,四个元素
2
、3
、1
和5
的平均值是(2 + 3 + 1 + 5) / 4 = 11 / 4 = 2.75
,截断后得到2
。
思路:
可以看作是窗口大小为 2 * k + 1 的滑动窗口,但更新是在中点处更新,这里采用覆盖的方式更新值
本质上还是入-更新-出,相当于初始化并更新,然后倒出-装入-更新。倒出-装入一步完成
复杂度:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
代码:
class Solution:def getAverages(self, nums: List[int], k: int) -> List[int]:size = k * 2 + 1avgs = [-1 for _ in range(len(nums))]if len(nums) < size:return avgstemp = sum(nums[:size])avgs[k] = temp // sizefor idx in range(0,len(nums) - size):temp += nums[idx + size] - nums[idx]avgs[idx + k + 1] = temp // sizereturn avgs
2379. 得到 K 个黑块的最少涂色次数
跳转: 2379. 得到 K 个黑块的最少涂色次数
问题:
给你一个长度为 n
下标从 0 开始的字符串 blocks
,blocks[i]
要么是 'W'
要么是 'B'
,表示第 i
块的颜色。字符 'W'
和 'B'
分别表示白色和黑色。
给你一个整数 k
,表示想要 连续 黑色块的数目。
每一次操作中,你可以选择一个白色块将它 涂成 黑色块。
请你返回至少出现 一次 连续 k
个黑色块的 最少 操作次数。
思路:
相当于在大小为k的窗口中求白色快最少数量
每一个都需要判断,不是简单加和,所以不能偷懒,入-更新-出
复杂度:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution:def minimumRecolors(self, blocks: str, k: int) -> int:ans = ktemp = 0for idx,value in enumerate(blocks):if value == 'W':temp += 1if idx < k - 1:continueans = min(temp,ans)if blocks[idx - k + 1] == 'W':temp -= 1return ans
2841. 几乎唯一子数组的最大和
跳转: 2841. 几乎唯一子数组的最大和
问题:
给你一个整数数组 nums
和两个正整数 m
和 k
。
请你返回 nums
中长度为 k
的 几乎唯一 子数组的 最大和 ,如果不存在几乎唯一子数组,请你返回 0
。
如果 nums
的一个子数组有至少 m
个互不相同的元素,我们称它是 几乎唯一 子数组。
子数组指的是一个数组中一段连续 非空 的元素序列。
思路:
入-更新-出,但只在满足不重复元素大于等于m的情况下更新
这里维护字典键值对数来判断不重复元素有多少
复杂度:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution:def maxSum(self, nums: List[int], m: int, k: int) -> int:ans = cnt = 0dict_num = {}for idx,num in enumerate(nums):cnt += numif num in dict_num:dict_num[num] += 1else:dict_num[num] = 1if idx < k - 1:continueif len(dict_num) >= m:ans = max(ans,cnt)temp = nums[idx - k + 1]cnt -= tempif dict_num[temp] == 1:del dict_num[temp]else:dict_num[temp] -= 1return ans