LeetCode #228 Easy

Summary Ranges

Summary Ranges: given a sorted unique integer array nums, return the smallest sorted list of ranges that cover every number in the array exactly.

Constraints
  • 0 <= nums.length <= 20
  • -2³¹ <= nums[i] <= 2³¹ - 1
  • All the values of nums are unique.
  • nums is sorted in ascending order.
arrays
Open on LeetCode ↗
02

Intuition

Summary ranges takes a sorted array of distinct integers and returns the smallest list of ranges covering every element. Because the input is sorted with no duplicates, consecutive integers form contiguous runs, and the whole task is finding where each run ends. The rule is simple: - A run continues while the next element is exactly one greater than the current, and breaks the moment it is not. So walk the array remembering where the current run started. When the difference to the next element exceeds one — or you reach the end — the run is complete and gets recorded. The only formatting subtlety is that a run of length one is written as the bare number, not as "5->5". That check is a comparison between the run's start index and its end index. Two details are worth being careful about. The last run has no successor to break it, so the loop must close whatever is open when it ends — forgetting this drops the final range, which is the standard bug here. And the difference test should compare values, not positions. Since the array is distinct and sorted, nums[i+1] - nums[i] == 1 is the correct condition; using index arithmetic instead happens to work only because duplicates are excluded. One caution for fixed-width languages: the subtraction can overflow when the array spans the full integer range. Comparing nums[i+1] != nums[i] + 1 avoids it.

How to spot this pattern

When a sorted array asks you to group consecutive sequences, the pattern is a single pass that tracks the start of each run and closes it when the sequence breaks. The format varies — here it is range strings — but the grouping logic is always the same: compare each element to its successor.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what you would need to remember from the left to avoid re-scanning. Aim for O(n) time and O(1) space.

1

Handle the empty array first

Return an empty list immediately if the input is empty. The main loop assumes at least one element exists to open a run, so this check keeps the rest free of guards.

2

Remember where each run starts

Keep a start index marking the beginning of the current run. This single variable is all the state needed — the current position and the start together describe the whole open range.

3

Continue while values are consecutive

The run extends while nums[i+1] == nums[i] + 1. Comparing values rather than indices is what makes the test correct, and phrasing it as an equality rather than a subtraction avoids overflow when the array spans the integer range.

4

Close the run on a break or at the end

When the next element is not consecutive, or the array ends, the run from start to i is complete. Closing at the end is essential — the last run has no successor to break it, and forgetting this drops the final range.

5

Format single elements without an arrow

If start == i the run holds one number, so emit it bare rather than as "5->5". Otherwise emit "start->end" using the values, not the indices.

6

Cost of the single pass

One traversal with constant work per element gives O(n) time and O(1) space beyond the output list, whose size depends on how fragmented the input is.

04

Solution & live demo

▶1class Solution:
▶2 def summaryRanges(self, nums):
▶3 result = []
▶4 i = 0
▶5 n = len(nums)
▶6 while i < n:
▶7 start = i
▶8 while i + 1 < n and nums[i + 1] == nums[i] + 1:
▶9 i += 1
▶10 if start == i:
▶11 result.append(str(nums[start]))
▶12 else:
▶13 result.append(str(nums[start]) + '->' + str(nums[i]))
▶14 i += 1
▶15 return result
05

Common pitfalls

Using nums[i+1] - nums[i] == 1 without bounds checking

✗ Wrong
for i in range(len(nums)):
    if nums[i+1] - nums[i] == 1:
✓ Right
for i in range(len(nums)):
    if i + 1 < len(nums) and nums[i+1] - nums[i] == 1:

On the last element, nums[i+1] is an index-out-of-bounds error. The bounds check ensures you only compare when a next element exists.

Formatting single-number ranges with an arrow

✗ Wrong
result.append(f'{nums[start]}->{nums[i]}')
✓ Right
if start == i:
    result.append(str(nums[start]))
else:
    result.append(f'{nums[start]}->{nums[i]}')

A range like 5->5 is redundant and does not match the expected output format. Single numbers should be formatted without the arrow.

Resetting start to i instead of i + 1 after closing a range

✗ Wrong
start = i
✓ Right
start = i + 1

Index i was the end of the last range. The new range starts at i + 1. Setting start = i includes the last element of the previous range in the next range, duplicating it.

06

Edge cases

Empty array

The loop does not run. Return an empty list.

Single element

One range: just that element as a string.

All elements are consecutive

One range covering the entire array: nums[0]->nums[-1].

07

Complexity

Time
O(n)
Space
O(1)
Single pass. Output list is not counted as extra space.