Monotone Increasing Digits
MedPrompt
An integer has monotone increasing digits if and only if each pair of adjacent digits x and y satisfy x <= y.
Given an integer n, return the largest number that is less than or equal to n with monotone increasing digits.
Example 1:
Input: n = 10
Output: 9Example 2:
Input: n = 1234
Output: 1234Example 3:
Input: n = 332
Output: 299
Constraints:
0 <= n <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves checking every integer, starting from n and going downwards, until we find one that has monotone increasing digits. For each integer, we verify the property by comparing its adjacent digits.
Algorithm
- Start a loop with a variable
iinitialized ton. - In each iteration, check if the number
ihas monotone increasing digits. - To perform the check, create a helper function
isMonotone(num):- Convert
numto its string representation,s. - Iterate through the string
sfrom the first character to the second-to-last character. - If at any point
s.charAt(j) > s.charAt(j+1), the number is not monotone, so returnfalse. - If the loop completes without finding such a violation, return
true.
- Convert
- If
isMonotone(i)returnstrue, theniis the largest monotone increasing number less than or equal ton. Returni. - If not, decrement
iand continue the loop. - The loop will eventually terminate because 0 is a monotone increasing number.
Walkthrough
The most straightforward way to solve this problem is to use a brute-force search. We start with the given number n and check if it satisfies the monotone increasing digit property. If it does, we have found our answer since we are looking for the largest number less than or equal to n. If it doesn't, we decrement the number by one and repeat the process. We continue this until we find a valid number. Since 0 is a valid monotone increasing number, this process is guaranteed to terminate.
A helper function can be used to check if a number is monotone increasing. This function would convert the number to a string or an array of digits and then iterate through them, ensuring that each digit is less than or equal to the next one.
Here is a code snippet for this approach:
class Solution { public int monotoneIncreasingDigits(int n) { for (int i = n; i >= 0; i--) { if (isMonotone(i)) { return i; } } return 0; // Should not be reached for n >= 0 } private boolean isMonotone(int num) { String s = String.valueOf(num); for (int i = 0; i < s.length() - 1; i++) { if (s.charAt(i) > s.charAt(i + 1)) { return false; } } return true; }}Complexity
Time
O(N * D), where N is the input number and D is the number of digits in N (D = log₁₀N). In the worst-case scenario (e.g., n = 100000000), we might have to check many numbers. This complexity is too high for the given constraint `n <= 10^9`.
Space
O(D) or O(log n), where D is the number of digits in n. This space is used to store the string representation of the number being checked.
Trade-offs
Pros
Simple to understand and implement.
Correct for all valid inputs, given enough time.
Cons
Extremely inefficient for large values of
n.Will likely cause a 'Time Limit Exceeded' (TLE) error on most online judges for the given constraints.
A much more efficient method is a greedy approach. We can construct the result by modifying the digits of n. The idea is to find the first violation of the monotone property from left to right, say at index i where digit[i-1] > digit[i]. To fix this, we must decrement digit[i-1] and, to maximize the number, set all subsequent digits to '9'. A clever way to implement this is to scan the digits from right to left.
Algorithm
- Convert the input integer
nto a character arrays. - Initialize an integer
markerto the length ofs. Thismarkerwill point to the first digit (from the left) that needs to be changed to '9'. - Iterate through the character array
sfrom right to left, starting from the second to last digit (i = s.length - 2down to0). - In each iteration, compare
s[i]withs[i+1]. - If
s[i] > s[i+1], a violation is found. This means the prefix ending ats[i]is too large.- To correct this, decrement the character
s[i]by one. - Update
markertoi + 1, as all digits from this position onwards must be '9' to maximize the resulting number.
- To correct this, decrement the character
- After the loop finishes, iterate from
markerto the end of the array (j = markertos.length - 1). - Set
s[j]to '9'. - Finally, convert the modified character array
sback to an integer and return it.
Walkthrough
This greedy algorithm works by identifying the point of non-monotonicity and correcting it to form the largest possible valid number. We want to keep the most significant digits as large as possible, which means we want to preserve the prefix of n as much as we can.
We can process the digits of n as a character array. A right-to-left scan is particularly effective. We iterate from the end of the number towards the beginning. If we find a pair of adjacent digits s[i-1] and s[i] such that s[i-1] > s[i], we've found a "cliff". To make the number monotone, we must decrease s[i-1]. After decrementing s[i-1], all digits to its right should be set to '9' to make the resulting number as large as possible. The right-to-left scan naturally handles cases where decrementing a digit causes a new violation with the digit to its left (e.g., in 332, changing the second 3 to 2 makes the number 322, which is still not monotone).
For example, with n = 332:
s = ['3', '3', '2'].- Scan from right. At
i=2, we compares[1]ands[2].3 > 2is a violation. - We decrement
s[1]to'2'. The array becomes['3', '2', '2']. We record that digits from index2onwards should be '9'. - Continue scan. At
i=1, we compares[0]ands[1]. Now3 > 2is a violation. - We decrement
s[0]to'2'. The array becomes['2', '2', '2']. We update our record: digits from index1onwards should be '9'. - After the scan, we apply the change: set digits from index 1 to the end to '9'. The array becomes
['2', '9', '9']. - The result is 299.
Here is the code for this approach:
class Solution { public int monotoneIncreasingDigits(int n) { char[] s = String.valueOf(n).toCharArray(); int len = s.length; int marker = len; // Scan from right to left to find the first cliff for (int i = len - 1; i > 0; i--) { if (s[i] < s[i - 1]) { // Decrement the digit at the cliff s[i - 1]--; // Mark the starting position for filling with 9s marker = i; } } // Fill all digits from the marker to the end with '9' for (int i = marker; i < len; i++) { s[i] = '9'; } return Integer.parseInt(new String(s)); }}Complexity
Time
O(D), where D is the number of digits in n (D = log₁₀n). The algorithm involves a few passes over the digits of the number, making it very fast.
Space
O(D) or O(log n), where D is the number of digits in n. This space is required to store the character array representation of the number.
Trade-offs
Pros
Highly efficient and optimal.
Solves the problem in linear time with respect to the number of digits.
Cons
Slightly more complex to reason about compared to the brute-force approach.
Requires conversion to and from a string/character array representation.
Solutions
Solution
class Solution {public int monotoneIncreasingDigits(int n) { char[] s = String.valueOf(n).toCharArray(); int i = 1; for (; i < s.length && s[i - 1] <= s[i]; ++i) ; if (i < s.length) { for (; i > 0 && s[i - 1] > s[i]; --i) { --s[i - 1]; } ++i; for (; i < s.length; ++i) { s[i] = '9'; } } return Integer.parseInt(String.valueOf(s)); }}class Solution {public: int monotoneIncreasingDigits(int n) { string s = to_string(n); int i = 1; for (; i < s.size() && s[i - 1] <= s[i]; ++i) ; if (i < s.size()) { for (; i > 0 && s[i - 1] > s[i]; --i) { --s[i - 1]; } ++i; for (; i < s.size(); ++i) { s[i] = '9'; } } return stoi(s); }};class Solution: def monotoneIncreasingDigits(self, n: int) -> int: s = list(str(n)) i = 1 while i < len(s) and s[i - 1] <= s[i]: i += 1 if i < len(s): while i and s[i - 1] > s[i]: s[i - 1] = str(int(s[i - 1]) - 1) i -= 1 i += 1 while i < len(s): s[i] = '9' i += 1 return int('' . join(s))Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.