2026-07-01:找出最小平衡下标。用go语言,给你一个整数数组 nums。我们要找“平衡下标”里最小的那个下标 i。 对任意下标 i,把数组分成左右两部分

26 阅读5分钟

2026-07-01:找出最小平衡下标。用go语言,给你一个整数数组 nums。我们要找“平衡下标”里最小的那个下标 i。

对任意下标 i,把数组分成左右两部分:

  • i 左边的所有元素求和,记为 leftSum(如果左边没有元素,那么 leftSum = 0)。

  • i 右边的所有元素求乘积,记为 rightProd(如果右边没有元素,那么 rightProd = 1)。

如果满足 leftSum == rightProd,则这个 i 就称为“平衡下标”。

最后返回所有满足条件的下标中最小的那个;如果一个都找不到,返回 -1。

1 <= nums.length <= 100000。

1 <= nums[i] <= 1000000000。

输入: nums = [2,1,2]。

输出: 1。

解释:

对于下标 i = 1:

左侧总和 = nums[0] = 2

右侧乘积 = nums[2] = 2

由于左侧总和等于右侧乘积,下标 1 是平衡下标。

没有更小的下标满足条件,因此答案是 1。

题目来自力扣3862。

一、分步详细执行流程

数组长度=3,初始状态: sum=0,mul=1,l=0,r=2 循环条件:l < r,只要左右指针没相遇就持续迭代。

第1轮循环:l=0,r=2,l<r成立

比较 sum(0)mul(1)0 < 1,走左侧累加分支

  1. nums[l]=nums[0]=2 加到 sum:sum = 0 + 2 = 2
  2. 左指针右移:l = 0 + 1 = 1 本轮结束状态:sum=2,mul=1,l=1,r=2

第2轮循环:l=1,r=2,l<r成立

比较 sum(2)mul(1)2 > 1,走右侧累乘分支

  1. 溢出预判:检查 mul * nums[r] 是否超 1e14。当前 mul=1,nums[r]=nums[2]=2,1*2=2 < 1e14,无溢出风险
  2. 右侧乘积更新:mul = 1 * 2 = 2
  3. 右指针左移:r = 2 - 1 = 1 本轮结束状态:sum=2,mul=2,l=1,r=1

循环终止判断

此时 l=1、r=1,l < r 条件不成立,跳出循环。

循环后校验平衡条件

判断 sum == mul:2 == 2,条件成立,返回当前 l 值 1,和题目输出一致。

二、通用场景完整逻辑拆解(不局限示例)

步骤1:初始化变量

  1. 求和变量 sum 初始为0:代表分割点左侧无元素时的初始和;
  2. 乘积变量 mul 初始为1:代表分割点右侧无元素时的初始乘积;
  3. 左指针 l 从数组最左端0开始;
  4. 右指针 r 从数组最后一个下标 len(nums)-1 开始。

步骤2:双指针收缩主循环(l < r 持续执行)

每次循环分两种分支判断:

分支A:sum < mul,扩充左侧区间

说明当前分割点偏右,需要把左边下一个元素纳入左侧求和区间:

  1. 左指针指向的元素 nums[l] 累加进 sum;
  2. 左指针 l 向右移动一位,代表候选平衡下标右移一位。

分支B:sum ≥ mul,缩减右侧区间

说明当前分割点偏左,需要把右边下一个元素纳入右侧求积区间:

  1. 溢出校验:判断 mul > 1e14 / nums[r]。若成立,代表 mul * nums[r] 会超出设定上限,乘积会无限变大不可能和有限的sum相等,直接返回-1结束程序;
  2. 无溢出则将 nums[r] 乘入 mul;
  3. 右指针 r 向左移动一位。

循环终止条件

l 和 r 指针重合时停止循环,此时重合位置 l 就是唯一候选平衡下标。 原理:双指针不断收拢区间,最终重合点恰好对应:左边全部是 [0, l-1](累加和sum)、右边全部是 [l+1, end](累乘积mul),完美匹配平衡下标i=l的左右计算规则。

步骤3:循环结束后校验平衡条件

  1. 若 sum(i=l左侧总和)等于 mul(i=l右侧乘积),说明 l 是合法平衡下标,直接返回 l;
  2. 若两者不相等,不存在任何平衡下标,返回 -1。

特殊兜底逻辑说明

当右侧累乘即将溢出 1e14 时直接返回-1: 数组元素最小为1,一旦乘积突破1e14,后续只会越乘越大,而左侧求和是线性增长、增速远慢于乘积,二者永远无法相等,提前剪枝避免无效循环与数值溢出崩溃。

三、时间复杂度分析

整个算法只有一层 while 循环,左指针 l 只向右移动、右指针 r 只向左移动,两个指针合计最多遍历数组全部元素一次,每个元素只会被 l 或 r 访问恰好1次。 数组长度记为 n(1 ≤ n ≤ 1e5):

  • 总操作次数 ≤ n
  • 时间复杂度:O(n)

四、额外空间复杂度分析

算法仅定义固定数量临时变量:sum、mul、l、r,没有开辟任何随数组长度n变化的数组、切片、哈希表等存储空间,额外空间与输入规模无关。

  • 额外空间复杂度:O(1)(常数级空间)

Go完整代码如下:

package main

import (
	"fmt"
)

func smallestBalancedIndex(nums []int) int {
	sum, mul := 0, 1
	l, r := 0, len(nums)-1
	for l < r {
		if sum < mul {
			sum += nums[l]
			l++
		} else {
			if mul > 1e14/nums[r] {
				return -1
			}
			mul *= nums[r]
			r--
		}
	}
	if sum == mul {
		return l
	}
	return -1
}

func main() {
	nums := []int{2, 1, 2}
	result := smallestBalancedIndex(nums)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

# -*-coding:utf-8-*-

from typing import List

def smallest_balanced_index(nums: List[int]) -> int:
    sum_val = 0
    mul_val = 1
    l, r = 0, len(nums) - 1
    
    while l < r:
        if sum_val < mul_val:
            sum_val += nums[l]
            l += 1
        else:
            # 检查乘法是否会溢出
            if mul_val > 10**14 // nums[r]:
                return -1
            mul_val *= nums[r]
            r -= 1
    
    if sum_val == mul_val:
        return l
    return -1


if __name__ == "__main__":
    nums = [2, 1, 2]
    result = smallest_balanced_index(nums)
    print(result)

在这里插入图片描述

C++完整代码如下:

#include <iostream>
#include <vector>
using namespace std;

int smallestBalancedIndex(vector<int>& nums) {
    long long sum = 0;
    long long mul = 1;
    int l = 0, r = nums.size() - 1;

    while (l < r) {
        if (sum < mul) {
            sum += nums[l];
            l++;
        } else {
            // 检查乘法是否会溢出
            if (mul > 100000000000000LL / nums[r]) {
                return -1;
            }
            mul *= nums[r];
            r--;
        }
    }

    if (sum == mul) {
        return l;
    }
    return -1;
}

int main() {
    vector<int> nums = {2, 1, 2};
    int result = smallestBalancedIndex(nums);
    cout << result << endl;
    return 0;
}

在这里插入图片描述