Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Lexicographical Ordering - Understanding that strings are compared character by character, so "10" < "2" because '1' < '2'
Tree/Trie Traversal - Visualizing numbers as a tree where each prefix extends to its children (1 -> 10, 11, ..., 19)
Counting Nodes in a Subtree - Calculating how many numbers exist under a given prefix without enumeration
Binary Search Concepts - Using count-based navigation to skip large portions of the search space
1. Brute Force
Intuition
The simplest approach converts all numbers from 1 to n into strings and sorts them lexicographically. In lexicographical order, "10" comes before "2" because '1' < '2'. After sorting, we simply return the k-th element. This is straightforward but inefficient for large n.
Algorithm
Generate all numbers from 1 to n as strings.
Sort the list of strings lexicographically.
Return the k-th string converted back to an integer.
Numbers in lexicographical order form a tree structure where each prefix leads to its extensions. For example, prefix "1" leads to "10", "11", ..., "19", "100", etc. We can count how many numbers exist under any prefix without enumerating them. Starting at "1", we count how many numbers lie in the subtree rooted at the current prefix. If this count is less than or equal to the remaining k, we skip this entire subtree and move to the next sibling. Otherwise, we descend into the subtree by appending a digit.
Algorithm
Define count(cur) to calculate how many numbers in [1, n] have cur as a prefix.
Start with cur = 1 and i = 1 (position in lexicographical order).
While i < k:
Calculate steps = count(cur).
If i + steps <= k, move to the next sibling: cur++ and i += steps.
Otherwise, descend into children: cur *= 10 and i++.
classSolution:deffindKthNumber(self, n:int, k:int)->int:
cur =1
i =1defcount(cur):
res =0
nei = cur +1while cur <= n:
res +=min(nei, n +1)- cur
cur *=10
nei *=10return res
while i < k:
steps = count(cur)if i + steps <= k:
cur = cur +1
i += steps
else:
cur *=10
i +=1return cur
publicclassSolution{publicintfindKthNumber(int n,int k){long cur =1;long i =1;while(i < k){long steps =count(cur, n);if(i + steps <= k){
cur++;
i += steps;}else{
cur *=10;
i++;}}return(int) cur;}privatelongcount(long cur,int n){long res =0;long nei = cur +1;while(cur <= n){
res +=Math.min(nei,(long)n +1)- cur;
cur *=10;
nei *=10;}return res;}}
classSolution{public:intfindKthNumber(int n,int k){longlong cur =1;longlong i =1;while(i < k){longlong steps =count(cur, n);if(i + steps <= k){
cur++;
i += steps;}else{
cur *=10;
i++;}}return(int)cur;}private:longlongcount(longlong cur,int n){longlong res =0;longlong nei = cur +1;while(cur <= n){
res +=min(nei,(longlong)n +1)- cur;
cur *=10;
nei *=10;}return res;}};
classSolution{/**
* @param {number} n
* @param {number} k
* @return {number}
*/findKthNumber(n, k){let cur =1,
i =1;constcount=(curVal)=>{let res =0,
nei = curVal +1;while(curVal <= n){
res += Math.min(nei, n +1)- curVal;
curVal *=10;
nei *=10;}return res;};while(i < k){const steps =count(cur);if(i + steps <= k){
cur++;
i += steps;}else{
cur *=10;
i++;}}return cur;}}
publicclassSolution{publicintFindKthNumber(int n,int k){long cur =1;long i =1;while(i < k){long steps =Count(cur, n);if(i + steps <= k){
cur++;
i += steps;}else{
cur *=10;
i++;}}return(int)cur;}privatelongCount(long cur,int n){long res =0;long nei = cur +1;while(cur <= n){
res += Math.Min(nei,(long)n +1)- cur;
cur *=10;
nei *=10;}return res;}}
funcfindKthNumber(n int, k int)int{
count :=func(cur int64)int64{var res int64=0
nei := cur +1for cur <=int64(n){
res +=min64(nei,int64(n)+1)- cur
cur *=10
nei *=10}return res
}var cur int64=1var i int64=1for i <int64(k){
steps :=count(cur)if i+steps <=int64(k){
cur++
i += steps
}else{
cur *=10
i++}}returnint(cur)}funcmin64(a, b int64)int64{if a < b {return a
}return b
}
class Solution {funfindKthNumber(n: Int, k: Int): Int {funcount(cur: Long): Long {var c = cur
var nei = cur +1var res =0Lwhile(c <= n){
res +=minOf(nei, n.toLong()+1)- c
c *=10
nei *=10}return res
}var cur =1Lvar i =1Lwhile(i < k){val steps =count(cur)if(i + steps <= k){
cur++
i += steps
}else{
cur *=10
i++}}return cur.toInt()}}
classSolution{funcfindKthNumber(_ n:Int,_ k:Int)->Int{funccount(_ cur:Int)->Int{var res =0var c = cur
var nei = cur +1while c <= n {
res +=min(nei, n +1)- c
c *=10
nei *=10}return res
}var cur =1var i =1while i < k {let steps =count(cur)if i + steps <= k {
cur +=1
i += steps
}else{
cur *=10
i +=1}}return cur
}}
Time & Space Complexity
Time complexity: O((logn)2)
Space complexity: O(1)
Common Pitfalls
Confusing Lexicographical Order with Numerical Order
In lexicographical order, "10" comes before "2" because strings are compared character by character. Treating numbers numerically instead of as strings leads to completely wrong results. The sequence starts 1, 10, 100, ..., 2, 20, ....
Integer Overflow in Prefix Counting
When counting numbers under a prefix, the prefix and its neighbor are multiplied by 10 repeatedly. For large n, these values can exceed 32-bit integer limits. Use 64-bit integers (long in Java, long long in C++) for the counting logic.
Incorrect Step Counting Logic
The count function must include the prefix itself plus all its extensions up to n. The formula min(neighbor, n + 1) - cur counts numbers in the current level. A common error is using n instead of n + 1, which excludes the boundary value.
Off-by-One in Position Tracking
The position variable i starts at 1 (representing we are at number 1). When moving to a child, increment i by 1. When skipping a subtree, increment i by the step count. Confusing these increments places you at the wrong number.
Not Handling Single-Digit to Multi-Digit Transitions
When descending from prefix "1" to "10", you multiply by 10. But if 10 > n, you cannot descend further. The algorithm must handle cases where the current prefix exceeds n after multiplication, which the count function naturally handles by returning 0.