leetcode 1658. 将 x 减到 0 的最小操作数[python3 双指针实现与思路整理]

题目

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。

示例 1:

  • 输入:nums = [1,1,4,2,3], x = 5
  • 输出:2
  • 解释:最佳解决方案是移除后两个元素,将 x 减到 0 。

题解

这个题目很好理解,就是删除nums的前缀与后缀数,使前缀与后缀的和为x。由于可能会有多种情况,因此选择一个数目最小的可能。以示例来说nums列表要想满足要求,有两种方法,一种是前缀为[1,1]后缀为[3],一种是前缀为空,后缀为[2,3]。显然后缀为[2,3]的方法只需要删除两个数,因此结果显然是方法2满足要求。
如下图所示。
image.png

显然该题目的核心是确定前缀与后缀的边界,这显然可以使用双指针来示解。现在又面临一个问题,双指针是从两边到中间遍历还是从一侧遍历。这里从两侧往中间遍历无法确定在lsum与rsum之后小于x的时候到底是走左侧指针还是右侧指针。这里有一个技巧,前缀指针从-1开始也表示lsum开始为0,后缀是初始为整个数组从0开始rsum为整个数据的和。那么如果确定前缀与后缀指针的执行过程呢。有以下三种情况:

  • 情况1: 如果 lsum+rsum=x,这是一组满足答案的解,可以与之前已有的最小解进行对比;
  • 情况2:如果 lsum+rsum>x说明和过大,需要将后缀指针right向右移动一个位置;
  • 情况3:如果 lsum+rsum<x说明和过小,需要将 left前缀指针向右移动一个位置。
    那么我们其实可以直接写代码了:
class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        n=len(nums)
        total=sum(nums)
        #特殊情况1:如果nums之后小于x则肯定返回-1
        if total < x:
            return -1
        #特殊情况2:nums之和正好为x,返回nums的长度
        if total==x:
            return n
        #特殊情况3:如果nums所有元素均大于x则返回-1
        flag=0
        for num in nums:
            if num<=x:
                flag=1
                break
        if flag==0:
            return -1
        
        #滑动窗口
        right=0
        lsum=0
        rsum=total
        #初始结果长度取n+1
        ans=n+1
        #left滑动遍历,这就是直接第三种情况lsum+rsum<x,left右移动
        for left in range(-1,n-1):
            if left!=-1:
                lsum+=nums[left]
            #情况2如果lsum+rsum>x,right右移
            while right<n and lsum+rsum>x:
                #右移一位
                rsum-=nums[right]
                right+=1
            #情况1:
            if lsum+rsum==x:
                ans=min(ans,(left+1)+(n-right))
        return -1 if ans>n else ans

计算复杂性

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n是数组 n u m s nums nums的长度。 l e f t left left r i g h t right right均最多遍历整个数组一次。
  • 空间复杂度: O ( 1 ) O(1) O(1)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值