We need to find the longest run of consecutive integers inside an unsorted array. The numbers don't have to be adjacent in the array. They just need to form a sequence like 5, 6, 7, 8 somewhere in the collection.
The constraint that drives everything here is the O(n) time requirement. Sorting gives an O(n log n) solution, but the problem asks us to do better. So the question is how to detect consecutive sequences without sorting.
Every consecutive sequence has a single starting element, the one with no predecessor (num - 1 is absent from the array). If we identify those starting points efficiently and count forward from each, we avoid recounting numbers that sit in the middle of a run.
0 <= nums.length <= 10^5 → With up to 100,000 elements, an O(n^2) solution risks 10^10 operations, so the required O(n) bound rules out repeated linear scans.-10^9 <= nums[i] <= 10^9 → Values span four billion, so index-based structures (counting sort, boolean arrays) would need an impractically large array. A hash-based structure is the practical choice. The values fit in a 32-bit signed integer, and num + 1 on the maximum value 10^9 stays well within int range, so no overflow guard is needed.For every number in the array, build the longest consecutive sequence that starts from it. For each number num, check whether num + 1 exists, then num + 2, and so on, counting until the chain breaks.
Without a fast lookup structure, each existence check scans the entire array. That is the part this approach pays for later.
longestStreak to 0.num in the array:currentNum = num and currentStreak = 1.currentNum + 1 exists somewhere in the array, increment currentNum and currentStreak.longestStreak if currentStreak is larger.longestStreak.For each element, we scan the array for consecutive successors and measure how far the chain extends.
Starting from 1, we find 2, 3, and 4, giving a streak of 4. The starts at 2 and 3 rediscover part of that same run (streaks of 3 and 2), which is the redundant work this approach repeats. The other elements each form a streak of 1, so the answer is 4.
Two costs stack up here: every existence check scans the entire array, and we start a sequence from every element, including those in the middle of an existing run. The next approaches attack each cost in turn, first by ordering the data, then by replacing the linear scan with O(1) lookups.
Sorting brings consecutive numbers next to each other, so a sequence like 5, 6, 7, 8 ends up as an adjacent run. After sorting, a single pass detects every run.
Walk through the sorted array comparing each element to the one before it. If the current element is exactly one larger, extend the current streak. If it equals the previous element, it is a duplicate, so leave the streak unchanged and move on. Otherwise the run breaks and the streak resets to 1. Skipping duplicates matters because a repeated value like 1, 1, 2 would otherwise reset the streak between the two 1s and lose the count.
longestStreak = 1 and currentStreak = 1.nums[i] == nums[i-1] + 1, increment currentStreak.nums[i] != nums[i-1] (skip duplicates), reset currentStreak = 1.longestStreak if currentStreak is larger.longestStreak.Sorting takes O(n log n), but the problem requires O(n). The next approach removes the sort entirely by storing the numbers in a hash set, which answers "does this neighbor exist?" in O(1).
A hash set answers "does this number exist?" in O(1). Putting every number into a set replaces the O(n) array scan from the brute force with a constant-time check.
Fast lookups alone don't fix the redundant counting. Building a sequence from every number repeats work: in [1, 2, 3, 4], starting from 2 counts 2, 3, 4 (length 3), and later starting from 1 counts 1, 2, 3, 4 (length 4), recounting 2, 3, and 4.
The fix is to count only from sequence beginnings. A number begins a sequence exactly when num - 1 is not in the set. For example:
1 starts a sequence (because 0 isn't present)2 does NOT start a sequence (because 1 is present)100 starts a sequence (because 99 isn't present)With this check, each number is visited at most twice: once in the outer loop, and once when counted forward from the start of its sequence. That keeps the total work at O(n).
A while loop inside a for loop looks like O(n^2), but the inner loop only runs when num is a sequence start, and it runs for exactly the length of that sequence. Each consecutive run is counted forward from its smallest element and from nowhere else, so each element is counted at most once across all inner-loop iterations combined. The inner loop therefore does at most n iterations in total, not per outer iteration, giving O(n) overall.
longestStreak = 0.num in the set:num - 1 is NOT in the set, this is the start of a sequence.num + 1, num + 2, etc., incrementing a counter.longestStreak with the counter.longestStreak.