Leetcode 683. K 个空花盆 (滑动窗口 或者 线段树)

Leetcode 683. K 个空花盆

花园里有 N 个花盆,每个花盆里都有一朵花。这 N 朵花会在 N 天内依次开放,每天有且仅有一朵花会开放并且会一直盛开下去。

给定一个数组 flowers 包含从 1 到 N 的数字,每个数字表示在那一天开放的花所在的花盆编号。

例如, flowers[i] = x 表示在第 i 天盛开的花在第 x 个花盆中,i 和 x 都在 1 到 N 的范围内。

给你一个整数 k,请你输出在哪一天恰好有两朵盛开的花,他们中间间隔了 k 朵花并且都没有开放。

如果不存在,输出 -1。


样例 1:

输入:
flowers: [1,3,2]
k: 1
输出: 2
解释: 在第二天,第一朵和第三朵花都盛开了。

样例 2:

输入:
flowers: [1,2,3]
k: 1
输出: -1

注释 :
给定的数组范围是 [1, 20000]。


解题思路1 (滑动窗口)

用DAYS数组保存第i个花盆开花的时间。保持left花盆和right花盆中间有k个花盆,

如果left和right之间的每一个花盆的开花时间都大于left花盆的开花时间同时大于right 花盆的开花时间,则这个left right花盆pair是满足条件的。 其中较晚开花的那个花盆的时间即为一个满足条件的答案。总体上我们应该返回满足这个条件的一个最小的解。

由于我们需要一个最小的解,所以对于满足条件时,我们可以直接更新left=right,right=left+K+1。 因为就算之前left~right之间的有满足条件的区间,但是其结果一定会比当前的结果大。

如果left 和 right之间有一个花盆i 其开花时间小于二者任意一个,说明left 和right不可能满足,同时left~i之间的花盆开花的时间都大于left,right花盆,所以都不能是下一步left的开始,(因为中间必定还是包含这个当前的i,所以会不满足条件。) 于是可以直接更新left=i,right=left+K+1

整个滑动窗口只会在n个数上滑动一次,所以时间复杂度是O(N)

代码

class Solution:
    def kEmptySlots(self, bulbs: List[int], K: int) -> int:
        
        days=[0]*(len(bulbs)+1) # 第i个花盆开花的时间
        
        for i,b_num in enumerate(bulbs):
            cur_day=i+1
            days[b_num]=cur_day

        result=-1
        left=1
        right=left+K+1
        # print(days)
        while right<len(days):
            flag=True
            for i in range(left+1,right):
                if days[i]>days[left] and days[i]>days[right]:
                    continue
                else:
                    flag=False
                    left=i
                    right=left+K+1
                    break
            if flag:
                cur_result=max(days[left],days[right])
                result=min(result,cur_result) if result!=-1 else cur_result
                left=right
                right=left+K+1
        return result

解题思路2 (线段树)

思路为线段树,记录N个花盆的开花情况,然后统计区间里开花的花盆数

对于第i天开花的b_num号花盆,则将区间b_num~b_num和置位1,如果满足以下两个条件中的一个,则可以返回当前的i作为结果:

1.区间b_num-K ~ b_num和为1,且区间 b_num-K-1 ~ b_num的和为2 (满足条件,且之前开的花在左边)
2.区间b_num ~ b_num+K 和为1,且区间b_num ~ b_num+k+1 和为2 (满足条件,且之前开的花在有右边)
如果遍历了所有天都不满足情况,则返回-1

由于更新线段树和区间查询的时间复杂度都是O(log(n)) 所以总体时间复杂度是O(nlog(n))

class Solution:
    def kEmptySlots(self, bulbs: List[int], K: int) -> int:
        N=len(bulbs)
        self.tree=[[0,0,0] for i in range(N*4)] # [0,0,0]表示一个tree node,三个数分别表示区间左边l  区间右边r 以及区间l~r的和, 初始化为全0
        self.build_tree(1,N,0)  # 构建线段树
        for i,b_num in enumerate(bulbs):
            cur_day=i+1
            self.update_tree(b_num,0)
            left=b_num-K-1
            right=b_num+K+1
            if left>=1: # 先判断左边的K的花盆
                if self.query_tree(left+1,b_num,0)==1 and self.query_tree(left,b_num,0)==2 :
                    return cur_day
            if right<=N:
                if self.query_tree(b_num,right-1,0)==1 and self.query_tree(b_num,right,0)==2:
                    return cur_day
        return -1


    
    def build_tree(self,l,r,i): #i从0开始,左右子树下标分别是2i+1 和 2i+2
        if l==r:
            self.tree[i][0]=l
            self.tree[i][1]=r
            self.tree[i][2]=0
            return
        mid=(l+r)//2
        self.build_tree(l,mid,i*2+1)
        self.build_tree(mid+1,r,i*2+2)
        self.tree[i][2]=self.tree[i*2+1][2]+self.tree[i*2+2][2]
        self.tree[i][0]=l
        self.tree[i][1]=r
        return
    
    def update_tree(self,index,i):
        l,r,v=self.tree[i]
        if l==r:
            self.tree[i][2]+=1
            return
        
        mid=(l+r)//2
        if index<=mid:
            self.update_tree(index,i*2+1)
        else:
            self.update_tree(index,i*2+2)
        self.tree[i][2]=self.tree[i*2+1][2]+self.tree[i*2+2][2]
        return
    
    def query_tree(self,left,right,i):
        # print(i)
        l,r,v=self.tree[i]
        if left==l and right==r:
            return self.tree[i][2]
        
        mid=(l+r)//2
        if left>=mid+1:
            return self.query_tree(left,right,i*2+2)
        if right<=mid:
            return self.query_tree(left,right,i*2+1)
        cur_sum=0
        cur_sum+=self.query_tree(left,mid,i*2+1)
        cur_sum+=self.query_tree(mid+1,right,i*2+2)
        return cur_sum
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值