LeetCode 440. 字典序的第K小数字
给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字。
输入:
n: 13 k: 2
输出:
10
解释:
字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。
先来个暴力超时解法:
class Solution:
def findKthNumber(self, n: int, k: int) -> int:
s = []
for i in range(1, n + 1):
s.append(str(i))
s.sort()
return int(s[k - 1])
妥妥的超时
再看题解,十叉树。活久见,接着往下看
十叉树,用题目的测试用例来举例子。
我们求字典序第k个就是上图前序遍历访问的第k节点!但是不需要用前序遍历,如果我们能通过数学方法求出节点1和节点2之间需要走几步,减少很多没必要的移动。
其实只需要按层节点个数计算即可,图中节点1和节点2在第二层,因为n = 13,节点1可以移动到节点2(同一层)所以在第二层需要移动1步。
第三层,移动个数就是 (13 - 10 + 1) = 4 (min(13 + 1, 20) - 10)
所以节点1到节点2需要移动 1 + 4 = 5
步
当移动步数小于等于k,说明需要向右节点移动,图中就是节点1移动到节点2。
当移动步数大于k,说明目标值在节点1和节点2之间,我们要向下移动!即从节点1移动到节点10。
class Solution:
def findKthNumber(self, n: int, k: int) -> int:
cur = 1
k -= 1
while k > 0:
steps = self.cal_step(n, cur, cur + 1)
if steps <= k: # 需要向右节点移动
k -= steps # 先把下一层走完,再往右走
cur += 1 # 往右
else: # 目标值在节点1和节点2之间,要向下移动
k -= 1 # 走一步
cur *= 10 # 往下,1走到10
return cur
# 计算从n1到n2要走的步数
# 例:第一轮计算从1到2的步数,然后判断走的步数和k的大小,判断是在1和2之间还是2之后
def cal_step(self, n, n1, n2):
step = 0
while n1 <= n:
step += min(n2, n + 1) - n1
n1 *= 10
n2 *= 10
return step