给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字。
注意:1 ≤ k ≤ n ≤ 109。
示例 :
输入:
n: 13 k: 2
输出:
10
解释:
字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/k-th-smallest-in-lexicographical-order
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
本题极难,但是是面试考察的点,需要熟练掌握,首先缕清思路把:
题目中要求找到字典序不大于 n 的第 k 个数字,我们知道字典序的排序是类似树形结构的,如图:
1 是最小的,第二小的是 10,然后是 100,有点类似二叉树的先序遍历,那么,在范围 n 内,如何确定节点呢?
拿 1 分析,如果此时需要找到小于 102 的第 10 个结点,如果判断 1 是往下走到 10,还是往右走到 2,我们只需要判断 在小于 102 的树中,处在 1 下面的节点的个数,如果节点个数大于 k,说明第 k 各节点是在 1 的下面,否则是在 1 的右边,如代码:
while (k > 0) {
long nodes = getNodes(n, cur);
if (nodes > k) {// 当前树节点大于 k,说明在下面,k--,cur 乘以 10
k--;
cur *= 10;
} else {// 反之,在右边的树,k -= nodes,cur 加一
k -= nodes;
cur++;
}
}
具体看注释:
然后题目的难点,就在于如何判断当前树下面有多少节点?
还是之前的例子分析把
如果想看 1 下面有少个节点小于等于 102,只需要让 1 加上每一层的节点个数,需要定义一个变量,指向下一个峰,也就是 2,此时第一层的节点个数就是 next - cur = 1 个,此时 cur 等于 1,n = 102,所以继续加下一层的节点个数
下一个峰是 20,节点个数是 next * 10 - cur * 10,等于 10,此时 cur 等于 10,继续加下一层,此时按照规律,下一层个数应该是 100,但是到 102 就截止了,次层数的节点是 n - cur + 1 个,所以,每次需要使用 Math.min 判断最小的数值是满足层数节点的。
本题难就难在一个思维,整体代码看起来没几行,挺简单的,但是分析起来确实复杂,本题需多次复盘
还有一个坑,本题 n 的范围到 10 的 9 次方,超过 int 的范围了,所以要使用 long 变量
class Solution {
public int findKthNumber(int n, int k) {
long cur = 1;
k--;
while (k > 0) {
long nodes = getNodes(n, cur);
if (nodes > k) {
k--;
cur *= 10;
} else {
k -= nodes;
cur++;
}
}
return (int) cur;
}
public int getNodes(int n, long cur) {
long totalNodes = 0;
long next = cur + 1;
while (cur <= n) {
totalNodes += Math.min(next - cur, n - cur + 1);
next *= 10;
cur *= 10;
}
return (int)totalNodes;
}
}