Q4 - Battery Pack Balance Point
We believe a variation of this interview question has recently been reported by candidates interviewing for software engineering roles at Tesla. The naive approach recomputes the two sides at every cell and runs in O(n^2); the whole point is to see that one running total collapses it to a single O(n) pass.
Scenario: A Tesla battery pack is a series string of cells, each holding some amount of charge (given in pack order). For diagnostics you want to find a balance point: a single cell, the fulcrum, such that the total charge in all the cells to its left equals the total in all the cells to its right. The fulcrum cell is the pivot and counts for neither side.
Part 1: Given charges (the per-cell charge in order), return the index of the leftmost balance point, or -1 if no cell balances the pack.
Example 1 (Part 1: an exact balance point)
Input: charges = [1, 7, 3, 6, 5, 6]
Output: 3
Why: at index 3 the cells to the left (1 + 7 + 3 = 11) match the cells to the
right (5 + 6 = 11). The fulcrum cell (the 6 at index 3) is not counted.
Example 2 (Part 1: no balance point)
Input: charges = [1, 2, 3]
Output: -1
Why: no single cell splits the rest into two equal-charge sides.
💡 One running total is all you need. Once you know the pack's total and the sum on the left, the sum on the right is just total - left - fulcrum. There is no need to add up the right side from scratch at every cell, which is what turns an O(n^2) idea into a single O(n) sweep.
Part 1: Sweep the Fulcrum Once
The naive move is, for each candidate cell, to sum everything on its left and everything on its right and compare. That is a full pass per cell, so O(n^2). The insight is that as the fulcrum slides right, the left sum only ever grows by the cell it passes, and the right sum is whatever charge is left over.
Step 1: The right side is free once you know the left
Compute the pack's total once. For a fulcrum at index i, the right side is everything except the left part and the fulcrum itself: right = total - left - charges[i]. So a single running left gives you both sides at every position.
charges = [1, 7, 3, 6, 5, 6] total = 28
at index i: left = sum of charges[0 .. i-1]
right = total - left - charges[i]
Step 2: Slide left to right, growing the running total
Start with left = 0. At each index, check whether left == right; if so, that is the balance point. Otherwise add the current charge to left and move on.
i=0 (1): left 0, right 28-0-1 = 27 -> 0 != 27
i=1 (7): left 1, right 28-1-7 = 20 -> 1 != 20
i=2 (3): left 8, right 28-8-3 = 17 -> 8 != 17
i=3 (6): left 11, right 28-11-6 = 11 -> 11 == 11 balance point!
left = 0
for i, charge in enumerate(charges):
right = total - left - charge # everything except the left part and the fulcrum
if left == right:
return i
left += charge
Here is the sweep on Example 1. The blue cells are the left side, the green cells are the right side, and the gold fulcrum slides across until the two sides match:
# O(n) time | O(1) space
def balance_point(charges):
total = sum(charges)
left = 0
for i, charge in enumerate(charges):
right = total - left - charge # everything except the left part and the fulcrum
if left == right:
return i
left += charge
return -1
Part 2: The Closest Balance
Part 2: Real packs rarely split perfectly, so an exact balance point often does not exist. Extend the function to always return an answer: the index that comes closest to balanced (the smallest |left - right|), together with that minimum imbalance, as [index, imbalance].
Example 3 (Part 2)
Input: charges = [3, 1, 4, 1, 5]
Output: [2, 2]
Why: no cell balances exactly. Index 2 comes closest: left = 3 + 1 = 4,
right = 1 + 5 = 6, an imbalance of 2, smaller than at any other cell.
It is the same single sweep, with one change: instead of stopping at the first left == right, compute the imbalance |left - right| at every cell and keep the smallest one seen, along with its index. An exact balance point simply shows up as an imbalance of 0, so Part 2 fully contains Part 1.
charges = [3, 1, 4, 1, 5] total = 14
i=0 (3): left 0, right 11 imbalance 11
i=1 (1): left 3, right 10 imbalance 7
i=2 (4): left 4, right 6 imbalance 2 <- smallest
i=3 (1): left 8, right 5 imbalance 3
i=4 (5): left 9, right 0 imbalance 9
best = [2, 2]
if gap < best_gap: # keep the smallest imbalance and where it happened
best_gap, best_index = gap, i
# O(n) time | O(1) space
def best_balance(charges):
total = sum(charges)
left = 0
best_index, best_gap = -1, float('inf')
for i, charge in enumerate(charges):
right = total - left - charge
gap = abs(left - right)
if gap < best_gap: # keep the smallest imbalance and where it happened
best_gap, best_index = gap, i
left += charge
return [best_index, best_gap]
Common Pitfalls
- Recomputing both sides at every cell. Summing the left and right from scratch each time is the
O(n^2)trap. One runningleftplus thetotalgives both sides inO(1)per cell. - Counting the fulcrum on a side. The balance-point cell belongs to neither side, so the right sum must subtract it:
total - left - charges[i], nottotal - left. - Returning after adding the current charge. Check
left == rightbefore foldingcharges[i]intoleft; otherwiseleftalready includes the fulcrum and the test is wrong by one cell. - Assuming an exact split exists. Part 1 can legitimately return
-1. Part 2 sidesteps that by always reporting the closest cell, which is often what the real diagnostic wants anyway.
Complexity Analysis
Both parts: O(n) time, where n is the number of cells: one pass to sum the pack and one pass to sweep the fulcrum (or a single pass if you fold the total in first). Space is O(1), just the running left, the total, and the best seen so far. This is the whole reason the running-total trick matters: it takes the obvious quadratic idea down to linear time and constant space.
Final Notes
We believe prefix-sum questions like this, framed around a physical balance, appear in Tesla software engineering interviews, because splitting a series of quantities into equal-weight sides is a real first-principles task in pack and load design. Reading it as "the right side is just the leftover" is the framing that lands.
- The right side is the leftover.
right = total - left - fulcrumis the whole trick. Saying that identity out loud, before writing any loop, is what shows you have seen past the naive double sum. - One running total, one pass. Grow
leftas the fulcrum moves and you never look backward. That is what makes itO(n)time andO(1)space. - Mind the fulcrum and the order of operations. The pivot cell counts for neither side, and you must test the balance before adding the current charge to
left. Those two details are the classic off-by-one traps in this sweep. - Part 2 is Part 1 with a running minimum. Track the smallest
|left - right|instead of stopping at zero, and the exact case falls out as imbalance0. Generalizing cleanly like this is a strong signal. - Name the family. This is the prefix-sum / running-total pattern, the same idea behind pivot indexes, equilibrium points, and split-the-array problems. Recognizing it means the next variant is easy. 🚀