Leetcode 剑指 Offer II 012. 左右两边子数组的和相等

题目难度: 中等

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

给你一个整数数组 nums ,请计算数组的 中心下标 。

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

示例 1:

  • 输入:nums = [1,7,3,6,5,6]
  • 输出:3
  • 解释:
    • 中心下标是 3 。
    • 左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
    • 右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

示例 2:

  • 输入:nums = [1, 2, 3]
  • 输出:-1
  • 解释:数组中不存在满足此条件的中心下标。

示例 3:

  • 输入:nums = [2, 1, -1]
  • 输出:0
  • 解释:
    • 中心下标是 0 。
    • 左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
    • 右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。

提示:

  • 1 <= nums.length <= 10^4
  • -1000 <= nums[i] <= 1000

题目思考

  1. 如何优化时间和空间复杂度?

解决方案

思路
  • 分析题目, 最容易想到的做法就是两重循环: 外层循环固定每个下标, 然后从它的左右下标开始两个内层循环, 分别求出左右两侧的和, 相等的话作为最终结果
  • 但这种做法时间效率太低 (O(N^2)), 如何优化呢?
  • 重新分析题目, 左侧所有元素相加的和显然就是前缀和, 而右侧所有元素相加的和自然是后缀和
  • 我们可以利用这一点, 先正向遍历, 得出每个下标对应的前缀和, 再反向遍历一遍, 累加当前的后缀和, 如果某下标的后缀和等于其前缀和, 就说明找到一个有效下标, 继续遍历直到找到最左有效下标为止
  • 这样我们就把时间复杂度优化到了O(N)
  • 另外由于题目要求最左有效下标, 我们还可以调换两次遍历的顺序, 这样第二次正向遍历时, 得到的第一个有效下标即为最终结果, 进一步优化了时间
  • 这还没完, 我们可以进一步压缩成一次正向遍历搞定, 也不需要前缀和或后缀和数组
  • 具体做法是初始化前缀和为 0, 后缀和为整个数组的和
  • 然后正向遍历数组, 先将后缀和减去当前下标 i 的值, 此时前缀和就是[0,i)子数组的和, 而后缀和是[i+1,n)子数组的和, 即 i 的左侧和右侧元素的和
  • 此时如果前缀和以及后缀和相等, 则说明找到最左有效下标, 否则前缀和加上当前元素的值继续遍历
  • 这样进一步将空间复杂度优化到了O(1)
  • 下面代码中对上述每个步骤都有详细注释, 方便大家理解
复杂度
  • 时间复杂度 O(N): 只需要遍历数组一遍
  • 空间复杂度 O(1): 只使用了几个常数空间的变量
代码
class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        # 单次正向遍历+前/后缀和
        lsm = 0
        rsm = sum(nums)
        for i, x in enumerate(nums):
            # 后缀和减去当前元素
            rsm -= x
            if lsm == rsm:
                # [0,i)前缀和==[i+1,n)后缀和
                # 且当前是最靠近左边的下标, 返回它
                return i
            # 前缀和加上当前元素
            lsm += x
        # 没找到符合条件的下标, 返回-1
        return -1

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值