1991. Find the Middle Index in Array
Problem Description
You are given a 0-indexed integer array nums
. Your task is to find the leftmost middleIndex
in the array.
A middleIndex
is defined as an index where the sum of all elements to its left equals the sum of all elements to its right. Mathematically, for a middleIndex
at position i
:
nums[0] + nums[1] + ... + nums[i-1] == nums[i+1] + nums[i+2] + ... + nums[nums.length-1]
Special cases to note:
- If
middleIndex == 0
, the left side sum is considered to be0
(since there are no elements to the left) - If
middleIndex == nums.length - 1
, the right side sum is considered to be0
(since there are no elements to the right)
The element at the middleIndex
itself is not included in either the left sum or the right sum.
Your goal is to return the leftmost (smallest) middleIndex
that satisfies the condition. If no such index exists, return -1
.
For example:
- In array
[2, 3, -1, 8, 4]
, index3
is a middle index because2 + 3 + (-1) = 4
equals4 = 4
- In array
[1, -1, 4]
, index2
is a middle index because1 + (-1) = 0
equals0
(no elements to the right) - In array
[1, 2, 3]
, there is no middle index, so return-1
Intuition
The key insight is that we need to find a position where the left sum equals the right sum. A naive approach would be to check each index by calculating the sum of elements to its left and right separately, but this would result in O(n²)
time complexity.
Instead, we can observe that as we move from left to right through the array:
- The left sum increases by adding the current element
- The right sum decreases by removing the current element
This observation leads us to a more efficient approach. We can start by calculating the total sum of all elements in the array - this will be our initial right sum. The left sum starts at 0
.
As we iterate through each index i
:
- First, we subtract
nums[i]
from the right sum (removing it from the right side) - Check if the left sum equals the right sum - if yes, we found our middle index
- If not, add
nums[i]
to the left sum (adding it to the left side) and continue
The order of operations is crucial here. We subtract from the right sum before checking equality because the element at the middle index should not be included in either sum. We add to the left sum after checking because when we move to the next index, the current element becomes part of the left side.
This way, we only need one pass through the array, achieving O(n)
time complexity while using only two variables to track the sums, giving us O(1)
space complexity.
Learn more about Prefix Sum patterns.
Solution Approach
We implement the solution using the prefix sum technique with two running sums:
-
Initialize variables:
l = 0
: Represents the sum of elements to the left of the current indexr = sum(nums)
: Represents the sum of all elements (initially all elements are on the "right" side)
-
Iterate through the array:
for i, x in enumerate(nums):
For each element
x
at indexi
: -
Update the right sum:
r -= x
We subtract the current element from the right sum. This effectively "removes" the current element from the right side, as we're now considering it as the potential middle index.
-
Check for middle index:
if l == r: return i
If the left sum equals the right sum at this point, we've found our middle index. Since we're iterating from left to right, this is guaranteed to be the leftmost valid middle index.
-
Update the left sum:
l += x
If the current index is not a middle index, we add the current element to the left sum, as it will be on the left side when we check the next index.
-
Return -1 if no middle index exists:
return -1
If we complete the iteration without finding a middle index, we return
-1
.
Example walkthrough with nums = [2, 3, -1, 8, 4]
:
- Initial:
l = 0
,r = 16
- Index 0:
r = 16 - 2 = 14
, check0 ≠ 14
,l = 0 + 2 = 2
- Index 1:
r = 14 - 3 = 11
, check2 ≠ 11
,l = 2 + 3 = 5
- Index 2:
r = 11 - (-1) = 12
, check5 ≠ 12
,l = 5 + (-1) = 4
- Index 3:
r = 12 - 8 = 4
, check4 = 4
✓, return3
The algorithm efficiently finds the middle index in a single pass with O(n)
time complexity and O(1)
space complexity.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the array nums = [1, 7, 3, 6, 5, 6]
.
Initial Setup:
- Calculate total sum:
1 + 7 + 3 + 6 + 5 + 6 = 28
l = 0
(left sum)r = 28
(right sum, initially contains all elements)
Iteration Process:
Index 0 (value = 1):
- Subtract from right:
r = 28 - 1 = 27
- Check if middle index:
l (0) ≠ r (27)
- No - Add to left:
l = 0 + 1 = 1
- Move to next index
Index 1 (value = 7):
- Subtract from right:
r = 27 - 7 = 20
- Check if middle index:
l (1) ≠ r (20)
- No - Add to left:
l = 1 + 7 = 8
- Move to next index
Index 2 (value = 3):
- Subtract from right:
r = 20 - 3 = 17
- Check if middle index:
l (8) ≠ r (17)
- No - Add to left:
l = 8 + 3 = 11
- Move to next index
Index 3 (value = 6):
- Subtract from right:
r = 17 - 6 = 11
- Check if middle index:
l (11) = r (11)
- Yes! - Return index
3
Verification:
- Left sum:
1 + 7 + 3 = 11
- Right sum:
5 + 6 = 11
- Element at index 3 (value 6) is not included in either sum
- Both sums equal 11, confirming index 3 is the correct middle index
The algorithm found the leftmost middle index in a single pass through the array.
Solution Implementation
1class Solution:
2 def findMiddleIndex(self, nums: List[int]) -> int:
3 # Initialize left sum as 0 and right sum as total sum of all elements
4 left_sum = 0
5 right_sum = sum(nums)
6
7 # Iterate through each element with its index
8 for index, num in enumerate(nums):
9 # Exclude current element from right sum
10 right_sum -= num
11
12 # Check if left sum equals right sum (middle index found)
13 if left_sum == right_sum:
14 return index
15
16 # Add current element to left sum for next iteration
17 left_sum += num
18
19 # No middle index found
20 return -1
21
1class Solution {
2 public int findMiddleIndex(int[] nums) {
3 // Initialize left sum as 0 (sum of elements to the left of current index)
4 int leftSum = 0;
5
6 // Initialize right sum as total sum of all elements in the array
7 int rightSum = Arrays.stream(nums).sum();
8
9 // Iterate through each index to find the middle index
10 for (int i = 0; i < nums.length; i++) {
11 // Subtract current element from right sum (exclude it from right side)
12 rightSum -= nums[i];
13
14 // Check if left sum equals right sum (found middle index)
15 if (leftSum == rightSum) {
16 return i;
17 }
18
19 // Add current element to left sum for next iteration
20 leftSum += nums[i];
21 }
22
23 // No middle index found where left sum equals right sum
24 return -1;
25 }
26}
27
1class Solution {
2public:
3 int findMiddleIndex(vector<int>& nums) {
4 // Initialize left sum to 0 and right sum to total sum of all elements
5 int leftSum = 0;
6 int rightSum = accumulate(nums.begin(), nums.end(), 0);
7
8 // Iterate through each index to find the middle index
9 for (int i = 0; i < nums.size(); ++i) {
10 // Exclude current element from right sum
11 rightSum -= nums[i];
12
13 // Check if left sum equals right sum (found middle index)
14 if (leftSum == rightSum) {
15 return i;
16 }
17
18 // Include current element in left sum for next iteration
19 leftSum += nums[i];
20 }
21
22 // No middle index found
23 return -1;
24 }
25};
26
1/**
2 * Finds the middle index where the sum of elements to the left equals the sum of elements to the right
3 * @param nums - Array of numbers to search for middle index
4 * @returns The middle index if found, otherwise -1
5 */
6function findMiddleIndex(nums: number[]): number {
7 // Initialize left sum to 0
8 let leftSum: number = 0;
9
10 // Initialize right sum to the total sum of all elements
11 let rightSum: number = nums.reduce((accumulator, current) => accumulator + current, 0);
12
13 // Iterate through each index to find the middle point
14 for (let i = 0; i < nums.length; i++) {
15 // Subtract current element from right sum (excluding current element)
16 rightSum -= nums[i];
17
18 // Check if left sum equals right sum (found middle index)
19 if (leftSum === rightSum) {
20 return i;
21 }
22
23 // Add current element to left sum for next iteration
24 leftSum += nums[i];
25 }
26
27 // No middle index found
28 return -1;
29}
30
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the input array nums
.
The algorithm makes two passes through the array:
- First pass:
sum(nums)
iterates through alln
elements to calculate the total sum -O(n)
- Second pass: The for loop with
enumerate(nums)
iterates through alln
elements once -O(n)
Inside the loop, all operations (subtraction, comparison, addition) are constant time - O(1)
Total time complexity: O(n) + O(n) = O(n)
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
- Two integer variables
l
andr
to store the left and right sums - Loop variables
i
andx
from the enumerate function
No additional data structures that scale with input size are created, so the space complexity is constant.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrect Sum Initialization
Problem: A common mistake is initializing both left_sum
and right_sum
to 0, or trying to calculate the right sum as sum(nums[i+1:])
in each iteration.
Incorrect Implementation:
def findMiddleIndex(self, nums: List[int]) -> int:
for i in range(len(nums)):
left_sum = sum(nums[:i]) # Recalculating every time - O(n²)
right_sum = sum(nums[i+1:]) # Recalculating every time - O(n²)
if left_sum == right_sum:
return i
return -1
Why it's wrong: This approach has O(n²) time complexity because it recalculates sums for each index, making it inefficient for large arrays.
Solution: Initialize right_sum
as the total sum of all elements, then maintain running sums by subtracting/adding the current element as you iterate.
Pitfall 2: Including the Middle Element in Sums
Problem: Accidentally including the element at the middle index in either the left or right sum calculation.
Incorrect Implementation:
def findMiddleIndex(self, nums: List[int]) -> int:
left_sum = 0
right_sum = sum(nums)
for index, num in enumerate(nums):
# Wrong: checking before removing current element from right
if left_sum == right_sum - num:
return index
left_sum += num
right_sum -= num
return -1
Why it's wrong: The condition check happens at the wrong time, potentially including the middle element in calculations or creating off-by-one errors.
Solution: Always remove the current element from right_sum
first, then check equality, then add to left_sum
.
Pitfall 3: Wrong Order of Operations
Problem: Updating the left sum before checking the condition, which leads to incorrect comparisons.
Incorrect Implementation:
def findMiddleIndex(self, nums: List[int]) -> int:
left_sum = 0
right_sum = sum(nums)
for index, num in enumerate(nums):
left_sum += num # Wrong: adding to left before checking
right_sum -= num
if left_sum == right_sum:
return index
return -1
Why it's wrong: This includes the current element in the left sum before checking if the current index is a middle index, violating the problem's requirement that the middle element should not be in either sum.
Solution: The correct order is: (1) Remove from right sum, (2) Check condition, (3) Add to left sum.
Pitfall 4: Not Handling Edge Cases
Problem: Forgetting that the first or last element can be a middle index when the opposite side has sum 0.
Incorrect Assumption:
def findMiddleIndex(self, nums: List[int]) -> int:
if len(nums) <= 2: # Wrong: assuming small arrays can't have middle index
return -1
# ... rest of the code
Why it's wrong: Arrays like [1, 0]
have index 0 as a valid middle index (left sum = 0, right sum = 0).
Solution: The algorithm should handle all array sizes naturally without special-casing small arrays. The running sum approach handles these edge cases correctly.
Which of the following uses divide and conquer strategy?
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!