DSAPrep
MediumArrays & Hashing

Longest Consecutive Sequence

Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence.

You must write an algorithm that runs in O(n) time.

Example 1

Input: nums = [100,4,200,1,3,2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

Example 2

Input: nums = [0,3,7,2,5,8,4,6,0,1]
Output: 9

Example 3

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

Constraints

  • 0 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9
View original on LeetCode โ†—

Sorting would make consecutive runs trivial to spot, but that costs O(n log n) and the problem demands linear time. The trick to get there without sorting: put every number in a hash set, then only ever start counting a run from a number that is the start of one โ€” a number whose predecessor (num - 1) is not in the set. Every element gets examined by at most two constant-time set lookups over the whole algorithm, never revisiting the middle of a run twice.

Sort First

Time O(n log n)Space O(n)

Sort the (deduplicated) numbers, then scan once counting how long each run of consecutive values lasts.

class Solution:
def longestConsecutive(self, nums: list[int]) -> int:
if not nums:
return 0
nums = sorted(set(nums))
longest = 1
current = 1
for i in range(1, len(nums)):
if nums[i] == nums[i - 1] + 1:
current += 1
longest = max(longest, current)
else:
current = 1
return longest

Correct and only O(n) after sorting, but the sort itself is O(n log n) โ€” more than the follow-up allows.

Hash Set, Start-of-Sequence Only

OptimalTime O(n)Space O(n)

Put every number in a set. For each number, only start extending a sequence if it is a sequence start โ€” meaning num - 1 is not in the set. From a true start, walk forward (num + 1, num + 2, โ€ฆ) counting how far the run goes. Numbers in the middle of a run are skipped as starting points entirely, so each element is only ever walked over once across the whole algorithm.

class Solution:
def longestConsecutive(self, nums: list[int]) -> int:
num_set = set(nums)
longest = 0
for num in num_set:
if num - 1 not in num_set:
length = 1
while num + length in num_set:
length += 1
longest = max(longest, length)
return longest

Tracing nums = [100, 4, 200, 1, 3, 2] (set built from all six numbers; only sequence starts get expanded):

100
0
4
1
200
2
1
3
3
4
2
5
num = 1

Hash Map

100 โ†’ in set4 โ†’ in set200 โ†’ in set1 โ†’ in set3 โ†’ in set2 โ†’ in set
1 / 7
comparingresultcurrentdiscarded

num=1. Is 0 in the set? No -- 1 is a sequence start. Walk forward.

Numbers 4, 3, and 2 are never used as starting points at all โ€” each has num - 1 in the set, so the if skips them, which is exactly what keeps the total work linear.

Correctness: every consecutive run has exactly one true start (the number with no predecessor in the set), so every run gets expanded from that start exactly once โ€” no run is ever counted twice, and no run is ever missed, since every number that is a start is checked.

Complexity: building the set is O(n). Across the whole loop, the inner while only advances through numbers that belong to a run being expanded from its true start, so every number is visited by the inner loop at most once in total โ€” O(n) time overall, not O(nยฒ). The set holds up to n numbers โ€” O(n) space.