zoukankan      html  css  js  c++  java
  • 0904-leetcode算法实现之水果成篮-fruit-into-baskets-python&golang实现

    在一排树中,第 i 棵树产生 tree[i] 型的水果。
    你可以从你选择的任何树开始,然后重复执行以下步骤:

    把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
    移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
    请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1,然后执行步骤 2,依此类推,直至停止。

    你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。

    用这个程序你能收集的水果树的最大总量是多少?

    示例 1:

    输入:[1,2,1]
    输出:3
    解释:我们可以收集 [1,2,1]。
    示例 2:

    输入:[0,1,2,2]
    输出:3
    解释:我们可以收集 [1,2,2]
    如果我们从第一棵树开始,我们将只能收集到 [0, 1]。
    示例 3:

    输入:[1,2,3,2,2]
    输出:4
    解释:我们可以收集 [2,3,2,2]
    如果我们从第一棵树开始,我们将只能收集到 [1, 2]。
    示例 4:

    输入:[3,3,3,1,2,1,1,2,3,3,4]
    输出:5
    解释:我们可以收集 [1,2,1,1,2]
    如果我们从第一棵树或第八棵树开始,我们将只能收集到 4 棵水果树。

    提示:

    1 <= tree.length <= 40000
    0 <= tree[i] < tree.length

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/fruit-into-baskets

    python

    # 水果成篮
    class Solution:
        def totalFruit(self, tree: [int]) -> int:
            """
            滑动窗口+双指针+哈希表, 时间O(n), 空间O(1)
            我们来抽象一下,就是给你一个数组, 让你选定一个子数组,
            这个子数组最多只有两种数字,这个选定的子数组最大可以是多少。
            思路:
            借助辅助哈希存储窗口元素出现次数,滑动窗口的左右边界不断调整使得窗口内仅含2种不同元素值,一次遍历,即可得到滑动窗口的最大值
            详细流程:
            1.初始化窗口长度ans为0,左边界指针i指向0号元素,初始化哈希表win,对应值初始0
            2.从第一个元素开始遍历,作为滑动窗口的右边界指针j,不断移动右边界,即滑动窗口不懂右移的过程,j++
            3.检查当前右边界指针元素是否出现过,窗口中未含该元素,窗口可容纳元素种类k--
            4.不管右边界指针元素是否出现过,其在窗口中的出现次数++
            5.检查当前边界窗口的可容纳元素种数是否小于0(-1),如果小于0,即当前窗口含3种元素,需要不断移动左边界指针,使得最终窗口内元素种类小于等于2,即k>=0
                -上述检查通过,即左边界指针元素出现次数--
                -左边界指针元素出现次数需要检查是否减完,即窗口哈希win中对应值为0,此时意味窗口可容纳元素种类数++
                -向右移动左边界指针i++
            6.上述3/4/5步骤走完,即当前窗口的左边界指针已停止移动,符合题目元素种类数要求,求得当前窗口覆盖的子数组的长度j-i+1,与上轮或初始化的ans比较得到较大值作为新的ans
            7.重复2/3/4/5/6步骤,直到所有元素都遍历完,最后的ans即为题中要求的窗口长度最大值
            :param tree:
            :return:
            """
            def atMostK(k, nums):
                """
                滑动窗口取k个不同值元素的最长连续子数组长度
                :param k:
                :param nums:
                :return:
                """
                from collections import defaultdict
                i = ans = 0 # 初始化左指针或左边界,暂且叫左边界,初始化返回结果为0,即仅含2种元素的最长子数组长度
                win = defaultdict(lambda: 0) # 初始化哈希表,val=0,val表示出现次数
                for j in range(len(nums)): # 仅需遍历一轮数组
                    if win[nums[j]] == 0: # 每次遍历数组元素时,检查在哈希表中是否存在该元素,如果存在,必定val已经大于0,不存在,则必定是初始化为0的或者减为0
                        k -= 1 # 如果元素对应出现次数为0,意味窗口中加入了新值元素,对应窗口含有的元素种类减1
                    win[nums[j]] += 1 # 每次遍历元素的时候,都在对应的val上+1
                    while k < 0: # 遍历每个元素时,检查k是否小于0,说明窗口中可定含有2种以上不同值的元素(3种不同值的元素),此时应该考虑改变窗口边界,主要是移动窗口左边界,最终应该使得窗口内含有2种不同值元素,即k=0
                        win[nums[i]] -= 1 # 每次while循环,当前左边界指针元素对应的次数减1
                        if win[nums[i]] == 0: # 检查左边界指针对应元素出现次数是否为0,意味着3种不同值变为了2种,此时k由-1变为0,即k++
                            k += 1
                        i += 1 # 检查左边界指针对应次数不为0,意味着左边界应该右移,即i++,移动左边界指针时检查元素种类是否大于2(3),只要元素值种数为3,就移动左边界,直到k=0为止
                    ans = max(ans, j - i + 1) # 当前符合2种元素值的左右边界指针,取子数组长度及上轮窗口大值的较大值,作为当前遍历元素的大值
                return ans # 当所有元素遍历完,ans即为符合条件滑动窗口的最大值
    
            return atMostK(2, tree) # 水果成篮,给定一排树,最多含有2种水果的情况
    
        def totalFruit1(self, tree: [int]) -> int:
            """
            按块扫描,暴力法
            tip:
                0<tree[i]<len(tree)
                1<=len(tree)<=4000
            思路:
    
            :param tree:
            :return:
            """
            res = 0
            n = len(tree)
            for i in range(n): # 1层循环
                pre1, pre2 = -1, -1 # 每轮循环时,初始化-1
                for j in range(i, n): # 进入2层for loop
                    if pre1 == -1 or pre1 == tree[j]: # 如果pre1=-1,表示1层for loop重新开始遍历,或者当pre1等于当前元素的值(意味着遇到与前2个元素相同值的元素),进入if语句,赋值,continue,j++
                        pre1 = tree[j]
                        continue
                    elif pre2 == -1 or pre2 == tree[j]: # 如果pre2=-1,表示1层for loop重新开始遍历,或者当pre2等于当前元素的值(意味着遇到与前2个元素相同值的元素),进入if语句,赋值,continue,j++
                        pre2 = tree[j]
                        continue
                    else: # 如果当前元素不等于pre1或pre2,表示遇到了第三个不同元素的值,从水果成篮的角度,就有第三种水果,意味当前的2层for loop要break,break前获取到连续子数组长度大值
                        if tree[j] == pre1 or tree[j] == pre2:
                            continue
                        res = max(res, j-i) # 由于此时的j为第三个不同元素的值,直接j-i即为当前唯2元素的最长子数组长度
                        break # break后,开始下一轮循环
    
            return res
    
        def totalFruit2(self, tree: [int]) -> int:
            """
            暴力扫描,力扣超时间。时间O(n^2), 空间O(3n)->n轮,每轮3对k-v
            思路:
            分2层遍历,第一层遍历的元素作为符合条件的下界,第二层遍历的元素作为上界,借助哈希表存储不同值的元素,
            第二层遍历时,只要哈希表遇到第三种元素,break,每次新的下界更新时,哈希表初始化
            算法流程:
            1.数组长n,初始化元素种类k,初始化返回结果res 0
            2.一层便利遍历i,初始化哈希表
            3.二层遍历j,从i到n-1
            -对j对应元素出现次数++
            -检查哈希表长度是否大于K,是则记录此轮res max,继续下轮遍历
            -对于j已经是最后的元素,依然只有k种元素,记录此轮res max
            4.重复1、2、3步骤,返回res值
            :param tree:
            :return:
            """
            from collections import defaultdict
            n = len(tree)
            k = 2
            res = 0
            for i in range(n):
                subarray_map = defaultdict(int)
                for j in range(i, n):
                    subarray_map[tree[j]] += 1
                    if len(subarray_map) > k:
                        res = max(res, j-i)
                        break
                    if j == n-1:
                        res = max(res, j-i+1)
            return res
    
    
    
    
    if __name__ == "__main__":
        tree = [1,2,1,0,1,1,3,3]
        tree1 = [1,2,1]
        test = Solution()
        print(test.totalFruit2(tree))
    
    
    
    

    golang

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	nums := []int{1, 2, 1}
    	fmt.Println(totalFruit(nums))
    }
    
    // 滑动窗口
    func totalFruit(tree []int) int {
    	return atMostK(2, tree)
    }
    
    func atMostK(k int, nums []int) int {
    	i := 0
    	ans := 0
    	win := make(map[int]int)
    	for j := 0; j < len(nums); j++ {
    		if win[nums[j]] == 0 {
    			k--
    		}
    		win[nums[j]]++
    		for k < 0 {
    			win[nums[i]]--
    			if win[nums[i]] == 0 {
    				k++
    			}
    			i++
    		}
    		if ans > j-i+1 {
    			ans = ans
    		} else {
    			ans = j - i + 1
    		}
    	}
    	return ans
    }
    
  • 相关阅读:
    [翻译]ASP.NET MVC 3 开发的20个秘诀(六)[20 Recipes for Programming MVC 3]:找回忘记的密码
    No.9425 [翻译]开发之前设计先行
    [翻译]ASP.NET MVC 3 开发的20个秘诀(五)[20 Recipes for Programming MVC 3]:发送欢迎邮件
    内联(inline)函数与虚函数(virtual)的讨论
    内联函数、模板、头文件杂记(转)
    gdb使用整理
    data structure and algorithm analysis上一点摘录
    lsof by example 例子
    linux core文件机制
    转一篇shared_ptr的小文
  • 原文地址:https://www.cnblogs.com/davis12/p/15415604.html
Copyright © 2011-2022 走看看