Problem
Given an unsorted array of integers, find the length of the longest run of consecutive integers present in it. The numbers can appear in any order, and you must solve it in O(n) time.
Example:
- Input:
nums = [100, 4, 200, 1, 3, 2] - Output:
4 - Explanation: The numbers 1, 2, 3, 4 are all present and form the longest consecutive chain.
Counter-example: [4, 6, 8] โ output 1, since no two of those numbers differ by exactly 1.
Intuition
Think of the numbers as beads that snap together when they differ by 1 โ the problem asks for the longest chain of snapped beads. If we store all numbers in a hash set for O(1) lookup, we can check whether any neighbor exists instantly. The key insight is to only start building a chain from the smallest number in that chain โ a number is a chain start if and only if its predecessor (num - 1) is absent from the set. This prevents counting the same chain multiple times and gives O(n) total work.
Approach 1 โ Sort
Sort the array and scan it once. Whenever the next number is exactly 1 greater than the previous, extend the current streak. Skip duplicates. Reset on a gap.
Steps:
- Return 0 immediately on an empty array
- Sort the array in ascending order
- Walk through with a running
current_streak(starts at 1) and amax_streak - If the current number equals the previous number plus 1, extend
current_streak - If the current number equals the previous number exactly, it's a duplicate โ skip without resetting
- Otherwise a gap was found โ reset
current_streakto 1
1def longestConsecutive(nums):
2 if not nums:
3 return 0
4
5 nums.sort()
6 max_streak = 1
7 current_streak = 1
8
9 for i in range(1, len(nums)):
10 if nums[i] == nums[i - 1] + 1:
11 current_streak += 1
12 max_streak = max(max_streak, current_streak)
13 elif nums[i] != nums[i - 1]: # equal means duplicate, not a gap
14 current_streak = 1
15
16 return max_streak- Time: O(n log n) โ dominated by sorting
- Space: O(1) โ no extra data structures beyond the sort's internal stack (O(log n) in practice)
Approach 2 โ Hash Set (Optimal)
Load all numbers into a hash set. For each number that is a chain start (has no predecessor in the set), walk forward through the chain until it ends. Track the maximum chain length seen.
Steps:
- Insert every number into a hash set for O(1) membership tests
- Initialize
max_streak = 0 - For each number in the set, skip it if
num - 1is present (it belongs to a longer chain starting earlier) - For numbers that ARE chain starts, extend: increment
current_numwhilecurrent_num + 1is in the set - Update
max_streakonce each chain is fully counted - Return
max_streak
1def longestConsecutive(nums):
2 num_set = set(nums)
3 max_streak = 0
4
5 for num in num_set:
6 if num - 1 not in num_set: # num is the start of a new chain
7 current_num = num
8 current_streak = 1
9
10 while current_num + 1 in num_set:
11 current_num += 1
12 current_streak += 1
13
14 max_streak = max(max_streak, current_streak)
15
16 return max_streak- Time: O(n) โ each number is inserted once and extended through at most once across all chain walks
- Space: O(n) โ the hash set holds every unique number
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Sort | O(n log n) | O(1) | When extra memory is prohibited or the input is nearly sorted |
| Hash Set | O(n) | O(n) | The standard answer when O(n) time is required by the problem |
Common Mistakes
- Not skipping duplicates in the sort approach: If
nums[i] == nums[i-1], resettingcurrent_streakto 1 would give the wrong answer for inputs like[1, 1, 2]. The check for equality must be explicit. - Iterating the original array instead of the set in Approach 2: Correctness is unaffected, but if the array has many duplicates every duplicate triggers its own
num - 1check, adding wasted work (though still O(n) overall). Iterating the set is cleaner. - Forgetting the chain-start guard: Without the
num - 1 not in numSetcheck, every element would start a chain walk, making the worst-case O(nยฒ) โ e.g., for[1, 2, 3, โฆ, n]each element walks to the end. - Off-by-one on streak initialization: Initializing
current_streak = 0before the while loop and incrementing inside causes the start element itself not to be counted, producing an answer that is 1 too low for every chain. - Returning 0 for a single-element array:
max_streakstarts at 0 and updates only inside the chain walk loop. For[42], the chain walk finds no neighbors so the update still executes asmax(0, 1) = 1. The logic is correct as long ascurrent_streakis initialized to 1 before the while loop.
Related Problems
two-sumโ same pattern: use a hash set/map so that "does X exist in the array?" becomes O(1) instead of O(n)contains-duplicateโ direct hash set membership check; the simplest form of the same ideafind-all-numbers-disappeared-in-an-arrayโ finding gaps in an integer range, closely related to reasoning about consecutive sequencessubarray-sum-equals-kโ hash map used to detect a target sum in O(1), applying the same "store and look up" strategy to a contiguous-subarray problemvalid-anagramโ hash map counting; reinforces the pattern of using auxiliary hash structures to answer existence/frequency questions in linear time