Facebook Pixel

3682. Minimum Index Sum of Common Elements 🔒

MediumArrayHash Table
LeetCode ↗

Problem Description

You are given two integer arrays nums1 and nums2, both of the same length n.

A pair of indices (i, j) is called a good pair if the value at index i in the first array equals the value at index j in the second array, i.e., nums1[i] == nums2[j].

Your task is to find the smallest possible index sum i + j among all good pairs.

  • If at least one good pair exists, return the minimum value of i + j.
  • If the two arrays share no common values at all (meaning no good pair can be formed), return -1.

Example:

Suppose nums1 = [1, 2, 3] and nums2 = [2, 4, 1].

  • The value 1 appears at index 0 in nums1 and index 2 in nums2, giving a good pair (0, 2) with sum 0 + 2 = 2.
  • The value 2 appears at index 1 in nums1 and index 0 in nums2, giving a good pair (1, 0) with sum 1 + 0 = 1.

The minimum index sum among all good pairs is 1, so the answer is 1.

The key challenge is to do this efficiently: rather than checking every possible (i, j) combination (which would take O(n^2) time), you can record the earliest occurrence of each value in nums2 using a hash map, then scan nums1 once to compute the best answer in O(n) time.

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

How We Pick the Algorithm

Why Hash Table / Counting?

This problem maps to Hash Table / Counting through a short path in the full flowchart.

Linkedlist?noFastlookup orcounting?yesHash Table /Counting

The problem finds the minimum index sum of common elements between two arrays by recording the first occurrence of each value in one array and scanning the other.

Open in Flowchart

Intuition

The brute-force approach would be to check every pair (i, j), compare nums1[i] with nums2[j], and track the smallest i + j. That works, but it costs O(n^2) time. The natural question is: where is the wasted work?

The key observation is this: for any value x that appears in both arrays, the only occurrence of x in nums2 that ever matters is its first (smallest-index) occurrence. If x appears at indices j1 < j2 in nums2, then for any i in nums1, the pair (i, j1) always gives a smaller sum than (i, j2). So all later occurrences of x in nums2 can be safely ignored.

This insight immediately suggests a preprocessing step: walk through nums2 once and record, in a hash map d, the earliest index of each distinct value. Since we iterate from left to right, we simply skip a value if it's already in the map — the first index we stored is guaranteed to be the smallest.

Once that map exists, the problem collapses into a single pass over nums1. For each element nums1[i], a lookup tells us in O(1) whether the value exists in nums2, and if so, the best possible partner index d[nums1[i]]. The candidate sum is i + d[nums1[i]], and we keep the minimum across all i.

By symmetry, the same argument applies to nums1: only the first occurrence of each value in nums1 could ever produce the minimum, but since we take min over all candidates anyway, scanning every element of nums1 handles this automatically without extra logic.

Finally, initializing the answer to inf gives a clean way to detect the "no good pair" case — if it never gets updated, no common value exists, and we return -1. The result is an O(n) time, O(n) space solution that trades a hash map for the quadratic comparison loop.

Solution Approach

The solution uses a hash map to eliminate the nested loop and achieve linear time. The implementation breaks down into three clear steps.

Step 1: Build the hash map from nums2.

d = {}
for i, x in enumerate(nums2):
    if x not in d:
        d[x] = i

We iterate through nums2 and store each value x as a key, mapped to its index i. The guard if x not in d ensures that only the first occurrence of each value is recorded. Because the traversal goes left to right, the stored index is automatically the smallest one for that value — which is the only index that could ever contribute to a minimum sum.

Step 2: Scan nums1 and compute candidate sums.

ans = inf
for i, x in enumerate(nums1):
    if x in d:
        ans = min(ans, i + d[x])

We initialize ans to infinity as a sentinel meaning "no good pair found yet." Then, for each element x = nums1[i]:

  • We check in O(1) whether x exists in the map d.
  • If it does, (i, d[x]) is a good pair, and d[x] is the best possible partner index in nums2 for this value. The candidate index sum is i + d[x].
  • We update ans with min(ans, i + d[x]), so after the loop finishes, ans holds the smallest sum across all good pairs.

Note that we don't need to deduplicate values in nums1: if a value repeats, its later occurrences produce strictly larger sums, and the min operation discards them naturally.

Step 3: Handle the no-pair case.

return -1 if ans == inf else ans

If ans was never updated, the two arrays share no common element, so we return -1. Otherwise, we return the computed minimum.

Complexity Analysis

  • Time complexity: O(n) — one pass over nums2 to build the map and one pass over nums1 to query it, with each hash map operation taking O(1) on average.
  • Space complexity: O(n) — in the worst case, every element of nums2 is distinct and the map stores n entries.

Alternative perspective: the roles of the two arrays are symmetric. One could equally build the map from nums1 and iterate over nums2; correctness and complexity are identical. A brute-force double loop comparing all (i, j) pairs would also work but would run in O(n^2), which the hash map approach improves upon decisively.

Example Walkthrough

Let's trace the algorithm with nums1 = [5, 3, 7, 3] and nums2 = [3, 9, 5, 3].

Step 1: Build the hash map from nums2.

We walk through nums2 left to right, recording only the first occurrence of each value:

Index iValue xActionMap d
03New value → store{3: 0}
19New value → store{3: 0, 9: 1}
25New value → store{3: 0, 9: 1, 5: 2}
33Already in map → skip{3: 0, 9: 1, 5: 2}

Note how the duplicate 3 at index 3 is ignored — pairing with index 0 always gives a smaller sum, so index 3 can never produce the answer.

Step 2: Scan nums1 and compute candidate sums.

We initialize ans = inf and check each element of nums1 against the map:

Index iValue xIn d?Candidate i + d[x]ans
05Yes, d[5] = 20 + 2 = 22
13Yes, d[3] = 01 + 0 = 11
27No—1
33Yes, d[3] = 03 + 0 = 31

Two things worth observing:

  • The value 7 has no match in nums2, so it contributes nothing — the O(1) lookup quietly skips it.
  • The repeated 3 in nums1 at index 3 produces a worse candidate (3) than its earlier occurrence (1), and min discards it automatically. No deduplication of nums1 was needed.

Step 3: Return the result.

Since ans = 1 was updated from inf, at least one good pair exists, and we return 1 — achieved by the pair (i, j) = (1, 0), where nums1[1] == nums2[0] == 3.

For contrast, if the inputs were nums1 = [1, 2] and nums2 = [7, 8], no lookup would ever succeed, ans would remain inf, and the function would return -1.

The entire process used one pass over each array — O(n) time — instead of checking all 4 × 4 = 16 index pairs as brute force would.

Solution Implementation

1class Solution:
2    def minimumSum(self, nums1: List[int], nums2: List[int]) -> int:
3        # Map each value in nums2 to the index of its first occurrence.
4        # Only the first (smallest) index matters for minimizing the sum.
5        first_index = {}
6        for index, value in enumerate(nums2):
7            if value not in first_index:
8                first_index[value] = index
9
10        # Initialize the answer with infinity as a sentinel value.
11        min_sum = inf
12
13        # Iterate over nums1; for every value that also exists in nums2,
14        # compute the sum of the two indices and keep the minimum.
15        for index, value in enumerate(nums1):
16            if value in first_index:
17                min_sum = min(min_sum, index + first_index[value])
18
19        # If no common value was found, return -1; otherwise return the minimum sum.
20        return -1 if min_sum == inf else min_sum
21
1class Solution {
2    /**
3     * Finds the minimum sum of indices (i + j) such that nums1[i] == nums2[j].
4     * Returns -1 if no such pair of indices exists.
5     *
6     * @param nums1 the first integer array
7     * @param nums2 the second integer array
8     * @return the minimum index sum, or -1 if no common value is found
9     */
10    public int minimumSum(int[] nums1, int[] nums2) {
11        int n = nums1.length;
12
13        // A large constant representing "infinity" (no valid answer found yet)
14        final int INF = 1 << 30;
15
16        // Map each value in nums2 to the smallest index at which it appears.
17        // putIfAbsent ensures only the first (smallest) index is stored.
18        Map<Integer, Integer> valueToMinIndex = new HashMap<>();
19        for (int j = 0; j < n; j++) {
20            valueToMinIndex.putIfAbsent(nums2[j], j);
21        }
22
23        int minIndexSum = INF;
24
25        // For each value in nums1, check if it also exists in nums2.
26        // If so, compute the sum of the current index and the smallest
27        // matching index in nums2, and keep track of the minimum.
28        for (int i = 0; i < n; i++) {
29            Integer j = valueToMinIndex.get(nums1[i]);
30            if (j != null) {
31                minIndexSum = Math.min(minIndexSum, i + j);
32            }
33        }
34
35        // If no common value was found, return -1; otherwise return the result.
36        return minIndexSum == INF ? -1 : minIndexSum;
37    }
38}
39
1class Solution {
2public:
3    int minimumSum(vector<int>& nums1, vector<int>& nums2) {
4        int n = nums1.size();
5        const int INF = INT_MAX;
6
7        // Map each value in nums2 to its earliest (smallest) index.
8        unordered_map<int, int> firstIndex;
9        for (int i = 0; i < n; ++i) {
10            // Only record the first occurrence to keep the index minimal.
11            if (!firstIndex.count(nums2[i])) {
12                firstIndex[nums2[i]] = i;
13            }
14        }
15
16        int ans = INF;
17
18        // For each value in nums1, check if the same value exists in nums2.
19        // If so, the cost is the sum of the current index and the earliest
20        // index of that value in nums2; keep the minimum over all matches.
21        for (int i = 0; i < n; ++i) {
22            auto it = firstIndex.find(nums1[i]);
23            if (it != firstIndex.end()) {
24                ans = min(ans, i + it->second);
25            }
26        }
27
28        // If no common value was found, return -1.
29        return ans == INF ? -1 : ans;
30    }
31};
32
1/**
2 * Finds the minimum sum of indices (i + j) such that nums1[i] === nums2[j].
3 * Returns -1 if no such pair of indices exists.
4 *
5 * @param nums1 - The first array of numbers
6 * @param nums2 - The second array of numbers
7 * @returns The minimum index sum, or -1 if no matching value exists in both arrays
8 */
9function minimumSum(nums1: number[], nums2: number[]): number {
10    // Length of the input arrays
11    const length: number = nums1.length;
12
13    // A large value representing "infinity" (used as the initial answer)
14    const INF: number = 1 << 30;
15
16    // Map each value in nums2 to the smallest index at which it appears
17    const valueToMinIndex: Map<number, number> = new Map<number, number>();
18    for (let j = 0; j < length; j++) {
19        // Only record the first (smallest) index for each value
20        if (!valueToMinIndex.has(nums2[j])) {
21            valueToMinIndex.set(nums2[j], j);
22        }
23    }
24
25    // Initialize the answer with "infinity"
26    let answer: number = INF;
27
28    // Iterate over nums1 and look for matching values in nums2
29    for (let i = 0; i < length; i++) {
30        const value: number = nums1[i];
31        if (valueToMinIndex.has(value)) {
32            // Update the answer with the smaller index sum
33            answer = Math.min(answer, i + (valueToMinIndex.get(value) as number));
34        }
35    }
36
37    // If no matching value was found, return -1; otherwise, return the answer
38    return answer === INF ? -1 : answer;
39}
40

Time and Space Complexity

  • Time Complexity: O(n + m), where n is the length of nums1 and m is the length of nums2. The code iterates through nums2 once to build the dictionary d (taking O(m) time), and then iterates through nums1 once to find the minimum index sum (taking O(n) time, since each dictionary lookup is O(1) on average). If both arrays have the same length n, this simplifies to O(n).

  • Space Complexity: O(m), where m is the length of nums2. The dictionary d stores at most one entry per distinct element in nums2, requiring O(m) space in the worst case. If both arrays have the same length n, this simplifies to O(n).

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

Common Pitfalls

Pitfall 1: Overwriting earlier indices when building the hash map

The most frequent mistake is building the map from nums2 without guarding against duplicates:

# WRONG: later occurrences overwrite earlier ones
d = {}
for i, x in enumerate(nums2):
    d[x] = i

If a value appears multiple times in nums2, this stores its last index instead of its first. Since only the smallest index for each value can ever minimize i + j, the computed answer may be larger than the true minimum.

Example: nums1 = [5], nums2 = [5, 9, 5].

  • Correct answer: pair (0, 0) with sum 0.
  • Buggy version stores d[5] = 2 and returns 0 + 2 = 2.

Solution: Keep the guard so only the first occurrence is recorded:

for i, x in enumerate(nums2):
    if x not in d:
        d[x] = i

An equivalent fix is to iterate nums2 in reverse with unconditional assignment (d[x] = i), since the last write would then be the leftmost index — but the explicit guard is clearer about intent.

Pitfall 2: Using 0 or -1 as the "not found" sentinel

Initializing min_sum = 0 is broken because 0 is a valid answer (pair (0, 0)), and min(0, anything) never updates. Similarly, initializing to -1 makes the min comparison always pick -1.

Solution: Use float('inf') (or math.inf) as the sentinel, then translate it to -1 only at the very end:

return -1 if min_sum == inf else min_sum

Alternatively, use a separate boolean flag found to track whether any good pair exists.

Pitfall 3: Building the map and querying it with the same array

A subtle copy-paste error is enumerating the same array twice — e.g., building first_index from nums2 but then also looping over nums2 instead of nums1 in the second pass. The code still runs without error and may even pass trivial tests where the arrays overlap heavily, which makes the bug easy to miss.

Solution: Double-check that the two passes operate on different arrays, and verify with a test case where the arrays differ, such as nums1 = [3], nums2 = [7] (expected output -1).

Pitfall 4: Returning early instead of checking all candidates

Some implementations return i + d[x] the moment the first match in nums1 is found. A later element of nums1 with a much smaller partner index in nums2 can still yield a smaller sum.

Example: nums1 = [8, 2], nums2 = [2, 9, 9, 8].

  • First match found: value 8 at i = 0, partner index 3, sum 3.
  • True minimum: value 2 at i = 1, partner index 0, sum 1.

Solution: Always finish the full scan of nums1, accumulating the minimum with min(min_sum, i + d[x]). (A valid optimization does exist — you may break out of the loop once i >= min_sum, because every future sum is at least i — but a plain early return on the first match is incorrect.)

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:

What's the relationship between a tree and a graph?


Recommended Readings

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

Load More