原题:
思路: 根据题目小U有两个分别只能装一种类型的水果的篮子,想到可以用滑动窗口的方法来解决 滑动窗口是一种常用的算法技术,特别适用于解决子数组或子字符串相关的问题。在这个问题中,我们可以使用滑动窗口来找到最多包含两种不同水果的最长子数组。
解题思路
-
初始化变量:
left和right指针,用于表示当前窗口的左右边界。max_length用于记录当前找到的最大子数组长度。fruit_count用于记录当前窗口中每种水果的数量。
-
滑动窗口:
- 使用
right指针遍历数组fruits。 - 将
fruits[right]加入当前窗口,并更新fruit_count。 - 如果当前窗口中的水果种类超过两种,移动
left指针,直到窗口中只有两种水果。 - 更新
max_length。
- 使用
-
返回结果:
- 返回
max_length,即小U最多可以采摘的水果数量。
- 返回
具体步骤
-
初始化:
left和right指针初始化为0。max_length初始化为0。fruit_count是一个字典,用于记录当前窗口中每种水果的数量。
-
滑动窗口:
- 遍历
fruits数组,将当前水果加入fruit_count。 - 如果
fruit_count中的水果种类超过两种,移动left指针,直到窗口中只有两种水果。 - 更新
max_length,记录当前窗口的最大长度。
- 遍历
-
返回结果:
- 返回
max_length,即小U最多可以采摘的水果数量。 解答代码:
- 返回
def solution(fruits: list) -> int: left = 0 max_length = 0 fruit_count = {}
for right in range(len(fruits)):
# 将当前水果加入窗口
fruit = fruits[right]
if fruit in fruit_count:
fruit_count[fruit] += 1
else:
fruit_count[fruit] = 1
# 如果窗口中的水果种类超过两种,移动左指针
while len(fruit_count) > 2:
# 从窗口中移除左边的水果
left_fruit = fruits[left]
fruit_count[left_fruit] -= 1
if fruit_count[left_fruit] == 0:
del fruit_count[left_fruit]
left += 1
# 更新最大长度
max_length = max(max_length, right - left + 1)
return max_length
方法二:双指针 + 哈希表
这种方法与滑动窗口类似,但实现细节略有不同。我们可以使用两个指针 i 和 j 来表示当前窗口的左右边界,并使用一个哈希表来记录窗口内每种水果的出现次数。
实现步骤
-
初始化:
- 定义两个指针
i和j,初始都指向数组的开始位置。 - 定义一个变量
max_length,用于记录可以摘取的最大水果数量。 - 定义一个字典
fruit_count,用于统计当前窗口内各种水果的数量。
- 定义两个指针
-
遍历数组:
- 使用一个
while循环,当j没有到达数组末尾时继续执行。 - 将当前水果
fruits[j]加入到窗口中,并更新fruit_count。 - 如果窗口内的水果种类超过两种,移动左指针
i,并更新fruit_count,直到窗口内的水果种类不超过两种。 - 更新
max_length。
- 使用一个
-
返回结果:
- 循环结束后,
max_length即为可以摘取的最大数量的水果。
- 循环结束后,
代码实现
def solution(fruits: list) -> int:
i = 0
max_length = 0
fruit_count = {}
for j in range(len(fruits)):
# 将当前水果加入窗口
fruit = fruits[j]
if fruit in fruit_count:
fruit_count[fruit] += 1
else:
fruit_count[fruit] = 1
# 如果窗口中的水果种类超过两种,移动左指针
while len(fruit_count) > 2:
# 从窗口中移除左边的水果
left_fruit = fruits[i]
fruit_count[left_fruit] -= 1
if fruit_count[left_fruit] == 0:
del fruit_count[left_fruit]
i += 1
# 更新最大长度
max_length = max(max_length, j - i + 1)
return max_length
方法三:动态规划
虽然滑动窗口方法已经足够高效,但我们也可以尝试使用动态规划来解决这个问题。动态规划通常用于解决具有重叠子问题和最优子结构的问题。
实现步骤
-
初始化:
- 定义一个二维数组
dp,其中dp[i][j]表示以第i个水果结尾且包含j种水果的最大长度。 - 初始化
dp数组。
- 定义一个二维数组
-
状态转移:
- 遍历数组,对于每个水果
fruits[i],更新dp数组。 - 如果当前水果与前一个水果相同,则
dp[i][j] = dp[i-1][j] + 1。 - 如果当前水果与前一个水果不同,则需要考虑是否引入新的水果种类。
- 遍历数组,对于每个水果
-
返回结果:
- 最终结果为
dp数组中的最大值。
- 最终结果为
代码实现
def solution(fruits: list) -> int:
n = len(fruits)
if n <= 2:
return n
dp = [[0] * 3 for _ in range(n)]
dp[0][1] = 1
max_length = 1
for i in range(1, n):
if fruits[i] == fruits[i-1]:
dp[i][1] = dp[i-1][1] + 1
dp[i][2] = dp[i-1][2] + 1
else:
dp[i][1] = 2
dp[i][2] = dp[i-1][1] + 1
max_length = max(max_length, dp[i][1], dp[i][2])
return max_length
方法四:暴力法
虽然暴力法不是最高效的,但它可以帮助我们理解问题的基本逻辑。暴力法通过枚举所有可能的子数组,找到符合条件的最长子数组。
实现步骤
-
初始化:
- 定义一个变量
max_length,用于记录可以摘取的最大水果数量。
- 定义一个变量
-
枚举所有子数组:
- 使用两层嵌套的
for循环,枚举所有可能的子数组。 - 对于每个子数组,使用一个哈希表来记录水果的种类和数量。
- 如果子数组中的水果种类不超过两种,更新
max_length。
- 使用两层嵌套的
-
返回结果:
- 最终结果为
max_length。
- 最终结果为
代码实现
def solution(fruits: list) -> int:
n = len(fruits)
max_length = 0
for i in range(n):
fruit_count = {}
for j in range(i, n):
fruit = fruits[j]
if fruit in fruit_count:
fruit_count[fruit] += 1
else:
fruit_count[fruit] = 1
if len(fruit_count) > 2:
break
max_length = max(max_length, j - i + 1)
return max_length
总结
- 滑动窗口:时间复杂度 O(n),空间复杂度 O(k)(k 为水果种类数),实现简单,效率高。
- 双指针 + 哈希表:与滑动窗口类似,但实现细节略有不同。
- 动态规划:适用于更复杂的子问题,但在这个问题中实现较为繁琐。
- 暴力法:时间复杂度 O(n^2),适用于小规模数据集,便于理解和调试。
根据实际需求和数据规模,选择合适的方法来解决问题。滑动窗口方法通常是最佳选择,因为它既高效又易于实现。