青训营X豆包MarsCode 农场采摘水果问题代码笔记 | 豆包MarsCode AI刷题

74 阅读5分钟

原题: image.png

思路: 根据题目小U有两个分别只能装一种类型的水果的篮子,想到可以用滑动窗口的方法来解决 滑动窗口是一种常用的算法技术,特别适用于解决子数组或子字符串相关的问题。在这个问题中,我们可以使用滑动窗口来找到最多包含两种不同水果的最长子数组。

解题思路

  1. 初始化变量

    • left 和 right 指针,用于表示当前窗口的左右边界。
    • max_length 用于记录当前找到的最大子数组长度。
    • fruit_count 用于记录当前窗口中每种水果的数量。
  2. 滑动窗口

    • 使用 right 指针遍历数组 fruits
    • 将 fruits[right] 加入当前窗口,并更新 fruit_count
    • 如果当前窗口中的水果种类超过两种,移动 left 指针,直到窗口中只有两种水果。
    • 更新 max_length
  3. 返回结果

    • 返回 max_length,即小U最多可以采摘的水果数量。

具体步骤

  1. 初始化

    • left 和 right 指针初始化为0。
    • max_length 初始化为0。
    • fruit_count 是一个字典,用于记录当前窗口中每种水果的数量。
  2. 滑动窗口

    • 遍历 fruits 数组,将当前水果加入 fruit_count
    • 如果 fruit_count 中的水果种类超过两种,移动 left 指针,直到窗口中只有两种水果。
    • 更新 max_length,记录当前窗口的最大长度。
  3. 返回结果

    • 返回 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

方法二:双指针 + 哈希表

这种方法与滑动窗口类似,但实现细节略有不同。我们可以使用两个指针 ij 来表示当前窗口的左右边界,并使用一个哈希表来记录窗口内每种水果的出现次数。

实现步骤

  1. 初始化

    • 定义两个指针 ij,初始都指向数组的开始位置。
    • 定义一个变量 max_length,用于记录可以摘取的最大水果数量。
    • 定义一个字典 fruit_count,用于统计当前窗口内各种水果的数量。
  2. 遍历数组

    • 使用一个 while 循环,当 j 没有到达数组末尾时继续执行。
    • 将当前水果 fruits[j] 加入到窗口中,并更新 fruit_count
    • 如果窗口内的水果种类超过两种,移动左指针 i,并更新 fruit_count,直到窗口内的水果种类不超过两种。
    • 更新 max_length
  3. 返回结果

    • 循环结束后,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

方法三:动态规划

虽然滑动窗口方法已经足够高效,但我们也可以尝试使用动态规划来解决这个问题。动态规划通常用于解决具有重叠子问题和最优子结构的问题。

实现步骤

  1. 初始化

    • 定义一个二维数组 dp,其中 dp[i][j] 表示以第 i 个水果结尾且包含 j 种水果的最大长度。
    • 初始化 dp 数组。
  2. 状态转移

    • 遍历数组,对于每个水果 fruits[i],更新 dp 数组。
    • 如果当前水果与前一个水果相同,则 dp[i][j] = dp[i-1][j] + 1
    • 如果当前水果与前一个水果不同,则需要考虑是否引入新的水果种类。
  3. 返回结果

    • 最终结果为 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

方法四:暴力法

虽然暴力法不是最高效的,但它可以帮助我们理解问题的基本逻辑。暴力法通过枚举所有可能的子数组,找到符合条件的最长子数组。

实现步骤

  1. 初始化

    • 定义一个变量 max_length,用于记录可以摘取的最大水果数量。
  2. 枚举所有子数组

    • 使用两层嵌套的 for 循环,枚举所有可能的子数组。
    • 对于每个子数组,使用一个哈希表来记录水果的种类和数量。
    • 如果子数组中的水果种类不超过两种,更新 max_length
  3. 返回结果

    • 最终结果为 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),适用于小规模数据集,便于理解和调试。

根据实际需求和数据规模,选择合适的方法来解决问题。滑动窗口方法通常是最佳选择,因为它既高效又易于实现。