算法实例教学:字典序的第K小数字(二)

发布时间:2026/7/21 17:28:11
算法实例教学:字典序的第K小数字(二) 接上文小编继续来讲字典序的第K小数字。通过观察可以发现前序遍历该字典树即可得到字典序从小到大的数字序列遍历到第 k 个节点即为第 k 小的数字。我们可以构造字典树并通过前序遍历求得目标节点时间复杂度为 O(k)。实际不需要构造字典树已知节点 i 的子节点为 (10 × i,10 × i 1,⋯,10 × i 9)可以通过计算找到前序遍历第 k 个节点即可。设当前的字典树的第 i 小的节点为 ni 则只需按照先序遍历再继续往后找 k−i 个节点即为目标节点。设与 ni 右侧相邻的兄弟节点为 ni1 按照先序遍历的顺序先访问 ni 构造的子树再访问 ni1 此时满足 ni ni1设以 ni 为根节点构成的子树的节点数目为 step(ni) 则此时有以下两种可能如果满足 step(ni) ≤ k−i则此时可以肯定第 k 小的节点一定不在 ni 为根的子树中存在于 ni 的兄弟节点构成的子树中则此时可以跳过 step(ni) 个节点从兄弟节点 ni1 开始往后查找 k − i − step(ni) 个节点。如果满足 step(ni) k−i则此时可以肯定第 k 小的节点一定在 ni 构成的子树中则此时需要在 ni 的孩子节点中依次进行查找。此时跳过子树中最小的根节点 ni 从左侧第一个孩子开始往后查找 k − i − 1 个节点此时左侧第一个孩子的值为 10 × ni 。依次重复上述操作直到找到 k 小的节点即可。在这里比较难以处理的是如何计算 step(ni) 即找到以 ni 为根的子树下有多少节点。可以按照层次遍历子树firsti 指向第 i 层的最左侧的孩子节点 lasti 指向第 i 层的最右侧的孩子节点根据推理可以知道: firsti 10 × firsti − 1 lasti 10 × lasti − 1 9 第 i 层共有 lasti − firsti 1 个节点由于所有的节点都需要满足小于等于 n 所以第 i 层的最右侧节点应该为 min(n,lasti) 不断迭代直到 firsti n 则终止向下搜索。实际上可以观察到最终结果一定是沿着字典树从根节点到某个叶子节点的路径进行搜索。代码Javaclass Solution { public int findKthNumber(int n, int k) { int curr 1; k--; while (k 0) { int steps getSteps(curr, n); if (steps k) { k - steps; curr; } else { curr curr * 10; k--; } } return curr; } public int getSteps(int curr, long n) { int steps 0; long first curr; long last curr; while (first n) { steps Math.min(last, n) - first 1; first first * 10; last last * 10 9; } return steps; } }Cclass Solution { public: int getSteps(int curr, long n) { int steps 0; long first curr; long last curr; while (first n) { steps min(last, n) - first 1; first first * 10; last last * 10 9; } return steps; } int findKthNumber(int n, int k) { int curr 1; k--; while (k 0) { int steps getSteps(curr, n); if (steps k) { k - steps; curr; } else { curr curr*10; k--; } } return curr; } };C#public class Solution { public int FindKthNumber(int n, int k) { int curr 1; k--; while (k 0) { int steps GetSteps(curr, n); if (steps k) { k - steps; curr; } else { curr curr * 10; k--; } } return curr; } public int GetSteps(int curr, int n) { long steps 0; long first curr; long last curr; while (first n) { steps Math.Min(last, n) - first 1; first first * 10; last last * 10 9; } return (int) steps; } }复杂度分析时间复杂度O(log2n) 其中 n 为给定的数值的大小。每次计算子树下的节点数目的搜索深度最大为 log10n 最多需要搜索 log10n 层每一层最多需要计算 10 次最多需要计算 10×(log10n)2 次因此时间复杂度为 O(log2n) 。空间复杂度O(1) 不需要开辟额外的空间只需常数空间记录常量即可。