1. Introduction to Hash Table Patterns in Interviews
Hash Tables provide O(1) average time complexity for insertion, deletion, and lookup operations. In coding interviews, Hash Tables are the primary data structure for:
- Complement / Target Matching: Eliminating nested O(N²) loops in pair-search problems.
- Frequency Counting & Anagram Grouping: Tracking character and element occurrences.
- Prefix Sum + Hash Map: Finding subarrays with specific sums in single linear passes.
- O(1) Caching (LRU / LFU): Combining maps with doubly linked lists for fast retrieval and eviction.
2. Core Hash Table Interview Questions & Verified Solutions
Q1. Two Sum (Complement Pattern)
Problem Statement: Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
cpp// C++ Solution #include <vector> #include <unordered_map> std::vector<int> twoSum(std::vector<int>& nums, int target) { std::unordered_map<int, int> seen; for (int i = 0; i < nums.size(); i++) { int complement = target - nums[i]; if (seen.find(complement) != seen.end()) { return {seen[complement], i}; } seen[nums[i]] = i; } return {}; }
python# Python Solution def twoSum(nums: list[int], target: int) -> list[int]: seen = {} for i, num in enumerate(nums): complement = target - num if complement in seen: return [seen[complement], i] seen[num] = i return []
- Time Complexity: O(N) | Space Complexity: O(N)
Q2. Group Anagrams (Frequency Signature Pattern)
Problem Statement: Given an array of strings strs, group the anagrams together.
python# Python Solution from collections import defaultdict def groupAnagrams(strs: list[str]) -> list[list[str]]: groups = defaultdict(list) for s in strs: count = [0] * 26 for char in s: count[ord(char) - ord('a')] += 1 groups[tuple(count)].append(s) return list(groups.values())
- Time Complexity: O(N · K) where N is string count and K is max length | Space Complexity: O(N · K)
Q3. Subarray Sum Equals K (Prefix Sum + Hash Map Pattern)
Problem Statement: Given an array of integers nums and an integer k, return the total number of continuous subarrays whose sum equals k.
Step-by-Step Logic:
- If the cumulative sum from index 0 to j is S_j, and from index 0 to i is S_i (where i < j), the sum of subarray between i+1 and j is S_j - S_i.
- If S_j - S_i = k, then S_i = S_j - k.
- Maintain a hash map of prefix sum frequencies. Initialize with
{0: 1}to handle subarrays starting at index 0.
java// Java Solution import java.util.HashMap; import java.util.Map; public int subarraySum(int[] nums, int k) { Map<Integer, Integer> prefixCounts = new HashMap<>(); prefixCounts.put(0, 1); int currentSum = 0; int totalCount = 0; for (int num : nums) { currentSum += num; int targetPrefix = currentSum - k; if (prefixCounts.containsKey(targetPrefix)) { totalCount += prefixCounts.get(targetPrefix); } prefixCounts.put(currentSum, prefixCounts.getOrDefault(currentSum, 0) + 1); } return totalCount; }
- Time Complexity: O(N) | Space Complexity: O(N)
Q4. Longest Consecutive Sequence (Hash Set Boundary Pattern)
Problem Statement: Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence in O(N) time.
javascript// JavaScript Solution function longestConsecutive(nums) { const set = new Set(nums); let maxStreak = 0; for (const num of set) { // Only start sequence if num is the beginning of a streak if (!set.has(num - 1)) { let currentNum = num; let currentStreak = 1; while (set.has(currentNum + 1)) { currentNum += 1; currentStreak += 1; } maxStreak = Math.max(maxStreak, currentStreak); } } return maxStreak; }
- Time Complexity: O(N) | Space Complexity: O(N)
Q5. LRU Cache Design (Hash Map + Doubly Linked List)
Problem Statement: Design a data structure that follows the constraints of a Least Recently Used (LRU) cache with O(1) get and put.
cpp#include <unordered_map> class LRUCache { private: struct Node { int key, val; Node *prev, *next; Node(int k, int v) : key(k), val(v), prev(nullptr), next(nullptr) {} }; int capacity; std::unordered_map<int, Node*> map; Node *head, *tail; void addNode(Node* node) { node->next = head->next; node->prev = head; head->next->prev = node; head->next = node; } void removeNode(Node* node) { Node* prevNode = node->prev; Node* nextNode = node->next; prevNode->next = nextNode; nextNode->prev = prevNode; } void moveToHead(Node* node) { removeNode(node); addNode(node); } public: LRUCache(int cap) : capacity(cap) { head = new Node(-1, -1); tail = new Node(-1, -1); head->next = tail; tail->prev = head; } int get(int key) { if (map.find(key) == map.end()) return -1; Node* node = map[key]; moveToHead(node); return node->val; } void put(int key, int value) { if (map.find(key) != map.end()) { Node* node = map[key]; node->val = value; moveToHead(node); } else { if (map.size() == capacity) { Node* lru = tail->prev; map.erase(lru->key); removeNode(lru); delete lru; } Node* newNode = new Node(key, value); map[key] = newNode; addNode(newNode); } } };
- Time Complexity:
get: O(1),put: O(1) | Space Complexity: O(capacity)
3. Summary & Pattern Guide
| Pattern | Technique | Problem Examples | Time | Space |
|---|---|---|---|---|
| Complement Map | Store seen[val] = idx | Two Sum, 4Sum | O(N) | O(N) |
| Frequency Key | 26-char signature tuple | Group Anagrams | O(N · K) | O(N · K) |
| Prefix Sum Map | Store prefixCounts[sum] | Subarray Sum Equals K | O(N) | O(N) |
| Set Boundaries | Only expand if num-1 absent | Longest Consecutive | O(N) | O(N) |
| Map + DLL | Key -> Node with pointer rewiring | LRU Cache | O(1) | O(C) |
Explore 500+ curated DSA coding questions on DSAMaster's practice platform.
