不会算法的小白不是好小白,可恶还有什么可以难倒我这个美女的,不做花瓶第一天!
209. 长度最小的子数组 - 力扣(LeetCode)
滑动窗口法:把数组的区间,假设成为两个指针,先后移动两个指针
我们先读懂题目,这个很重要,不过我现在读的不是很懂,没事美女有弱点可以理解
let transactions = [1, 2, 3, 4, 5];
目标是找到一个连续的子数组,其元素总和至少为 9
let target = 9;
let result = minSubArrayLen(target, transactions);
transactions = [1, 2, 3, 4, 5]
target = 9
start = 0, end = 0
sum = 0
ans = Infinity
第一轮迭代: end = 0
sum = 1
(1
)sum < target
(9
),所以 end++
。第二轮迭代: end = 1
sum = 3
(1 + 2
)sum < target
,所以 end++
。第三轮迭代: end = 2
sum = 6
(1 + 2 + 3
)sum < target
,所以 end++
。第四轮迭代: end = 3
sum = 10
(1 + 2 + 3 + 4
)sum >= target
:
Math.min(ans, end - start + 1)
-> Math.min(Infinity, 4 - 0 + 1)
-> ans = 4
sum -= nums[start]
,start++
-> sum = 9
(2 + 3 + 4
), start = 1
第五轮迭代: end = 3
sum = 9
(2 + 3 + 4
)sum >= target
:
Math.min(ans, end - start + 1)
-> Math.min(4, 4 - 1 + 1)
-> ans = 3
sum -= nums[start]
,start++
-> sum = 7
(3 + 4
), start = 2
接下来的迭代
end
继续增加,但不再找到总和大于等于 target
的更短子数组。ans
的值为 3
。3
,表示最短的满足条件的子数组长度是 3
(即 [2, 3, 4]
)。很好这个滑动窗口是这样理解了,但是不会活学活用,那么下面继续
904. 水果成篮 - 力扣(LeetCode)
很抽象我读不懂题目:找了一个外援理解了一下题目
人话:我们的目标是找到一个窗口,其中只包含两种类型的水果,并且这个窗口尽可能大
初始化: 定义两个变量来跟踪窗口的开始和结束位置。同时,使用一个数据结构(如哈希表)来跟踪窗口内不同水果的数量。
扩大窗口: 从左向右移动窗口的右边界,即不断添加新的水果到窗口中,同时更新哈希表。
满足条件的窗口: 当窗口中包含超过两种水果时,移动窗口的左边界以排除一种水果,直到窗口重新只包含两种水果。
记录结果: 在每次更新窗口时,如果窗口只包含两种水果,更新最大收集水果的数量。
重复直到结束: 继续扩大和缩小窗口,直到覆盖了整个数组。
假设 fruits = [1, 2, 1, 2, 3]
,我们可以按以下步骤使用滑动窗口法:
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] = 1
basket = {1: 1}
maxFruits = Math.max(0, 0 - 0 + 1) = 1
第二次迭代 (end = 1
):
fruit = fruits[1] = 2
basket = {1: 1, 2: 1}
maxFruits = Math.max(1, 1 - 0 + 1) = 2
第三次迭代 (end = 2
):
fruit = fruits[2] = 1
basket = {1: 2, 2: 1}
maxFruits = Math.max(2, 2 - 0 + 1) = 3
第四次迭代 (end = 3
):
fruit = fruits[3] = 2
basket = {1: 2, 2: 2}
maxFruits = Math.max(3, 3 - 0 + 1) = 4
第五次迭代 (end = 4
):
fruit = fruits[4] = 3
basket = {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"
,长度为 6
minLength = 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"
。