Skip to content
beginner Phase 1 · Foundation

Hashing

Learn hash tables, hash maps, and hash-based algorithms for O(1) lookups.

1h 15m
6 problems
Topic Progress 0%

Hash Table Fundamentals

What is a Hash Table?

A hash table is a data structure that maps keys to values using a hash function. It provides O(1) average-time lookup, insertion, and deletion.

How It Works

Key: "apple" → Hash Function → Index: 3

Table:
Index 0: []
Index 1: []
Index 2: []
Index 3: ["apple" → 5.00]  ← stored here
Index 4: []
Index 5: []

The Hash Function

A hash function converts a key into an array index.

// Simple hash function for strings
int hash(String key) {
    int hash = 0;
    for (char c : key.toCharArray()) {
        hash = (hash * 31 + c) % capacity;
    }
    return hash;
}

Why O(1) Average?

  1. Hash function distributes keys evenly
  2. Each bucket has ~1 element on average
  3. Direct access to bucket: O(1)

Java Collections

// HashMap: Key-Value pairs
Map<String, Integer> map = new HashMap<>();
map.put("apple", 5);
int price = map.get("apple");  // 5

// HashSet: Unique elements only
Set<String> set = new HashSet<>();
set.add("apple");
boolean exists = set.contains("apple");  // true

// LinkedHashMap: Maintains insertion order
Map<String, Integer> linked = new LinkedHashMap<>();

// TreeMap: Sorted by key
Map<String, Integer> tree = new TreeMap<>();

Common Operations

Map<String, Integer> map = new HashMap<>();

// Insert
map.put("key", 100);

// Access
int val = map.get("key");           // 100
int val2 = map.getOrDefault("missing", 0); // 0

// Check existence
boolean hasKey = map.containsKey("key");    // true
boolean hasVal = map.containsValue(100);     // true

// Remove
map.remove("key");

// Size
int size = map.size();

// Iterate
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

// Get all keys
Set<String> keys = map.keySet();

// Get all values
Collection<Integer> values = map.values();

Hash Collisions

What is a Collision?

When two different keys hash to the same index.

"apple" → hash → 3
"grape" → hash → 3  ← Collision!

Collision Resolution

1. Chaining (Java's approach)

Each bucket contains a linked list:

Index 3: [("apple", 5) → ("grape", 4)]

2. Open Addressing

Find another empty slot:

Index 3: ("apple", 5)
Index 4: ("grape", 4)  ← probe next slot

When Collisions Happen

  • Many keys hash to same index
  • Load factor too high
  • Poor hash function

Load Factor

Load Factor = (number of elements) / (number of buckets)

Java's HashMap resizes when load factor > 0.75.

Impact on Performance

Load Factor Avg Chain Length Performance
0.1 0.1 O(1)
0.5 0.5 O(1)
0.75 0.75 O(1)
1.0 1.0 O(1)-O(n)
2.0 2.0 O(n) worst

Time Complexity

Operation Average Worst Case
Insert O(1) O(n)
Lookup O(1) O(n)
Delete O(1) O(n)

Worst case happens when all keys hash to same bucket (degrades to linked list).

Frequency Counting

The Pattern

Count occurrences of each element.

// Count character frequencies
String s = "hello";
Map<Character, Integer> freq = new HashMap<>();
for (char c : s.toCharArray()) {
    freq.put(c, freq.getOrDefault(c, 0) + 1);
}
// freq = {h:1, e:1, l:2, o:1}

Using for Arrays

// Count element frequencies
int[] arr = {1, 2, 2, 3, 3, 3};
Map<Integer, Integer> freq = new HashMap<>();
for (int num : arr) {
    freq.put(num, freq.getOrDefault(num, 0) + 1);
}
// freq = {1:1, 2:2, 3:3}

Common Problems

1. Find Most Frequent Element

int maxCount = 0;
int mostFrequent = 0;
for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
    if (entry.getValue() > maxCount) {
        maxCount = entry.getValue();
        mostFrequent = entry.getKey();
    }
}

2. Check if All Characters Are Unique

boolean allUnique(String s) {
    Set<Character> seen = new HashSet<>();
    for (char c : s.toCharArray()) {
        if (!seen.add(c)) return false;
    }
    return true;
}

3. Find First Non-Repeating Character

int firstNonRepeating(String s) {
    Map<Character, Integer> freq = new HashMap<>();
    for (char c : s.toCharArray()) {
        freq.put(c, freq.getOrDefault(c, 0) + 1);
    }
    for (int i = 0; i < s.length(); i++) {
        if (freq.get(s.charAt(i)) == 1) return i;
    }
    return -1;
}

Frequency Counting Template

Map<T, Integer> freq = new HashMap<>();
for (T element : collection) {
    freq.put(element, freq.getOrDefault(element, 0) + 1);
}

When to Use

  • Counting occurrences
  • Finding duplicates
  • Grouping elements
  • Checking permutations/anagrams
  • Two Sum pattern

Hashing Patterns

Pattern 1: Two Sum

Find two numbers that add to target.

int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[] {map.get(complement), i};
        }
        map.put(nums[i], i);
    }
    return new int[] {};
}
// Time: O(n), Space: O(n)

Pattern 2: Grouping

Group elements by some property.

// Group by remainder when divided by k
Map<Integer, List<Integer>> groups = new HashMap<>();
for (int num : nums) {
    int key = ((num % k) + k) % k;  // handle negatives
    groups.computeIfAbsent(key, k -> new ArrayList<>()).add(num);
}

Pattern 3: Subarray Sum

Find subarrays with given sum.

int subarraySum(int[] nums, int k) {
    Map<Integer, Integer> prefixSums = new HashMap<>();
    prefixSums.put(0, 1);
    int sum = 0, count = 0;
    
    for (int num : nums) {
        sum += num;
        if (prefixSums.containsKey(sum - k)) {
            count += prefixSums.get(sum - k);
        }
        prefixSums.put(sum, prefixSums.getOrDefault(sum, 0) + 1);
    }
    return count;
}
// Time: O(n), Space: O(n)

Pattern 4: Anagram Detection

Check if strings are anagrams.

boolean isAnagram(String s, String t) {
    if (s.length() != t.length()) return false;
    
    int[] count = new int[26];
    for (int i = 0; i < s.length(); i++) {
        count[s.charAt(i) - 'a']++;
        count[t.charAt(i) - 'a']--;
    }
    
    for (int c : count) {
        if (c != 0) return false;
    }
    return true;
}

Pattern 5: LRU Cache

class LRUCache extends LinkedHashMap<Integer, Integer> {
    private int capacity;
    
    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }
    
    public int get(int key) {
        return super.getOrDefault(key, -1);
    }
    
    public void put(int key, int value) {
        super.put(key, value);
    }
    
    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > capacity;
    }
}

When to Use Hashing

Problem Type Pattern
Two Sum Complement lookup
Grouping Key-based grouping
Frequency Count occurrences
Anagram Character count
Subarray sum Prefix sum
Caching LRU/LFU

Practice Problems

0 / 6 solved
Two Sum
Hashing

Given an array of integers `nums` and an integer `target`, return the indices of the two numbers such that they add up to `target`. You may assume that each input would have **exactly one solution**, and you may not use the same element twice. You can return the answer in any order.

Example:

Input: nums = [2, 7, 11, 15], target = 9

Output: [0, 1]

Because nums[0] + nums[1] == 9, we return [0, 1].

Edge Cases:

  • Array with exactly two elements
  • Negative numbers in the array
  • Target is zero with positive and negative numbers
  • Duplicate values that form the target pair
Longest Consecutive Sequence
HashSet

Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. Must run in O(n) time.

Example:

Input: nums = [100,4,200,1,3,2]

Output: 4

The longest consecutive sequence is [1,2,3,4].

Edge Cases:

  • Empty array: return 0
  • Single element: return 1
  • All same elements: return n
  • No consecutive elements: return 1
Two Sum
Hash Map

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

Example:

Input: nums = [2,7,11,15], target = 9

Output: [0,1]

Because nums[0] + nums[1] == 9, we return [0, 1].

Contains Duplicate
Hashing

Given an integer array `nums`, return `true` if any value appears **at least twice** in the array, and return `false` if every element is distinct.

Example:

Input: nums = [1, 2, 3, 1]

Output: true

1 appears twice.

Edge Cases:

  • Single element array (no duplicate possible, return false)
  • Array where all elements are the same
  • Large array with no duplicates (must scan entire array)
Two Sum
HashMap

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

Example:

Input: nums = [2,7,11,15], target = 9

Output: [0,1]

Because nums[0] + nums[1] == 9, we return [0, 1].

Edge Cases:

  • Two elements that sum to target: return both indices
  • Negative numbers: still works with HashMap
  • Duplicate values: careful with index storage
Valid Anagram
Frequency Counting

Given two strings `s` and `t`, return `true` if `t` is an anagram of `s`, and `false` otherwise. An **Anagram** is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Example:

Input: s = "anagram", t = "nagaram"

Output: true

Both strings contain the same characters with the same frequency.

Edge Cases:

  • Different lengths (always not an anagram)
  • Single character strings
  • All same characters (e.g., "aaa" and "aaa")
  • Large strings with all 26 letters

Quiz

1. What is the average time complexity of HashMap operations?

Question 1 options

2. What happens when the load factor of a HashMap exceeds 0.75?

Question 2 options

3. What is the worst-case time complexity of HashMap?

Question 3 options

Flashcards

Question

What is the time complexity of HashMap get/put?

Answer

O(1) average, O(n) worst case. Resizing is O(n) amortized.

Question

When should you use HashSet vs HashMap?

Answer

HashSet when you only need to check existence. HashMap when you need key-value mapping.

Question

What is the Two Sum pattern?

Answer

For each element, check if its complement (target - element) exists in the HashMap.

Revision Notes

Key Takeaways

  • 1. HashMap provides O(1) average for get/put
  • 2. Load factor determines when to resize
  • 3. Collisions are resolved via chaining
  • 4. Use HashMap for complement/frequency problems
  • 5. HashSet is ideal for existence checks

Interview Tips

  • HashMap is the most used data structure in interviews
  • Always mention O(1) average vs O(n) worst
  • Discuss load factor when asked about internals
  • Use HashMap to trade space for time

Cheat Sheet

Hashing Cheat Sheet

Key Operations:

Operation Average Worst
Insert O(1) O(n)
Lookup O(1) O(n)
Delete O(1) O(n)

Common Patterns:

  1. Two Sum - complement lookup
  2. Frequency counting - occurrence count
  3. Grouping - key-based grouping
  4. Anagram check - character count
  5. LRU Cache - LinkedHashMap

Java Collections:

  • HashMap: Key-Value
  • HashSet: Unique elements
  • LinkedHashMap: Insertion order
  • TreeMap: Sorted keys