Facebook Pixel

3862. Find the Smallest Balanced Index

LeetCode ↗

Problem Description

You are given an integer array nums.

An index i is called balanced if the sum of all elements located strictly to the left of index i is equal to the product of all elements located strictly to the right of index i.

There are two special cases to keep in mind:

  • If there are no elements to the left of index i (that is, i is the first index), then the sum is treated as 0.
  • If there are no elements to the right of index i (that is, i is the last index), then the product is treated as 1.

Your task is to find every index that satisfies this balanced condition, and return the smallest such index.

If there is no balanced index in the array, return -1.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Prefix Sums?

This problem maps to Prefix Sums through a short path in the full flowchart.

Sum oradditiveproblem?yesSubarrayslidingwindow?noPrefix Sums

Maintains running sum from left and product from right, scanning right-to-left to find where prefix sum equals suffix product.

Open in Flowchart

Intuition

The most direct idea is: for each index i, compute the sum of all elements to its left and the product of all elements to its right, then check whether they are equal. However, recomputing these values from scratch for every index would be wasteful.

The key observation is that as we move across the array, both the left sum and the right product can be maintained incrementally instead of being recalculated each time.

Notice an important difference between the two quantities. The left sum grows by adding elements, while the right product grows by multiplying elements. Products grow much faster than sums. Once the right product becomes large enough that it is greater than or equal to the remaining sum on the left, multiplying by more elements will only make it larger, so there is no chance for the two sides to become equal again. This gives us a natural opportunity to stop early.

To take advantage of this, we enumerate indices from right to left. We start by computing the total sum s of the whole array, and we keep a running product p of all elements seen so far on the right (initially 1, matching the rule that an empty right side has product 1).

When we arrive at index i:

  • First, we remove nums[i] from s, so that s now represents exactly the sum strictly to the left of i.
  • Then we compare s with p. If s == p, index i is balanced.
  • Finally, we fold nums[i] into the product p, since for the next index (further left) this element belongs to its right side.

Because we scan from right to left, the first balanced index we encounter is automatically the smallest one, so we can return it immediately. And by checking whether p has already grown past s, we can break out of the loop early to save unnecessary work.

Pattern Learn more about Prefix Sum patterns.

Solution Approach

Solution 1: Enumeration

We first compute the total sum s of all elements in the array. Then we enumerate each index i from right to left, maintaining a variable p to record the product of all elements to the right of index i. When we reach index i, we first subtract nums[i] from s, then check whether s equals p; if so, we return index i. Next, we multiply p by nums[i]. If p is greater than or equal to s, the product will only keep growing and no balanced index can be found afterwards, so we can terminate the enumeration early.

If no balanced index is found after the enumeration, we return -1.

Let's break down the implementation step by step:

  1. Initialize the state. Compute s = sum(nums) to represent the running sum, and set p = 1 to represent the product of the (currently empty) right side, matching the rule that an empty right side has product 1.

  2. Enumerate from right to left. We loop with i going from len(nums) - 1 down to 0:

    • Update the left sum. Execute s -= nums[i], so that s now holds the sum of all elements strictly to the left of index i.
    • Check the balanced condition. If s == p, then index i is balanced, and since we scan from right to left, it is guaranteed to be the smallest such index, so we return i directly.
    • Update the right product. Execute p *= nums[i], folding the current element into the product, because for the next index further to the left, nums[i] belongs to its right side.
    • Early termination. If p >= s, the product can only continue to grow as we move left, so it can never equal the remaining sum again. We break out of the loop to avoid unnecessary work.
  3. Return the result. If the loop completes without finding any balanced index, we return -1.

The time complexity is O(n), where n is the length of the array, since each element is visited at most once. The space complexity is O(1), as we only use a constant number of extra variables.

Example Walkthrough

Let's trace through the algorithm with a small example: nums = [2, 3, 1, 8, 2].

Initialization

  • Compute the total sum: s = 2 + 3 + 1 + 8 + 2 = 16.
  • Set the right product: p = 1 (the empty right side has product 1).

We now scan from right to left. At each index i, we first do s -= nums[i] so that s becomes the sum strictly to the left, then compare s with p, then fold nums[i] into p, and finally check for early termination.

i = 4 (nums[4] = 2)

  • Update left sum: s = 16 - 2 = 14.
  • Check balance: s = 14, p = 1 → not equal.
  • Update right product: p = 1 * 2 = 2.
  • Early termination check: p (2) >= s (14)? No, continue.

i = 3 (nums[3] = 8)

  • Update left sum: s = 14 - 8 = 6.
  • Check balance: s = 6, p = 2 → not equal.
  • Update right product: p = 2 * 8 = 16.
  • Early termination check: p (16) >= s (6)? Yes → break.

Since the product 16 has already overtaken the remaining left sum 6, multiplying by any further elements would only make p larger, so no balanced index can exist beyond this point.

Result

The loop ended without finding any index where s == p, so we return -1.


To see a successful case, consider nums = [1, 2, 3, 3]:

Stepinums[i]s (after subtract)p (before update)Balanced?p (after update)
start911
13361No3
22333Yes

At i = 2, the left sum (1 + 2 = 3) equals the right product (3), so index 2 is balanced. Because we scan from right to left, this is guaranteed to be the smallest balanced index, and we return 2 immediately.

Solution Implementation

1class Solution:
2    def smallestBalancedIndex(self, nums: list[int]) -> int:
3        # prefix_sum starts as the total sum; as we move left it becomes
4        # the sum of all elements strictly before the current index i.
5        prefix_sum = sum(nums)
6
7        # suffix_product accumulates the product of all elements strictly
8        # after the current index i; starts at 1 (empty product).
9        suffix_product = 1
10
11        # Iterate from the rightmost index toward the left, so the first
12        # match we find is the smallest qualifying index.
13        for i in range(len(nums) - 1, -1, -1):
14            # Remove nums[i] so prefix_sum now equals the sum of nums[0..i-1].
15            prefix_sum -= nums[i]
16
17            # Balanced index: prefix sum equals suffix product.
18            if prefix_sum == suffix_product:
19                return i
20
21            # Include nums[i] into the suffix product for the next (left) index.
22            suffix_product *= nums[i]
23
24            # Pruning: once the product reaches or exceeds the remaining sum,
25            # no smaller index can satisfy the equality, so stop early.
26            if suffix_product >= prefix_sum:
27                break
28
29        # No balanced index found.
30        return -1
31
1class Solution {
2    public int smallestBalancedIndex(int[] nums) {
3        // leftSum tracks the sum of elements; it starts as the total sum,
4        // then gets reduced so it represents the sum to the LEFT of index i.
5        long leftSum = 0;
6        // rightProduct accumulates the product of elements to the RIGHT of index i.
7        long rightProduct = 1;
8
9        // Compute the total sum of all elements first.
10        for (int value : nums) {
11            leftSum += value;
12        }
13
14        // Traverse from right to left so we can find the smallest valid index.
15        for (int i = nums.length - 1; i >= 0; --i) {
16            // Remove the current element: leftSum now equals the sum of nums[0..i-1].
17            leftSum -= nums[i];
18
19            // If the left-side sum equals the right-side product, index i is balanced.
20            if (leftSum == rightProduct) {
21                return i;
22            }
23
24            // Include the current element into the right-side product
25            // for the next (smaller) index.
26            rightProduct *= nums[i];
27
28            // Early termination: once the product is no smaller than the remaining sum,
29            // it can only grow larger (assuming positive values), so no further
30            // index can satisfy leftSum == rightProduct.
31            if (rightProduct >= leftSum) {
32                break;
33            }
34        }
35
36        // No balanced index found.
37        return -1;
38    }
39}
40```
41
42## Notes on the logic & potential perspectives
43
44A few things worth highlighting:
45
461. **The early-break optimization assumes positive integers.** For arrays containing `0` or negatives, multiplying could keep the product small or shrink it, so the `break` might exit prematurely and miss a valid index. If the input can include such values, you would remove the early break and iterate fully.
47
482. **Overflow consideration.** Both `leftSum` and `rightProduct` use `long`. For large arrays of large values, the product can still overflow `long`. If that's a concern, an alternative is to compare against the sum incrementally and stop multiplying once the product clearly exceeds any achievable sum bound.
49
503. **Alternative without early break (safer, slightly slower):**
51
52```java
53class Solution {
54    public int smallestBalancedIndex(int[] nums) {
55        long total = 0;
56        for (int value : nums) {
57            total += value;
58        }
59
60        long leftSum = total;
61        long rightProduct = 1;
62        int answer = -1;
63
64        for (int i = nums.length - 1; i >= 0; --i) {
65            leftSum -= nums[i];
66            if (leftSum == rightProduct) {
67                answer = i; // keep updating; smallest index wins since we go right-to-left
68            }
69            rightProduct *= nums[i];
70        }
71        return answer;
72    }
73}
74
1class Solution {
2public:
3    int smallestBalancedIndex(vector<int>& nums) {
4        // prefixSum will hold the sum of elements strictly before the current index.
5        // We start with the total sum and subtract each element as we move left.
6        long long prefixSum = 0;
7
8        // suffixProduct accumulates the product of elements from the current index
9        // to the end of the array.
10        long long suffixProduct = 1;
11
12        // Compute the total sum of all elements first.
13        for (int value : nums) {
14            prefixSum += value;
15        }
16
17        // Traverse from right to left.
18        for (int i = static_cast<int>(nums.size()) - 1; i >= 0; --i) {
19            // Remove nums[i] so prefixSum becomes the sum of elements before index i.
20            prefixSum -= nums[i];
21
22            // If the prefix sum equals the suffix product, index i is balanced.
23            if (prefixSum == suffixProduct) {
24                return i;
25            }
26
27            // Include nums[i] into the suffix product for the next iteration.
28            suffixProduct *= nums[i];
29
30            // Optimization: products grow faster than sums for positive integers,
31            // so once the product reaches or exceeds the remaining sum,
32            // no smaller index can ever balance, and we can stop early.
33            if (suffixProduct >= prefixSum) {
34                break;
35            }
36        }
37
38        // No balanced index found.
39        return -1;
40    }
41};
42
1/**
2 * Finds the smallest index i such that the product of all elements to the
3 * right of i equals the sum of all elements to the left of i.
4 *
5 * @param nums - The input array of numbers.
6 * @returns The smallest balanced index, or -1 if none exists.
7 */
8function smallestBalancedIndex(nums: number[]): number {
9    // Compute the total sum of all elements in the array.
10    let prefixSum = 0;
11    for (const value of nums) {
12        prefixSum += value;
13    }
14
15    // Traverse from right to left.
16    // - prefixSum: maintained as the sum of elements strictly before index i.
17    // - suffixProduct: the product of elements strictly after index i.
18    for (let i = nums.length - 1, suffixProduct = 1; i >= 0; --i) {
19        // Remove nums[i] so prefixSum becomes the sum of elements before i.
20        prefixSum -= nums[i];
21
22        // Check whether the suffix product matches the prefix sum.
23        if (suffixProduct === prefixSum) {
24            return i;
25        }
26
27        // Include nums[i] into the suffix product for the next iteration.
28        suffixProduct *= nums[i];
29
30        // Optimization: with non-negative values, once the product reaches
31        // or exceeds the remaining sum, no smaller index can satisfy the
32        // balance condition, so we can stop early.
33        if (suffixProduct >= prefixSum) {
34            break;
35        }
36    }
37
38    // No balanced index found.
39    return -1;
40}
41

Time and Space Complexity

The time complexity is O(n), and the space complexity is O(1). Here, n is the length of the array nums.

The analysis is as follows:

  • Time Complexity: The algorithm first computes s = sum(nums), which takes O(n) time. Then it iterates through the array from the end toward the beginning with a single loop. In the worst case (when the break condition p >= s is never triggered early), the loop runs n times, each iteration performing constant-time operations (subtraction, comparison, multiplication). Therefore, the overall time complexity is O(n).

  • Space Complexity: The algorithm only uses a constant number of extra variables (s, p, and the loop index i), independent of the input size. No additional data structures that grow with n are allocated. Hence, the space complexity is O(1).

Pattern Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Incorrect Early-Termination Pruning When the Array Contains Zeros or Negatives

The most dangerous pitfall in this solution is the early termination condition if suffix_product >= prefix_sum: break. This pruning assumes that once the product reaches or exceeds the remaining sum, it can only keep growing, which is only valid when all elements are positive (specifically >= 1). This assumption breaks down in several cases:

  1. The array contains a 0. If a future (more-left) element is 0, multiplying it into suffix_product resets the product to 0. So even if suffix_product is currently large, it could drop back to 0 and match a small prefix_sum later. Breaking early would skip a valid balanced index.

  2. The array contains 1s. Multiplying by 1 does not grow the product, so suffix_product >= prefix_sum being true at one step does not guarantee the product will outpace the shrinking sum.

  3. The array contains negative numbers. Products with negatives can oscillate (grow, shrink, change sign), completely invalidating the monotonicity assumption.

Example That Fails

Consider nums = [2, 3, 0, 5]:

  • Total prefix_sum = 10.
  • i = 3: prefix_sum = 10 - 5 = 5; 5 != 1; suffix_product = 5. Since 5 >= 5, the code breaks here and returns -1.
  • But the true answer at i = 2: left sum is 2 + 3 = 5, right product is 5. Index 2 is balanced!

The pruning caused us to miss the correct answer.

Solution: Remove (or Correctly Guard) the Pruning

The safest fix is to remove the early-termination check entirely and always scan all indices. The algorithm is still O(n):

class Solution:
    def smallestBalancedIndex(self, nums: list[int]) -> int:
        prefix_sum = sum(nums)
        suffix_product = 1
        ans = -1

        # Scan right-to-left; remember the last (smallest-index) match.
        for i in range(len(nums) - 1, -1, -1):
            prefix_sum -= nums[i]
            if prefix_sum == suffix_product:
                ans = i           # keep updating; smallest index wins
            suffix_product *= nums[i]

        return ans

If you want to keep a safe pruning, only apply it when every element is guaranteed to be >= 2 (so the product is strictly increasing and the sum is strictly decreasing). For general input, the unconditional full scan is the correct choice.


Pitfall 2: Misreading the Empty-Side Default Values

It is easy to swap the two special-case defaults:

  • Empty left side → sum is 0 (additive identity).
  • Empty right side → product is 1 (multiplicative identity).

A common mistake is initializing suffix_product = 0 (because 0 "feels" like the natural starting point). This would make any element multiplied into it stay 0 and silently corrupt every subsequent comparison. Always initialize suffix_product = 1 to honor the empty-product rule, and let prefix_sum naturally start at the full sum so that after subtracting nums[0] at the last iteration it correctly represents an empty left side (value 0).


Pitfall 3: Returning the Largest Index Instead of the Smallest

Because the loop runs right to left, the first match found is the smallest index — which is why the original code can return i immediately. However, if you restructure the loop to scan left to right, you must return immediately on the first match (not keep updating), otherwise you would end up returning the largest balanced index. Be deliberate about the relationship between scan direction and which match you keep.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

Given a sorted array of integers and an integer called target, find the element that equals to the target and return its index. Select the correct code that fills the ___ in the given code snippet.

1def binary_search(arr, target):
2    left, right = 0, len(arr) - 1
3    while left ___ right:
4        mid = (left + right) // 2
5        if arr[mid] == target:
6            return mid
7        if arr[mid] < target:
8            ___ = mid + 1
9        else:
10            ___ = mid - 1
11    return -1
12
1public static int binarySearch(int[] arr, int target) {
2    int left = 0;
3    int right = arr.length - 1;
4
5    while (left ___ right) {
6        int mid = left + (right - left) / 2;
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16
1function binarySearch(arr, target) {
2    let left = 0;
3    let right = arr.length - 1;
4
5    while (left ___ right) {
6        let mid = left + Math.trunc((right - left) / 2);
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More