LeetCopilot Logo
LeetCopilot
LeetCode Pattern/Hash Map/Hash Map Index Mapping: Store Positions for O(1) Lookup

Hash Map Index Mapping: Store Positions for O(1) Lookup

LeetCopilot Team
Dec 23, 2025
12 min read
Hash MapIndex MappingPosition TrackingInterview Prep
Master the index mapping pattern for position tracking. Learn when to store first index, last index, or all indices, and how to optimize space while maintaining O(1) lookup.

You're solving a problem and you realize: "I need to know where I saw this value before."

Should you store the first index? The last index? All indices? How do you handle duplicates?

This is the gap between knowing you need indices and mastering the index mapping pattern.

The index mapping pattern is a fundamental hash map technique where you store element positions for instant lookup. It appears in 40+ LeetCode problems, from "Two Sum" to "Contains Duplicate II" to "Find All Anagrams." But the pattern isn't just "store indices"—it's a systematic approach to deciding what positions to track and how.

This guide will teach you:

  • When to use index mapping vs. other patterns
  • The three index storage strategies (first, last, all)
  • How to handle duplicates correctly
  • Space optimization techniques
  • Common mistakes and edge cases

Master this, and position-tracking problems become pattern recognition.

What Is Index Mapping?

The core concept

The index mapping pattern uses a hash map to store where each element appears in an array, enabling O(1) position lookup.

Structure:

  • Key: The element value
  • Value: Index (or indices) where it appears

Example:

python
nums = [7, 2, 5, 2, 8]

# Map value to index
index_map = {
    7: 0,
    2: 3,  # Last occurrence
    5: 2,
    8: 4
}

Why it works

Without hash map:

python
# Find where value 5 appears - O(n)
for i, num in enumerate(nums):
    if num == 5:
        return i

With hash map:

python
# Build once - O(n)
index_map = {num: i for i, num in enumerate(nums)}
# Lookup - O(1)
index = index_map[5]

Trade-off: Spend O(n) space to save repeated O(n) searches.

Real-world analogy

Imagine a parking lot:

  • Without index map: Walk through every spot to find your car (slow)
  • With index map: Remember your parking spot number, go directly there (fast)

When to Use Index Mapping

Quick checklist

Use index mapping when:

  • ✅ You need to find where an element appears
  • ✅ You need to check distance between occurrences
  • ✅ You need to track last seen position
  • ✅ You need O(1) position lookup
  • ✅ Problem asks for indices (not just values)

Phrases in problem statements

Strong index mapping signals:

  • "return indices of..."
  • "find the index where..."
  • "distance between occurrences"
  • "last seen position"
  • "contains duplicate within k distance"

Index mapping vs. other patterns

vs. Frequency counter:

  • Frequency: Count how many times (don't care where)
  • Index mapping: Track where it appears (care about position)

vs. Hash set:

  • Set: Only check existence (don't need position)
  • Index mapping: Need to know position

vs. Sorting:

  • Sorting: Changes indices, can't track original positions
  • Index mapping: Preserves original positions

The Three Index Storage Strategies

Strategy 1: Store Last Index

When to use: Need most recent occurrence

python
# Store last index (overwrite on duplicate)
index_map = {}
for i, num in enumerate(nums):
    index_map[num] = i  # Overwrites previous

Example problems:

  • Two Sum (need any valid pair)
  • Contains Duplicate II (check distance from last occurrence)

Strategy 2: Store First Index

When to use: Need earliest occurrence

python
# Store first index (don't overwrite)
index_map = {}
for i, num in enumerate(nums):
    if num not in index_map:
        index_map[num] = i  # Only store first

Example problems:

  • First Unique Character (need first occurrence)
  • Find First Duplicate (need earliest duplicate)

Strategy 3: Store All Indices

When to use: Need all occurrences

python
from collections import defaultdict

# Store all indices
index_map = defaultdict(list)
for i, num in enumerate(nums):
    index_map[num].append(i)

Example problems:

  • Find All Anagrams (need all starting positions)
  • Intersection of Arrays (need all matching positions)

Decision framework

code
Do you need all occurrences?
├─ Yes → Store list of indices
└─ No → Do you need first or last?
    ├─ First → Store if not exists
    └─ Last → Always overwrite

For more on handling duplicates, see Handling Duplicates in Hash Maps.

The Universal Templates

Template 1: Store Last Index

python
def storeLastIndex(nums: List[int]) -> Dict[int, int]:
    """
    Store last occurrence of each element.
    
    Time: O(n)
    Space: O(k) where k = number of distinct elements
    """
    index_map = {}
    
    for i, num in enumerate(nums):
        index_map[num] = i  # Overwrites previous
    
    return index_map

# Example
nums = [1, 2, 3, 2, 1]
# Result: {1: 4, 2: 3, 3: 2}

Template 2: Store First Index

python
def storeFirstIndex(nums: List[int]) -> Dict[int, int]:
    """
    Store first occurrence of each element.
    
    Time: O(n)
    Space: O(k)
    """
    index_map = {}
    
    for i, num in enumerate(nums):
        if num not in index_map:
            index_map[num] = i  # Only store first
    
    return index_map

# Example
nums = [1, 2, 3, 2, 1]
# Result: {1: 0, 2: 1, 3: 2}

Template 3: Store All Indices

python
from collections import defaultdict

def storeAllIndices(nums: List[int]) -> Dict[int, List[int]]:
    """
    Store all occurrences of each element.
    
    Time: O(n)
    Space: O(n) worst case
    """
    index_map = defaultdict(list)
    
    for i, num in enumerate(nums):
        index_map[num].append(i)
    
    return index_map

# Example
nums = [1, 2, 3, 2, 1]
# Result: {1: [0, 4], 2: [1, 3], 3: [2]}

Java Templates

java
// Store last index
Map<Integer, Integer> indexMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
    indexMap.put(nums[i], i);
}

// Store first index
Map<Integer, Integer> indexMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
    indexMap.putIfAbsent(nums[i], i);
}

// Store all indices
Map<Integer, List<Integer>> indexMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
    indexMap.putIfAbsent(nums[i], new ArrayList<>());
    indexMap.get(nums[i]).add(i);
}

JavaScript Templates

javascript
// Store last index
const indexMap = new Map();
for (let i = 0; i < nums.length; i++) {
  indexMap.set(nums[i], i);
}

// Store first index
const indexMap = new Map();
for (let i = 0; i < nums.length; i++) {
  if (!indexMap.has(nums[i])) {
    indexMap.set(nums[i], i);
  }
}

// Store all indices
const indexMap = new Map();
for (let i = 0; i < nums.length; i++) {
  if (!indexMap.has(nums[i])) {
    indexMap.set(nums[i], []);
  }
  indexMap.get(nums[i]).push(i);
}

Pattern 1: Two Sum (Store Last Index)

Problem: Two Sum (LeetCode 1)

Problem: Find two numbers that sum to target, return their indices.

Why index mapping: Need to return indices, not values.

python
def twoSum(nums: List[int], target: int) -> List[int]:
    """
    Use index mapping to find complement.
    Store index as we go (last occurrence).
    
    Time: O(n)
    Space: O(n)
    """
    index_map = {}
    
    for i, num in enumerate(nums):
        complement = target - num
        
        if complement in index_map:
            return [index_map[complement], i]
        
        index_map[num] = i  # Store last index
    
    return []

Key insight: Store index as value, check for complement before adding.

Why last index works: We check before adding, so we never use the same element twice.

For detailed analysis, see Two Sum Pattern.

Pattern 2: Contains Duplicate II (Store Last Index)

Problem: Contains Duplicate II (LeetCode 219)

Problem: Check if array contains duplicates within distance k.

Why index mapping: Need to track distance between occurrences.

python
def containsNearbyDuplicate(nums: List[int], k: int) -> bool:
    """
    Store last seen index, check distance.
    
    Time: O(n)
    Space: O(min(n, k))
    """
    index_map = {}
    
    for i, num in enumerate(nums):
        if num in index_map:
            # Check distance from last occurrence
            if i - index_map[num] <= k:
                return True
        
        # Update to current index (last occurrence)
        index_map[num] = i
    
    return False

Trace example:

text
nums = [1, 2, 3, 1], k = 3

i=0: num=1, not in map, add {1: 0}
i=1: num=2, not in map, add {1: 0, 2: 1}
i=2: num=3, not in map, add {1: 0, 2: 1, 3: 2}
i=3: num=1, in map! distance = 3 - 0 = 3 <= 3, return True

Key insight: Always update to latest index to track most recent occurrence.

Pattern 3: First Unique Character (Store First Index)

Problem: First Unique Character (LeetCode 387)

Problem: Find the first non-repeating character's index.

Why first index: Need earliest unique character.

python
from collections import Counter

def firstUniqChar(s: str) -> int:
    """
    Count frequencies, then find first unique.
    
    Time: O(n)
    Space: O(1) - at most 26 characters
    """
    # Count frequencies
    freq = Counter(s)
    
    # Find first character with frequency 1
    for i, char in enumerate(s):
        if freq[char] == 1:
            return i
    
    return -1

Alternative (store first index):

python
def firstUniqChar(s: str) -> int:
    # Store first index and count
    index_map = {}
    
    for i, char in enumerate(s):
        if char not in index_map:
            index_map[char] = [i, 1]  # [first_index, count]
        else:
            index_map[char][1] += 1  # Increment count
    
    # Find minimum index with count 1
    result = float('inf')
    for first_idx, count in index_map.values():
        if count == 1:
            result = min(result, first_idx)
    
    return result if result != float('inf') else -1

Key insight: Store first index, track count separately.

Pattern 4: Find All Anagrams (Store All Indices)

Problem: Find All Anagrams (LeetCode 438)

Problem: Find all start indices of anagrams of p in s.

Why all indices: Need to return all starting positions.

python
from collections import Counter

def findAnagrams(s: str, p: str) -> List[int]:
    """
    Use sliding window + frequency counter.
    Collect all valid starting indices.
    
    Time: O(n)
    Space: O(1) - at most 26 characters
    """
    if len(p) > len(s):
        return []
    
    result = []
    p_count = Counter(p)
    window_count = Counter(s[:len(p)])
    
    if window_count == p_count:
        result.append(0)
    
    # Slide window
    for i in range(len(p), len(s)):
        # Add new character
        window_count[s[i]] += 1
        
        # Remove old character
        left_char = s[i - len(p)]
        window_count[left_char] -= 1
        if window_count[left_char] == 0:
            del window_count[left_char]
        
        # Check if anagram
        if window_count == p_count:
            result.append(i - len(p) + 1)
    
    return result

Key insight: Collect all valid indices as we find them.

Space Optimization Techniques

Technique 1: Sliding Window to Limit Map Size

For "within distance k" problems:

python
def containsNearbyDuplicate(nums: List[int], k: int) -> bool:
    """
    Use sliding window to keep map size <= k.
    
    Time: O(n)
    Space: O(min(n, k))
    """
    window = set()
    
    for i, num in enumerate(nums):
        if num in window:
            return True
        
        window.add(num)
        
        # Keep window size <= k
        if len(window) > k:
            window.remove(nums[i - k])
    
    return False

Optimization: Use set instead of map (only need presence, not index).

Technique 2: Use Set When Index Not Needed

python
# If you only need to check existence, not position
seen = set()  # Instead of index_map = {}

for num in nums:
    if num in seen:
        return True
    seen.add(num)

Space saved: Set is more memory efficient than dict.

Technique 3: In-Place Marking (Array Only)

For arrays with limited range:

python
def findDuplicates(nums: List[int]) -> List[int]:
    """
    Find duplicates in array where 1 <= nums[i] <= n.
    Use array itself as hash map.
    
    Time: O(n)
    Space: O(1)
    """
    result = []
    
    for num in nums:
        index = abs(num) - 1
        
        if nums[index] < 0:
            # Already seen
            result.append(abs(num))
        else:
            # Mark as seen
            nums[index] = -nums[index]
    
    return result

Trade-off: Modifies input, only works for specific constraints.

For more optimization techniques, see Hash Map Memory Optimization.

Common Mistakes

Mistake 1: Storing Wrong Key/Value

Wrong:

python
# Storing index as key, value as value
index_map[i] = num  # Can't look up by value!

Correct:

python
# Store value as key, index as value
index_map[num] = i

Mistake 2: Not Deciding First vs. Last

Wrong (inconsistent):

python
# Sometimes overwrites, sometimes doesn't
if random.choice([True, False]):
    index_map[num] = i

Correct (explicit choice):

python
# Always overwrite (last index)
index_map[num] = i

# Or never overwrite (first index)
if num not in index_map:
    index_map[num] = i

Mistake 3: Forgetting to Handle Missing Keys

Wrong:

python
index = index_map[num]  # KeyError if num not in map!

Correct:

python
# Check first
if num in index_map:
    index = index_map[num]

# Or use .get()
index = index_map.get(num, -1)

Mistake 4: Using List When Not Needed

Wrong (overkill):

python
# Storing list when only need one index
index_map = defaultdict(list)
for i, num in enumerate(nums):
    index_map[num].append(i)
# Then only use index_map[num][0]

Correct:

python
# Just store single index
index_map = {}
for i, num in enumerate(nums):
    if num not in index_map:
        index_map[num] = i

Complexity Analysis

Time Complexity

Building index map: O(n)

  • Single pass through array
  • Each insertion is O(1) average

Querying index: O(1)

  • Hash map lookup

Space Complexity

Store last/first index: O(k) where k = distinct elements

  • Best case: O(1) (all elements same)
  • Worst case: O(n) (all elements distinct)

Store all indices: O(n)

  • Must store every index

Optimization: O(min(n, k)) with sliding window

Practice Problems

Beginner

  1. Two Sum (#1) - Store last index
  2. Contains Duplicate (#217) - Use set (simpler)
  3. Contains Duplicate II (#219) - Store last index, check distance
  4. First Unique Character (#387) - Store first index or use Counter

Intermediate

  1. Find All Anagrams (#438) - Collect all indices
  2. Intersection of Two Arrays II (#350) - Track indices
  3. Longest Substring Without Repeating Characters (#3) - Store last index
  4. Minimum Index Sum of Two Lists (#599) - Store indices, find minimum sum

Advanced

  1. Find Duplicate Subtrees (#652) - Serialize as key, store indices
  2. Repeated DNA Sequences (#187) - Store all indices of sequences
  3. Substring with Concatenation of All Words (#30) - Track word indices

Summary

The index mapping pattern is a fundamental technique for tracking element positions.

Key principles:

  • Choose storage strategy: First, last, or all indices
  • Store value as key: Enable O(1) lookup by value
  • Store index as value: What you need when you find the key
  • Optimize space: Use set if index not needed, sliding window to limit size

The templates:

python
# Last index
index_map[num] = i

# First index
if num not in index_map:
    index_map[num] = i

# All indices
index_map[num].append(i)

When to use:

  • ✅ Need to find where element appears
  • ✅ Check distance between occurrences
  • ✅ Track last/first seen position
  • ✅ Return indices (not just values)

Common mistakes:

  • ❌ Storing index as key
  • ❌ Not deciding first vs. last
  • ❌ Not handling missing keys
  • ❌ Using list when not needed

Next steps:

  1. Master the complete hash map guide
  2. Learn Two Sum pattern
  3. Study handling duplicates
  4. Practice the beginner problem set

Master index mapping, and position-tracking problems become pattern recognition.

Want to Practice LeetCode Smarter?

LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.

Also compatible with Edge, Brave, and Opera

Related Tutorials