题目和思路:
977.有序数组的平方:这题是昨天的拓展题已经做过了,参考昨日博客代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素-优快云博客
暴力解法:
遍历所有符合要求的子序列,第一层循环找起点,第二个循环从起点开始找符合要求的子序列的终点。比较难受的一点就是找不到符合要求的序列,我的方法是初始化时设定一个比数组长度大的值,然后每次更新找记录值和当前值中较小的min<res,cur>,最后判断一下如果res比原数组还大就输出0。好歹能跑对先不管他了。以下是代码:
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
res = len(nums)+1
for i in range(len(nums)):
cnt = target - nums[i]
j = i+1
while cnt > 0 and j < len(nums):
cnt -= nums[j]
j+=1
if cnt <= 0:
res = min(j-i, res)
if res > len(nums):return 0
else:return res
滑动窗口:
其实是双指针的变形,快指针指向终点,慢指针指向起点,子序列的和小于target时移动快指针,大于等于target时记录res并尝试缩小窗口也就是移动慢指针,如此循环最终找到最小的res。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
res = len(nums) + 1
s, fast,slow = 0,0,0
for fast in range(res - 1):
s += nums[fast]
while s >= target:
res = min(res,fast - slow + 1)
s -= nums[slow]
slow += 1
return res if res<=len(nums) else 0
*学到一个return res if res<=len(nums) else 0的写法
做了挺久,主要应用滑动窗口和哈希表。fast指针遍历数组,每移动一次查看当前哈希表,如果指向元素就在表内,给表的值加一(用于后续slow指针移动计数);如果哈希表内只有0,1个元素,指向元素不在表内,添加一个键值给这个元素;如果哈希表内有2个元素了已经满了,指向元素还不在表内,说明从slow到fast-1是一个合法的区间,记录长度,并且开始移动slow缩小窗口,缩小的逻辑是每次移动给哈希表对应键的值-1,如果值变成0,就把这个键删掉,创建新的键——fast指向的元素。需要注意的是,如果队尾元素也在哈希表内,就少了一次记录长度的过程,在return的时候直接比较一下就可以得到结果。这样就完成了一个O(2n)的滑动窗口算法。代码如下:
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
l = len(fruits)
s,fast,slow = 0,0,0
basket = {}
for fast in range(l):
if fruits[fast] in basket: basket[fruits[fast]] += 1
elif len(basket) < 2: basket[fruits[fast]] = 1
else:
s = max(s,fast - slow)
while len(basket) == 2:
basket[fruits[slow]] -= 1
if basket[fruits[slow]] == 0: del basket[fruits[slow]]
slow += 1
basket[fruits[fast]] = 1
return max(s,l-slow)
终于AC了!
滑动窗口由两个指针 slow和 fast生成,移动这两个指针来控制窗口。window_counts 字典用来跟踪当前窗口中每个字符的数量,而 dict_t 字典用来存储字符串 t 中每个字符的期望数量。当窗口中包含了 t 中所有所需的字符时,cnt 计数器会与 required 相等,此时尝试通过移动slow指针来缩小窗口,直到找到最小的包含 t 所有字符的子串。最终,函数返回这个最小子串,如果不存在这样的子串,则返回空字符串。代码如下:
class Solution:
def minWindow(self, s: str, t: str) -> str:
if not s or not t:
return ""
from collections import Counter
# 计算t中每个字符的数量
dict_t = Counter(t)
required = len(dict_t)
fast,slow = 0,0
cnt = 0
window_counts = {}
res = float('inf'), None, None
while fast < len(s):
character = s[fast]
window_counts[character] = window_counts.get(character, 0) + 1
if character in dict_t and window_counts[character] == dict_t[character]:
cnt += 1
while slow <= fast and cnt == required:
character = s[slow]
if fast -slow + 1 < res[0]: res = (fast - slow + 1, slow, fast)
window_counts[character] -= 1
if character in dict_t and window_counts[character] < dict_t[character]:
cnt -= 1
slow += 1
fast += 1
return "" if res[0]==float('inf') else s[res[1]:(res[2] + 1)]
*学到一个dict[key] = dict.get(key,0)+1的写法。
自己写跟代码随想录的想法完全不一样,我逐个更新当前位置值,定义方向"微分"以及边界判断是否需要转向...具体看代码吧...
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
# 创建一个 n x n 的空矩阵
matrix = [[0] * n for _ in range(n)]
# 定义起始位置和方向
x, y, dx, dy = 0, 0, 1, 0
# 填充矩阵
for i in range(1, n * n + 1):
matrix[y][x] = i
# 检查下一个位置是否超出边界或已经被访问过
if matrix[(y + dy) % n][(x + dx) % n]:
# 改变方向
dx, dy = -dy, dx
x += dx
y += dy
return matrix
方法避开了卡哥的良苦用心,那就再练习一下循环不变式...
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
nums = [[0] * n for _ in range(n)]
startx, starty = 0, 0
loop, mid = n // 2, n // 2
count = 1
for offset in range(1, loop + 1) :
for i in range(starty, n - offset) :
nums[startx][i] = count
count += 1
for i in range(startx, n - offset) :
nums[i][n - offset] = count
count += 1
for i in range(n - offset, starty, -1) :
nums[n - offset][i] = count
count += 1
for i in range(n - offset, startx, -1) :
nums[i][starty] = count
count += 1
startx += 1
starty += 1
if n % 2 != 0 :
nums[mid][mid] = count
return nums
今日总结:
2小时两道拓展题累死自己,但是还是更好的理解了滑动窗口解法。螺旋矩阵小聪明避开了循环问题,自己练习循环不变式的时候还是会出错,勤加练习吧...
一刷训练营感觉数组题目其实技巧性还是有一些的,有些题没有经验完全不知道如何下手。跟完两天收获很大,感觉薄弱的算法正在走向正轨...
本文介绍了使用暴力法和滑动窗口算法解决两个编程问题:有序数组的平方和长度最小子数组。通过实例展示了如何用滑动窗口优化求解过程,包括904.水果成篮和76.最小覆盖子串。同时提及了螺旋矩阵II的两种解法。作者通过实战提高了对算法的理解和运用。
1541

被折叠的 条评论
为什么被折叠?



