Given integers n
and k
, find the lexicographically k-th smallest integer in the range from 1
to n
.
Note: 1 ≤ k ≤ n ≤ 109.
Example:
Input: n: 13 k: 2 Output: 10 Explanation: The lexicographical order is [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9], so the second smallest number is 10.
這道題是之前那道Lexicographical Numbers的延伸,之前讓按字典順序打印數組,而這道題讓我們快速定位某一個位置,那么我們就不能像之前那道題一樣,一個一個的遍歷,這樣無法通過OJ,這也是這道題被定為Hard的原因。那么我們得找出能夠快速定位的方法,我們如果仔細觀察字典順序的數組,我們可以發現,其實這是個十叉樹Denary Tree,就是每個節點的子節點可以有十個,比如數字1的子節點就是10到19,數字10的子節點可以是100到109,但是由於n大小的限制,構成的並不是一個滿十叉樹。我們分析題目中給的例子可以知道,數字1的子節點有4個(10,11,12,13),而后面的數字2到9都沒有子節點,那么這道題實際上就變成了一個先序遍歷十叉樹的問題,那么難點就變成了如何計算出每個節點的子節點的個數,我們不停的用k減去子節點的個數,當k減到0的時候,當前位置的數字即為所求。現在我們來看如何求子節點個數,比如數字1和數字2,我們要求按字典遍歷順序從1到2需要經過多少個數字,首先把1本身這一個數字加到step中,然后我們把范圍擴大十倍,范圍變成10到20之前,但是由於我們要考慮n的大小,由於n為13,所以只有4個子節點,這樣我們就知道從數字1遍歷到數字2需要經過5個數字,然后我們看step是否小於等於k,如果是,我們cur自增1,k減去step;如果不是,說明要求的數字在子節點中,我們此時cur乘以10,k自減1,以此類推,直到k為0推出循環,此時cur即為所求:
class Solution { public: int findKthNumber(int n, int k) { int cur = 1; --k; while (k > 0) { long long step = 0, first = cur, last = cur + 1; while (first <= n) { step += min((long long)n + 1, last) - first; first *= 10; last *= 10; } if (step <= k) { ++cur; k -= step; } else { cur *= 10; --k; } } return cur; } };
類似題目:
參考資料: