Longest Consecutive Sequence looks like a sorting problem, but the trick is to solve it in O(n) with a hash set — and to be clever about which numbers you start counting from.
Problem. Given an unsorted array of integers nums, return the length of the longest run of
consecutive integers (values that differ by 1). The numbers can appear in any order, and you must do
it in O(n) time.
Example: nums = [100, 4, 200, 1, 3, 2] → answer 4 (the run 1, 2, 3, 4).
The slow way first
The obvious idea is to sort the array, then sweep through it counting consecutive runs. That works, but sorting is O(n log n) — and the problem explicitly asks for O(n). We need to find runs without ordering everything.
The question to ask: while I am looking at one number, what do I wish I could check instantly? I wish I could ask "is num + 1 also here?" — and a hash set answers that in O(1).
The idea: only count from a run's start
Dump every number into a set. Now, for each number, I could walk num + 1, num + 2, … counting how far the run goes. But if I did that for every number, I would re-walk the same run over and over — that is O(n²).
The fix: only start walking when num is the beginning of a run. A number num begins a run exactly when num - 1 is not in the set. Then I walk forward from there. Because each number is the start of at most one run, the total walking is O(n).
The key insight: the num - 1 not in set guard is what keeps it O(n). It guarantees we only ever walk a run from its leftmost element, so no number is counted twice.
Walk through it
Step through the animation. 100 and 200 are lonely starts (length 1). 4 is skipped because 3 sits before it — it is not a start. When the pointer reaches 1 (since 0 is absent, 1 is a start), we walk 1 → 2 → 3 → 4, lighting up the whole run, until 5 is missing and the run stops at length 4.
Pseudocode
put every number into a set
longest = 0
for each num in the set:
if num - 1 is NOT in the set: # num begins a run
length = 1
nxt = num + 1
while nxt is in the set: # walk the run forward
length += 1
nxt += 1
longest = max(longest, length)
return longestThe Python solution
def longest_consecutive(nums):
num_set = set(nums)
longest = 0
for num in num_set:
if num - 1 not in num_set:
length = 1
nxt = num + 1
while nxt in num_set:
length += 1
nxt += 1
longest = max(longest, length)
return longestnum_set = set(nums)gives O(1) membership tests and drops duplicates for free.- Line 5 —
num - 1 not in num_set— is the heart of the trick: it only lets us start a run at its leftmost number. - The
whileloop walks forward from a confirmed start, extendinglengthwhile the next integer exists. longest = max(...)keeps the best run seen so far.
Complexity
| Case | Time | Notes |
|---|---|---|
| Sort then sweep | O(n log n) (moderate) | fails the O(n) requirement |
| Hash set (this solution) | O(n) (moderate) | each number visited at most twice |
O(n) (moderate)It looks like the nested while could make it O(n²), but the start guard means each number is touched at most twice — once in the outer loop, once while walking a run — so it is O(n).
When this pattern shows up
When a problem needs O(n) but the naive idea sorts, ask whether a hash set can replace the ordering. Membership tests in O(1) let you "look around" a value without sorting. The deeper move here — doing work only from a canonical starting point so you never repeat it — shows up whenever a simple loop would redo the same scan.
Do not start walking from every number — that is the O(n²) trap. The num - 1 not in set check is what
makes the algorithm linear. Also remember set(nums) removes duplicates, which is exactly what we want.
Practice
For nums = [100, 4, 200, 1, 3, 2], why does the algorithm NOT start counting a run at the number 4?
1. What makes a number num the START of a run?
2. Why is the overall time O(n) and not O(n squared) despite the inner while loop?
3. Why convert nums to a set first?
4. For nums = [1, 2, 0, 1], what is the answer?