农场采摘水果问题 | 豆包MarsCode AI刷题

3 阅读6分钟

农场采摘水果问题

一、问题分析

在这道题目中,我们给定了一个整数数组 fruits,它表示一排果树上每棵树上水果的类型。小U有两个篮子,每个篮子只能装一种水果类型,并且每个篮子可以装无限量的水果。我们的目标是帮助小U从任意一棵树开始,最多采摘多少个水果,而在采摘的过程中,只能从两种水果类型的篮子中采摘。

问题的难点在于,水果树的排列是顺序的,并且小U只能携带两种类型的水果。即使他能够从一棵树开始采摘,若遇到第三种水果类型,便必须停止采摘。由于每棵树上的水果类型是有限的,我们需要找到一种高效的方法来计算小U能够采摘的最大水果数量,确保时间复杂度尽可能低。

为了求解这个问题,我们可以利用滑动窗口的技术。滑动窗口是一种在数组或字符串中寻找满足特定条件的子序列的常见方法,能够在保证效率的前提下动态调整窗口的范围,从而解决问题。通过维护一个窗口,确保窗口内的水果种类不超过两种,我们可以实现对题目要求的求解。

二、思路解析

本题的核心是维护一个窗口,其中包含不超过两种不同类型的水果。我们可以使用一个哈希表来记录窗口内每种水果类型的数量,并且通过两个指针 ij 来动态调整窗口的范围。j 代表窗口的右边界,用于扩展窗口,而 i 代表窗口的左边界,用于收缩窗口。

在遍历过程中,我们不断扩展右边界 j,将水果类型加入当前窗口。当窗口内的水果种类超过两种时,我们通过移动左边界 i 来收缩窗口,直到窗口内只剩下两种水果。每次调整窗口时,我们都会计算当前窗口的长度并更新最大值,从而确保我们找到最大连续子数组的长度。

具体的实现步骤是:首先初始化一个空的哈希表 mymap,用来记录窗口内每种水果的数量;然后利用 j 逐步扩大窗口,同时更新水果的计数。如果窗口内的水果种类超过两种,我们就逐步移动左边界 i,直到窗口符合要求。最后,通过计算每次有效窗口的长度,得出最大可采摘水果的数量。

三、代码详解

def solution(fruits):
    n = len(fruits)  # 获取水果树的数量
    mymap = {}  # 用于记录窗口内不同水果类型的数量
    i = 0  # 窗口的左边界
    max_length = 0  # 最大的采摘数量

    for j in range(n):  # 遍历每棵树,右边界
        if fruits[j] in mymap:
            mymap[fruits[j]] += 1  # 当前水果类型计数加一
        else:
            mymap[fruits[j]] = 1  # 新的水果类型,计数初始化为1

        while len(mymap) > 2:  # 如果窗口内水果种类超过两种
            if mymap[fruits[i]] > 1:
                mymap[fruits[i]] -= 1  # 左边界水果数量减少
            else:
                del mymap[fruits[i]]  # 如果某种水果数量为0,则从窗口中删除
            i += 1  # 收缩窗口,左指针右移

        max_length = max(max_length, j - i + 1)  # 更新最大采摘数量

    return max_length  # 返回最大值

if __name__ == '__main__':
    print(solution([1, 2, 1, 2]) == 4)  # 输出 4
    print(solution([2, 0, 1, 2, 2]) == 3)  # 输出 3
    print(solution([1, 2, 3, 2, 2, 4]) == 4)  # 输出 4

代码的核心逻辑是利用滑动窗口来解决问题。首先,我们定义了一个哈希表 mymap 来记录窗口中每种水果的数量,ij 分别作为窗口的左右边界,max_length 用于记录当前能采摘的最大水果数量。我们遍历数组 fruits,使用 j 指针逐步扩展窗口,代表当前窗口的右边界。当遇到一个新的水果时,如果该水果类型已经在 mymap 中,我们就将该水果的计数加一;如果该水果类型不在 mymap 中,就将其添加并设置计数为 1。随着 j 的移动,窗口逐渐扩大。每当窗口内的水果种类超过两种时,我们进入一个 while 循环,开始收缩窗口,即通过移动左边界 i 来减小窗口大小,直到窗口内的水果种类数不超过两种。如果窗口内的某种水果类型的数量减少到 0,我们会从 mymap 中删除该水果类型,从而保证窗口内始终只包含最多两种水果。每次更新窗口时,我们计算当前窗口的长度 j - i + 1,并与 max_length 进行比较,保留较大的值,从而得到最大采摘数量。最后,返回 max_length,即小U能够采摘的最大水果数量。这样,滑动窗口技术能够高效地解决这个问题,确保时间复杂度为 O(n),其中 n 为 fruits 数组的长度,因为每个元素仅会被访问两次(一次被右指针 j 访问,另一次被左指针 i 访问)。

四、总结

这道题目通过滑动窗口的方式巧妙地解决了在一个数组中寻找符合特定条件的最长子数组问题。通过使用哈希表来动态记录窗口内不同水果类型的数量,并通过左右指针的调整来扩展或收缩窗口,确保窗口内只包含两种类型的水果,从而能够最大化采摘的水果数量。滑动窗口技术使得我们可以在 O(n) 的时间复杂度内高效求解问题。

该问题不仅考察了滑动窗口的应用,还需要灵活运用哈希表来辅助统计每种水果的数量。通过不断地调整窗口范围,我们可以得到满足题意的最长连续子数组,这种方法比暴力枚举法更为高效,避免了重复计算,节省了时间和空间复杂度。

总的来说,滑动窗口算法是解决这类“最长子数组”问题的经典方法,能够有效处理动态变化的子数组问题。通过不断调整窗口的大小,可以保证算法的时间复杂度不会过高,适用于实际开发中大量涉及子数组问题的场景。