Hashing & Sets · Hashing Fundamentals

Hash Map Frequency Patterns

Counting occurrences with a hash map to answer questions a single pass alone can't.

  • 6 min read
  • 9 practice questions
  • 4 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: a set as an index, and a vote instead of a map
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

"Have I seen this value before, and how many times?" Nested loops answer that in O(n²). A hash map answers each question in O(1) on average, which turns most brute-force counting into a single pass. This skill covers counting with a map, using a set as an index, voting algorithms that need no map at all, and what the map does inside. That last part tells you when its O(1) stops being true.

We'll go from simple to more technical:

  1. The pattern: count in one pass with a map (getOrDefault / merge / Counter).
  2. A second flavor: a set as an index (longest consecutive run) and voting (majority element).
  3. Where frequency counting runs in production (analytics, deduplication, rate limits).
  4. What breaks it: bad keys, mutated keys, fake signatures, unboxing, unbounded growth.

The approach

Java

Map<Integer, Integer> counts = new HashMap<>();
for (int x : nums) {
    counts.merge(x, 1, Integer::sum);          // or counts.getOrDefault(x, 0) + 1
}
for (Map.Entry<Integer, Integer> e : counts.entrySet()) {
    if (e.getValue() > nums.length / 2) return e.getKey();   // e.g. the majority element
}

Python

def majority_by_count(nums: list[int]) -> int:
    counts = {}
    for x in nums:
        counts[x] = counts.get(x, 0) + 1       # or: counts = collections.Counter(nums)
    for value, c in counts.items():
        if c > len(nums) // 2:
            return value                       # e.g. the majority element
    return -1

merge (or getOrDefault) handles the first sighting: counts.put(x, counts.get(x) + 1) throws NullPointerException there, because get returns null and unboxing null fails. Compare counts with equals or as ints - two Integer objects compare equal with == up to 127 (a cached range) and unequal from 128 on.

Python has the same two traps under other names. counts[x] += 1 on a plain dict raises KeyError at the first sighting - use counts.get(x, 0) + 1, collections.Counter or defaultdict(int). And == compares ints by value, but is compares identity: CPython caches -5 to 256, so a is b works on small counts and fails on large ones, exactly like Java's Integer ==.

Inside, hashCode() picks a bucket; keys that land in the same bucket are kept together and told apart with equals(). When the map holds more than 0.75 x its capacity, it doubles the table and redistributes every entry. Since Java 8, a bucket that reaches 8 entries (in a table of at least 64 buckets) turns into a red-black tree. So lookups are O(1) on average, and degrade toward O(n) only when many keys collide - O(log n) for colliding keys that are Comparable. Python's dict gets the same average O(1) differently: one flat table probed by open addressing (no buckets, no trees), resized once it's two-thirds full - and since 3.7 it keeps insertion order.

A second flavor: a set as an index, and a vote instead of a map

Two of this skill's problems need less than a full count.

Longest consecutive run only asks "is x + 1 present?", which a HashSet answers in O(1):

Java

int longestConsecutive(int[] nums) {
    Set<Integer> set = new HashSet<>();
    for (int x : nums) set.add(x);
    int best = 0;
    for (int x : set) {
        if (set.contains(x - 1)) continue;          // not the start of a run - its run is counted elsewhere
        int len = 1;
        while (set.contains(x + len)) len++;
        best = Math.max(best, len);
    }
    return best;
}

Python

def longest_consecutive(nums: list[int]) -> int:
    values = set(nums)
    best = 0
    for x in values:
        if x - 1 in values:
            continue                           # not the start of a run - its run is counted elsewhere
        length = 1
        while x + length in values:
            length += 1
        best = max(best, length)
    return best

Starting only at the first value of each run means every value is walked at most once more, so the whole thing is O(n). Without the check, 1..n costs O(n^2).

Majority element (more than n / 2 copies) needs no map at all. Boyer-Moore voting keeps one candidate and a counter:

Java

int majorityElement(int[] nums) {
    int candidate = 0, count = 0;
    for (int x : nums) {
        if (count == 0) candidate = x;              // adopt a new candidate
        count += (x == candidate) ? 1 : -1;         // a different value cancels one vote
    }
    return candidate;                               // correct when a majority is guaranteed
}

Python

def majority_element(nums: list[int]) -> int:
    candidate, count = 0, 0
    for x in nums:
        if count == 0:
            candidate = x                      # adopt a new candidate
        count += 1 if x == candidate else -1   # a different value cancels one vote
    return candidate                           # correct when a majority is guaranteed

Each decrement pairs one copy of the candidate with one different value, and a value with more than half the elements can't be paired away completely. For "more than n / 3", keep two candidates - and count them again afterwards, because voting always leaves candidates: on [1, 2, 3, 4, 5] it ends with 4 and 5, and neither qualifies.

Where you'll see it

Rate limiting per API key, deduplicating events by ID and top-K analytics are all "count by key, then act on the counts" - backed by Redis instead of an in-process map once the counts must be shared or survive restarts. Grouping by a derived key is the same tool: group emails by lowercased address, or words by their sorted letters to find anagrams.

Which tool for which job?

You needUse
Count every valuea hash map: merge(x, 1, Integer::sum) / Counter
"Is x present?" onlya hash set
A value with more than n/2 copiesBoyer-Moore voting, O(1) space
Values above n/k in a huge streamMisra-Gries with k - 1 counters
Approximate counts for a streama Count-Min sketch (never undercounts)
Small dense integer keysan array
Group by a signaturea real canonical key (sorted letters, a count vector)

Common mistakes

MistakeWhat happensDo this instead
Keys without matching equals/hashCodelookups silently missoverride both, or use records
Mutating a key after putthe entry is stranded: size 1, every get returns nullimmutable keys (tuples, records)
A sum of character codes as an anagram key"ad" and "bc" collidesorted letters or a count vector
== on Integer countstrue at 127, false at 128equals
Trusting vote candidates without recountingwrong answers when there's no majorityverify with a second pass

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why does counts.put(x, counts.get(x) + 1) crash the first time x is seen?

Show answer

get returns null for a missing key, and null + 1 has to unbox null to an int, which throws NullPointerException. Use merge(x, 1, Integer::sum) or getOrDefault(x, 0) + 1.

2. Why is the HashSet solution to longest consecutive sequence O(n) and not O(n^2)?

Show answer

It only starts counting at values whose predecessor is absent - the first value of each run - so each value is stepped over by at most one run walk. Without that check, every value in 1..n would start its own walk.

3. Why does Majority Element II need a second pass after the vote?

Show answer

Voting always ends with up to two candidates, whether or not any value actually appears more than n / 3 times - [1, 2, 3, 4, 5] leaves 4 and 5. Only counting them tells which, if any, qualify.

Key takeaways

  • Count with merge or getOrDefault; compare counts as ints, not Integer objects.
  • O(1) is the average case: buckets, equals, 0.75 load factor, tree bins for heavy collisions.
  • Keys must be real signatures with stable equals/hashCode - and never mutated while in the map.
  • A HashSet plus "start only at the first value of a run" makes longest consecutive O(n).
  • Boyer-Moore voting finds a majority in O(1) space; verify candidates when an answer isn't guaranteed.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Exact countshash map / Counter
Membershiphash set
MajorityBoyer-Moore
Heavy hitters in a streamMisra-Gries
Approximate counts, fixed memoryCount-Min sketch
Small dense keysarray

Snippets worth knowing by heart

Java

Map<Integer, Integer> freq = HashMap.newHashMap(nums.length);   // pre-sized (Java 19+)
for (int x : nums) freq.merge(x, 1, Integer::sum);
boolean same = freq.get(a).equals(freq.get(b));                 // never == on Integer

Python

counters = {}                            # Misra-Gries, k - 1 counters
for x in stream:
    if x in counters:
        counters[x] += 1
    elif len(counters) < k - 1:
        counters[x] = 1
    else:
        for key in list(counters):
            counters[key] -= 1
            if counters[key] == 0:
                del counters[key]

The traps

  • Bad equals/hashCode: lookups silently miss. Override both together, or use records.
  • Mutated keys: change a key after put and its entry is stranded. Use immutable keys.
  • Fake signatures: summing character codes makes "ad" and "bc" collide. Use sorted letters or a count vector.
  • == on Integer: it's true at 127 and false at 128. Compare counts with equals.
  • Unverified candidates: Boyer-Moore returns something even with no majority. Recount the candidate in a second pass.

Staff-level points, one line each

  • Misra-Gries with 19 counters caught all values above n/20 among 1,219 distinct.
  • Count-Min, 4 × 2,000: never under, worst over by 5 on a million items.
  • A mutated List key: size 1, but every get returns null.
  • Integer 127 == 127 is true, 128 == 128 false.
  • A dict growing to 1,000,000 keys resized 19 times. Pre-size.

Say it in an interview

  • "One pass with a map: O(n) average, O(n) space."
  • "For a majority I'd vote in O(1) space and verify if it isn't guaranteed."
  • "For huge streams, Misra-Gries or a Count-Min sketch."
  • "Keys must be immutable, with consistent equals and hashCode."

Practice

This skill comes with 9 multiple-choice questions that tell you which misconception a wrong answer reveals and 4 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Hash Map Frequency Patterns free

Majority Element

MediumProCoding problemJava · Python

One value fills more than half of nums: it appears more than n / 2 times, where n is the length. Every test has such a value. Find it.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Longest Consecutive Sequence

MediumProCoding problemJava · Python

Return the length of the longest run of integers that follow one another, like 7, 8, 9, that you can make from the values in nums. Their order and positions in the array don't matter.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Majority Element II

MediumProCoding problemJava · Python

Find every value that fills more than a third of nums, meaning it appears more than nums.length / 3 times (integer division). There can be at most two such values. Return them sorted ascending.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Group Anagrams

MediumProCoding problemJava · Python

Group the words that are anagrams of each other (the same letters, the same number of times). Return the groups in any order, with the words inside each group in any order; the driver sorts both and prints one group per line.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Hash Map Frequency Patterns free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime