128. Longest Consecutive Sequence
Quick Identifier
- Topic: arrays-hashing
- Pattern: Hash Set (Sequence Building)
- Hint: Throw everything into a Hash Set. Then, to avoid $O(N^2)$ repeated work, only start counting a sequence if the current number is the absolute beginning of a sequence. How do you know it's the beginning? Because
currentNumber - 1doesn't exist in the set. - Difficulty: Medium
- Companies: Google, Amazon, Meta, Bloomberg, Microsoft
- LeetCode: 128
Quick Sights
- Time Complexity:
O(n). Building the set takes $O(n)$. Then, although there is awhileloop inside aforloop, the inner loop only runs for numbers that are the start of a sequence. Thus, every number is visited at most twice (once in the outer loop, once in the inner loop). Total time is $O(n)$. - Space Complexity:
O(n). The Hash Set stores all unique numbers from the array. - Core Mechanism: Convert the array to a Hash Set for $O(1)$ lookups. Iterate through the set. For each
num, check ifnum - 1exists.- If it does exist, skip it. This
numis just a middle piece of some sequence. - If it doesn't exist, this
numis the start of a sequence. Enter awhileloop, checking fornum + 1,num + 2, etc., keeping alengthtally. Keep track of the maximumlengthseen.
- If it does exist, skip it. This
Concept Explanation
As a senior engineer, the strict constraint here is O(n) time. The moment you see that constraint on an unsorted array problem looking for sequences or pairs, your brain must immediately ban the .sort() function ($O(n \log n)$) and reach for a Hash Set.
- The Brute Force Trap: If you throw everything into a set, and then for every number, you count upward as high as you can go, you will recount the same sequences over and over. If the array is
[1, 2, 3, 4], checking1counts to 4. Checking2counts to 4. Checking3counts to 4. That degrades to $O(N^2)$ in the worst case (a single massive sequence). - The "Start of Sequence" Invariant: How do we prevent recounting? We just need to identify the "Head" of the sequence.
- Is
2the head? No, because1is in the set. - Is
3the head? No, because2is in the set. - Is
1the head? Yes, because0is not in the set.
- Is
- The Pruning: By adding a simple
if (!set.has(num - 1))check, we instantly prune all middle-of-sequence calculations. We only ever trigger the innerwhileloop from the absolute bottom of a chain, guaranteeing strict $O(N)$ linear time.
Diagram
This flowchart outlines the intelligent Hash Set pruning logic.
Loading diagram…
Study Pattern (SDE3+)
- 0–3 min: Immediately point out the
O(n)constraint and rule out sorting. Introduce the Hash Set. Crucially, explain why the naive Hash Set approach is $O(n^2)$ and how the!set.has(num - 1)check mathematically collapses the time complexity back to amortized $O(n)$. - Implementation pass: In languages like Go without a native Set, use a
map[int]struct{}for zero-memory boolean tracking. Be sure to iterate over the Set (or keys of the map), not the original array, to naturally skip duplicate numbers right out of the gate. - 5 min extension: What if the interviewer says, "Good, but now do it in $O(1)$ space"? The only way to do it in $O(1)$ space is to sort the array, which takes $O(n \log n)$ time. You would explain the trade-off: you can't have both $O(n)$ time and $O(1)$ space for this specific problem unless the numbers are bounded within a very small range (allowing a fixed-size boolean array).
Approaches
- Sorting — Sort the array, then iterate through to find the longest streak. Time: $O(n \log n)$, Space: $O(1)$ (or $O(n)$ depending on sort). Suboptimal due to time constraint.
- Naive Hash Set — Throw in a set, count upward from every element. Time: $O(n^2)$, Space: $O(n)$. Suboptimal.
- Intelligent Hash Set — Throw in a set, only count upward if
num-1is missing. Time: $O(n)$, Space: $O(n)$. (Always pick this)
Optimal Solution
Go
func longestConsecutive(nums []int) int {
// Create a set using a map with an empty struct (zero memory allocation)
numSet := make(map[int]struct{}, len(nums))
for _, n := range nums {
numSet[n] = struct{}{}
}
longestSeq := 0
// Iterate through the unique numbers in the set
for num := range numSet {
// Pruning: Only start counting if this number is the start of a sequence
if _, hasPrev := numSet[num-1]; !hasPrev {
currentNum := num
currentStreak := 1
// Walk upward as long as the consecutive number exists
for {
if _, hasNext := numSet[currentNum+1]; hasNext {
currentNum++
currentStreak++
} else {
break
}
}
// Update global max
if currentStreak > longestSeq {
longestSeq = currentStreak
}
}
}
return longestSeq
}JavaScript
function longestConsecutive(nums) {
// Load all numbers into a Hash Set for O(1) lookups
const numSet = new Set(nums);
let longestSeq = 0;
for (const num of numSet) {
// Pruning: Only start counting if 'num' is the start of a sequence
// (Meaning the number just below it doesn't exist)
if (!numSet.has(num - 1)) {
let currentNum = num;
let currentStreak = 1;
// Walk upward through the consecutive sequence
while (numSet.has(currentNum + 1)) {
currentNum++;
currentStreak++;
}
// Update global max
longestSeq = Math.max(longestSeq, currentStreak);
}
}
return longestSeq;
}Walkthrough
Input: nums = [100, 4, 200, 1, 3, 2]
numSet = {100, 4, 200, 1, 3, 2}
num | Is num - 1 in Set? | Action | while loop (Counting) | longestSeq |
|---|---|---|---|---|
100 | No (99 missing) | Start counting | Finds 100. (101 missing) | 1 |
4 | Yes (3 exists) | Skip | - | 1 |
200 | No (199 missing) | Start counting | Finds 200. (201 missing) | 1 |
1 | No (0 missing) | Start counting | Finds 1, 2, 3, 4. (5 missing) | max(1, 4) = 4 |
3 | Yes (2 exists) | Skip | - | 4 |
2 | Yes (1 exists) | Skip | - | 4 |
Output: 4 (The sequence [1, 2, 3, 4]).
Edge Cases
- Empty array. Correctly bypasses the loops and returns
0. - Array with duplicates (e.g.,
[1, 2, 0, 1]). The Set automatically collapses duplicates, so the algorithm perfectly counts the sequence[0, 1, 2]without being thrown off by the extra1.
Pitfalls
- Iterating over the original
numsarray instead of thenumSet. If the array is[1, 1, 1, 1, 1, 2], and you iterate overnums, you will trigger the start-of-sequence logic for the1five different times! Iterating over theSetguarantees you only check1once. - Misunderstanding the $O(n)$ proof. A nested loop usually means $O(n^2)$, but because the
ifstatement ensures thewhileloop only runs exactly once per sequence, the total number of operations inside thewhileloop across the entire execution of the program will never exceed $N$. $O(N + N) = O(N)$.
Similar Problems
- 217. Contains Duplicate — Foundational Hash Set usage.
- 049. Group Anagrams — Hash Map state grouping.
- Number of Connected Components in an Undirected Graph — The graph theory analogue to finding isolated "sequences" (components).
Mind-Map Tags
#arrays #hashset #sequence-building #amortized-on #pruning
Mark this page when you finish learning it.
Last updated on
Spotted something unclear or wrong on this page?