LeetCode 1291 - Sequential Digits
The problem asks us to find all integers within a given range [low, high] whose digits are sequential, meaning that each
Difficulty: 🟡 Medium
Topics: Enumeration
Solution
Problem Understanding
The problem asks us to find all integers within a given range [low, high] whose digits are sequential, meaning that each digit is exactly one greater than the previous digit. For instance, 123 is sequential because 2 = 1 + 1 and 3 = 2 + 1, but 135 is not sequential because the digits do not increase consecutively.
The input consists of two integers, low and high, which define the inclusive range in which we need to find such numbers. The output is a sorted list of integers that satisfy the sequential property within this range.
The constraints indicate that low and high are both at least 10 and at most 10^9. This upper bound rules out brute-force enumeration of all integers, because iterating from 10 to 10^9 would be far too slow. We need a method that generates only the sequential numbers without checking every integer in the range.
Important edge cases include numbers at the boundaries, such as low itself or high itself being sequential. Additionally, we need to consider numbers with different lengths (number of digits), because a 2-digit sequential number is valid, but 1-digit numbers are below the minimum constraint.
Approaches
The brute-force approach is straightforward: iterate over every integer in [low, high] and check if its digits are sequential. This is correct, but extremely inefficient because the range could contain up to a billion numbers. Checking every number is unnecessary since sequential digits form a very sparse set within the range.
The optimal approach leverages the fact that sequential digits are highly structured. All sequential numbers can be generated by starting with digits 1 through 9 and repeatedly appending the next consecutive digit until we exceed the maximum allowed number of digits or the value exceeds high. By systematically generating sequences this way, we avoid iterating over the vast majority of numbers that could never be sequential.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(high - low) | O(1) | Iterates over every number and checks if sequential |
| Optimal | O(1) with respect to range size | O(1) extra | Generates sequential numbers systematically; the total number of sequential numbers is bounded by the number of digits (constant) |
Algorithm Walkthrough
- Initialize a list
resultto store sequential numbers. - Iterate over starting digits from
1to9. - For each starting digit, build numbers by repeatedly appending the next consecutive digit until the number exceeds
highor the last digit becomes9. - After generating a number, check if it is within
[low, high]. If it is, add it to theresultlist. - Continue this process for all starting digits to generate all sequential numbers.
- Finally, sort the
resultlist to ensure ascending order before returning it.
Why it works: This algorithm works because every sequential number is determined entirely by its starting digit and length. By systematically generating all sequences starting from each digit, we are guaranteed to produce every possible sequential number within the range without producing any invalid numbers.
Python Solution
from typing import List
class Solution:
def sequentialDigits(self, low: int, high: int) -> List[int]:
result = []
for start in range(1, 10):
num = start
next_digit = start
while next_digit < 10:
if low <= num <= high:
result.append(num)
next_digit += 1
num = num * 10 + next_digit
return sorted(result)
The Python implementation follows the algorithm closely. We iterate over starting digits 1 through 9 and build numbers sequentially by appending digits. Each time we append, we check if the number falls in the range [low, high]. Finally, we sort the list because numbers generated from smaller starting digits may not automatically be in ascending order.
Go Solution
func sequentialDigits(low int, high int) []int {
var result []int
for start := 1; start <= 9; start++ {
num := start
nextDigit := start
for nextDigit < 10 {
if num >= low && num <= high {
result = append(result, num)
}
nextDigit++
num = num*10 + nextDigit
}
}
// Sort the slice in ascending order
for i := 0; i < len(result)-1; i++ {
for j := i + 1; j < len(result); j++ {
if result[i] > result[j] {
result[i], result[j] = result[j], result[i]
}
}
}
return result
}
In Go, we follow the same logic. Go does not have a built-in sorted function for slices of integers, so we use a nested loop for sorting. Otherwise, the generation of sequential numbers is identical to the Python approach.
Worked Examples
Example 1: low = 100, high = 300
| start | num generation | in range? | added to result |
|---|---|---|---|
| 1 | 1 -> 12 -> 123 | yes | 123 |
| 2 | 2 -> 23 -> 234 | yes | 234 |
| 3 | 3 -> 34 -> 345 | no | - |
| 4-9 | ... | all exceed 300 | - |
Output: [123, 234]
Example 2: low = 1000, high = 13000
| start | num generation | in range? | added to result |
|---|---|---|---|
| 1 | 1 -> 12 -> 123 -> 1234 -> 12345 | 1234, 12345 | 1234, 12345 |
| 2 | 2 -> 23 -> 234 -> 2345 -> 23456 | 2345 | 2345 |
| 3 | 3 -> 34 -> 345 -> 3456 | 3456 | 3456 |
| 4 | 4 -> 45 -> 456 -> 4567 | 4567 | 4567 |
| 5 | ... | 5678 | 5678 |
| 6 | ... | 6789 | 6789 |
| 7+ | exceed 13000 | - | - |
Output: [1234, 2345, 3456, 4567, 5678, 6789, 12345]
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(1) | The number of sequential numbers is bounded by 36 (all sequences from 1 to 9), so generation and sorting is effectively constant |
| Space | O(1) | The result list contains at most 36 numbers, so additional space is constant |
Even though the algorithm contains loops, the total number of sequential numbers is extremely small and independent of the size of low and high.
Test Cases
# test cases
solution = Solution()
assert solution.sequentialDigits(100, 300) == [123, 234] # basic example
assert solution.sequentialDigits(1000, 13000) == [1234, 2345, 3456, 4567, 5678, 6789, 12345] # larger range
assert solution.sequentialDigits(10, 99) == [12, 23, 34, 45, 56, 67, 78, 89] # 2-digit numbers only
assert solution.sequentialDigits(10, 12) == [12] # minimal range with a single valid number
assert solution.sequentialDigits(90, 100) == [] # no numbers in range
assert solution.sequentialDigits(1, 9) == [] # all numbers below 10
assert solution.sequentialDigits(123456789, 987654321) == [123456789] # very large range, only one valid number
| Test | Why |
|---|---|
[100, 300] |
Simple 3-digit range, confirms basic functionality |
[1000, 13000] |
Checks multi-digit generation and sorting |
[10, 99] |
Tests 2-digit sequential numbers |
[10, 12] |
Minimal range with a single valid sequential number |
[90, 100] |
Range with no valid numbers |
[1, 9] |
Below minimum sequential number size |
[123456789, 987654321] |
Large range with only one valid number |
Edge Cases
Edge Case 1: Range below 10, e.g., [1, 9]. Since sequential digits must have at least 2 digits, the algorithm correctly returns an empty list.
Edge Case 2: Range overlaps only partially with valid numbers, e.g., [90, 100]. No sequential numbers exist, but the algorithm still generates sequences and filters by range, returning an empty result.
Edge Case 3: Very large numbers near the upper constraint, e.g., [123456789, 987654321]. The algorithm correctly handles these by generating sequences up to the maximum number of digits possible and includes only valid numbers within the given range. There is no integer