Lexicographical Numbers - Complete Solution Guide
Lexicographical Numbers is LeetCode problem 386, a Medium level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
Given an integer n , return all the numbers in the range [1, n] sorted in lexicographical order. You must write an algorithm that runs in O(n) time and uses O(1) extra space. Example 1: Input: n = 13 Output: [1,10,11,12,13,2,3,4,5,6,7,8,9] Example 2: Input: n = 2 Output: [1,2] Constraints: 1 <= n <= 5 * 10 4
Detailed Explanation
The problem asks us to generate a list of numbers from 1 to `n` (inclusive) and sort them in lexicographical order (dictionary order). For example, with `n = 13`, the lexicographical order is `[1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9]`. Essentially, we want to treat the numbers as strings and sort them as strings, but the output should be a list of integers. The constraint is that we should aim for O(n) time complexity and O(1) extra space (excluding the output list).
Solution Approach
The solution uses an iterative approach to simulate a depth-first search. It starts with the number 1 and iteratively generates the next lexicographically ordered number. The main logic involves three cases: 1. If multiplying the current number by 10 results in a number less than or equal to `n`, we multiply it by 10 (go deeper in the lexicographical tree). 2. Otherwise, if incrementing the current number by 1 doesn't result in a number ending in 0 (meaning the last digit wasn't 9) and remains within the bound of `n`, we increment it by 1 (go to the next sibling). 3. If neither of the above two conditions is met, we need to backtrack to a higher-level node in the lexicographical tree. This is done by repeatedly dividing by 10 until we reach a number whose last digit is not 9. Then, we increment that number by 1.
Step-by-Step Algorithm
- Step 1: Initialize an empty result array of size `n` and initialize the current number `curr` to 1.
- Step 2: Iterate `n` times. In each iteration, add the current number `curr` to the result array.
- Step 3: Check if `curr * 10` is less than or equal to `n`. If it is, update `curr` to `curr * 10` (move to the next level in the lexicographical tree).
- Step 4: If `curr * 10 > n`, check if incrementing `curr` by 1 is possible: check if `curr % 10 != 9` and `curr + 1 <= n`. If it is, update `curr` to `curr + 1` (move to the next sibling in lexicographical order).
- Step 5: If neither step 3 nor step 4 is possible, it means we need to backtrack. Keep dividing `curr` by 10 until `curr % 10 != 9`. After finding such `curr`, divide it by 10 and increment by 1. `curr = curr / 10 + 1`.
- Step 6: Repeat steps 2-5 until the result array is filled.
Key Insights
- Insight 1: Lexicographical order can be thought of as a modified depth-first search (DFS) on a tree where each node's children are its multiples by 10 up to n.
- Insight 2: The core idea is to start with 1, then try multiplying by 10. If the result is within the bound `n`, we continue. Otherwise, we increment by 1. If the last digit is 9, or incrementing causes us to exceed the bound, we backtrack to a higher level of the implicit DFS tree.
- Insight 3: The provided solution implicitly simulates a depth-first traversal of a 'lexicographical tree' to construct the lexicographically sorted sequence.
Complexity Analysis
Time Complexity: O(n)
Space Complexity: O(n)
Topics
This problem involves: Depth-First Search, Trie.
Companies
Asked at: Barclays.