不会算法的小白不是好小白,可恶还有什么可以难倒我这个美女的,不做花瓶第一天!
一、长度最小的子数组
209. 长度最小的子数组 - 力扣(LeetCode)
1.思路
滑动窗口法:把数组的区间,假设成为两个指针,先后移动两个指针
我们先读懂题目,这个很重要,不过我现在读的不是很懂,没事美女有弱点可以理解
2.辅助理解的例子(没办法罗,思路不过脑子只能解析一下)
let transactions = [1, 2, 3, 4, 5];
目标是找到一个连续的子数组,其元素总和至少为 9
let target = 9;
let result = minSubArrayLen(target, transactions);
1.初始设置
transactions = [1, 2, 3, 4, 5]
target = 9
start = 0, end = 0
sum = 0
ans = Infinity
2.迭代过程
-
第一轮迭代:
end = 0sum = 1(1)sum < target(9),所以end++。
-
第二轮迭代:
end = 1sum = 3(1 + 2)sum < target,所以end++。
-
第三轮迭代:
end = 2sum = 6(1 + 2 + 3)sum < target,所以end++。
-
第四轮迭代:
end = 3sum = 10(1 + 2 + 3 + 4)sum >= target:Math.min(ans, end - start + 1)->Math.min(Infinity, 4 - 0 + 1)->ans = 4sum -= nums[start],start++->sum = 9(2 + 3 + 4),start = 1
-
第五轮迭代:
end = 3sum = 9(2 + 3 + 4)sum >= target:Math.min(ans, end - start + 1)->Math.min(4, 4 - 1 + 1)->ans = 3sum -= nums[start],start++->sum = 7(3 + 4),start = 2
-
接下来的迭代
end继续增加,但不再找到总和大于等于target的更短子数组。
3结果
- 最终,
ans的值为3。 - 函数返回
3,表示最短的满足条件的子数组长度是3(即[2, 3, 4])。
很好这个滑动窗口是这样理解了,但是不会活学活用,那么下面继续
二.水果成篮
904. 水果成篮 - 力扣(LeetCode)
很抽象我读不懂题目:找了一个外援理解了一下题目
人话:我们的目标是找到一个窗口,其中只包含两种类型的水果,并且这个窗口尽可能大
步骤
-
初始化: 定义两个变量来跟踪窗口的开始和结束位置。同时,使用一个数据结构(如哈希表)来跟踪窗口内不同水果的数量。
-
扩大窗口: 从左向右移动窗口的右边界,即不断添加新的水果到窗口中,同时更新哈希表。
-
满足条件的窗口: 当窗口中包含超过两种水果时,移动窗口的左边界以排除一种水果,直到窗口重新只包含两种水果。
-
记录结果: 在每次更新窗口时,如果窗口只包含两种水果,更新最大收集水果的数量。
-
重复直到结束: 继续扩大和缩小窗口,直到覆盖了整个数组。
例子
假设 fruits = [1, 2, 1, 2, 3],我们可以按以下步骤使用滑动窗口法:
- 开始: 窗口为空,最大数量为 0。
- 窗口扩大: 添加
1,窗口为[1],最大数量为 1。 - 窗口扩大: 添加
2,窗口为[1, 2],最大数量为 2。 - 窗口扩大: 添加
1,窗口为[1, 2, 1],最大数量为 3。 - 窗口扩大: 添加
2,窗口为[1, 2, 1, 2],最大数量为 4。 - 窗口扩大: 添加
3,窗口为[1, 2, 1, 2, 3]。现在窗口中有三种水果,需要缩小窗口。 - 缩小窗口: 移除窗口左边的
1,窗口变为[2, 1, 2, 3]。依然有三种水果,继续缩小。 - 缩小窗口: 移除窗口左边的
2,窗口变为[1, 2, 3]。现在窗口中有两种水果,最大数量更新为 3。
最终,最大收集的水果数量为 4(在添加第四个水果之前)。
初始设置
basket = {}: 用来存储水果的类型和数量。start = 0: 窗口的开始位置。maxFruits = 0: 可以收集的最大水果数。fruits = [1, 2, 1, 2, 3]: 待处理的水果数组。
迭代过程
-
第一次迭代 (
end = 0):fruit = fruits[0] = 1basket = {1: 1}maxFruits = Math.max(0, 0 - 0 + 1) = 1
-
第二次迭代 (
end = 1):fruit = fruits[1] = 2basket = {1: 1, 2: 1}maxFruits = Math.max(1, 1 - 0 + 1) = 2
-
第三次迭代 (
end = 2):fruit = fruits[2] = 1basket = {1: 2, 2: 1}maxFruits = Math.max(2, 2 - 0 + 1) = 3
-
第四次迭代 (
end = 3):fruit = fruits[3] = 2basket = {1: 2, 2: 2}maxFruits = Math.max(3, 3 - 0 + 1) = 4
-
第五次迭代 (
end = 4):fruit = fruits[4] = 3basket = {1: 2, 2: 2, 3: 1}- 现在
basket中有三种水果。需要移动start来删除一种水果。 - 移动
start,basket = {1: 1, 2: 2, 3: 1} - 继续移动
start,basket = {2: 2, 3: 1} maxFruits = Math.max(4, 4 - 1 + 1) = 4
结果
函数最终返回 maxFruits = 4。这意味着最长的符合规则的连续子数组是 [1, 2, 1, 2],其长度为 4。
三、最小覆盖子串
76. 最小覆盖子串 - 力扣(LeetCode)
辅助笨蛋美女理解的例子
初始设置
s = "ADOBECODEBANC"t = "ABC"- 初始化窗口的左右指针:
l = 0, r = 0 - 窗口中符合条件的字符数量:
formed = 0 - 记录
t中字符出现频率的哈希表tFreq - 记录窗口中字符出现频率的哈希表
windowCounts - 最小子串的长度:
minLength = Infinity - 最小子串的起始索引:
minLeft = 0
迭代过程
-
初始化
tFreq:tFreq = {'A': 1, 'B': 1, 'C': 1}
-
第一次迭代 (
r = 0):- 当前字符
s[r] = 'A' windowCounts = {'A': 1}formed = 1(因为 'A' 是t的一部分)r++
- 当前字符
-
第二次迭代 (
r = 1):- 当前字符
s[r] = 'D' windowCounts = {'A': 1, 'D': 1}formed保持不变r++
- 当前字符
-
第三次迭代 (
r = 2):- 当前字符
s[r] = 'O' windowCounts = {'A': 1, 'D': 1, 'O': 1}formed保持不变r++
- 当前字符
-
第四次迭代 (
r = 3):- 当前字符
s[r] = 'B' windowCounts = {'A': 1, 'D': 1, 'O': 1, 'B': 1}formed = 2(因为 'B' 是t的一部分)r++
- 当前字符
-
第五次迭代 (
r = 4):- 当前字符
s[r] = 'E' windowCounts = {'A': 1, 'D': 1, 'O': 1, 'B': 1, 'E': 1}formed保持不变r++
- 当前字符
-
第六次迭代 (
r = 5):- 当前字符
s[r] = 'C' windowCounts = {'A': 1, 'D': 1, 'O': 1, 'B': 1, 'E': 1, 'C': 1}formed = 3('C' 是t的一部分,现在窗口包含所有t的字符)- 开始尝试缩小窗口 (
l开始移动):- 移动
l直到formed减少 l停在3('B' 处),此时窗口是"ADOBEC",长度为6minLength = 6, minLeft = 0
- 移动
- 当前字符
-
继续迭代:
- 继续移动
r和l,寻找更小的覆盖子串 - 每当
formed达到3时,尝试缩小窗口并更新minLength和minLeft
- 继续移动
结果
- 经过迭代,找到最小覆盖子串
"BANC"(长度为4,起始索引为9) - 函数返回
"BANC"
每次迭代都会检查当前窗口是否包含了 t 中的所有字符。
美女不懂的地方,反思理解到了
缩小窗口的具体步骤
假设我们有字符串 s = "ADOBECODEBANC" 和 t = "ABC",并且我们已经通过向右移动右指针 r 扩展了窗口,直到它包含了 t 中的所有字符。目前的窗口是 "ADOBEC"(从索引 0 到 5)。
-
缩小窗口:
- 我们现在尝试通过向右移动左指针
l来缩小窗口。每移动一次l,窗口中包含的字符数量就会相应减少。
- 我们现在尝试通过向右移动左指针
-
维护
formed条件:formed变量用来跟踪窗口中是否包含了t中的所有字符。- 在这个例子中,当窗口包含
A,B,C各至少一次时,formed条件为满足状态。
-
移动
l直到formed减少:- 开始逐个移除窗口左侧的字符,并检查每次移除后窗口是否仍然满足
formed条件。 - 在这个例子中,当左指针
l移到3的位置时(即字符B),窗口为"BEC"(去掉了ADO)。这时,窗口不再包含t中的所有字符(A被移除了),所以formed条件不再满足。
- 开始逐个移除窗口左侧的字符,并检查每次移除后窗口是否仍然满足
-
更新最小子串信息:
- 在移动
l之前,如果当前窗口满足条件,我们检查它是否比之前记录的最小窗口更小。 - 在这个例子中,窗口
"ADOBEC"的长度为6,这是目前找到的满足条件的最小窗口。因此,minLength更新为6,minLeft更新为窗口的起始索引0。
- 在移动
通过以上步骤,我们找到了一个满足条件的子串 "ADOBEC"。但算法不会停止,它会继续尝试找到可能存在的更小的满足条件的子串,直到遍历完整个字符串 s。最终的答案在这个例子中是 "BANC"。