Link:
题目要求
给定整数n和k,根据字符串顺序排列(e.g. 1, 10, 11, … , 2, 20…) 1到n的所有整数 ,求第k小的数
核心算法和原理
- 朴素排序肯定太慢
- 如果将这些数构建成一颗由1 ~ 9组成的Trie树(左到右1~9),则中序遍历的结果就是这个序列
- 两个相邻数(a and a + 1)之间的排序位置的差 即为 以左边节点为根节点的子树的节点个数,且这是computable的,从这个特性下手
- 计算方法
- 一层一层的计算左边这颗子树的节点个数,每一层节点数为
- 解释:这一层如果满那么就是从a * 10 ^ layer 到 (a + 1) * 10 ^ layer - 1 (e.g. 1000 → 1999),不满(e.g. n = 1524不会出现1 → 5 → 2 → 5)则为 (n+1) - a * 10 ^ layer ( 1000 → 1524 )
- 层层叠加到a * 10 ^ layer恰好小于等于n即可
- 算法过程
- 从a = 1开始,用i记录a现在是第几小的数
- 当i < k时,计算a 和 a+1之间的节点差gap
- 若i + gap ≤ k,说明第k大的数的节点一定在 以大于等于a + 1 为根节点的子树里,将a设为右边的节点( a++ ),重复2
- 若非,则说明就在以a为根节点的子树里,将a设为最左的直接子节点( a *= 10 ),重复2
- 此时得到的a就是第k小的数
- 复杂度分析
- 时间复杂度O (8 · lgN · lgN)(算gap次数 + 遍历层数 + 横向移动次数)
代码
class Solution { public: int findKthNumber(int n, int k) { int i = 1, gap = 0; int a = 1; while (i < k) { gap = getGap(a, a + 1, n); if (i + gap <= k) { a++; i += gap; } else { a *= 10; i++; } } return a; } int getGap(long a, long b, long n) { int gap = 0; while (a <= n) { gap += min(b, n + 1) - a; a *= 10; b *= 10; } return gap; } };
