一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第25天,点击查看活动详情。
前言
笔者除了大学时期选修过《算法设计与分析》和《数据结构》还是浑浑噩噩度过的(当时觉得和编程没多大关系),其他时间对算法接触也比较少,但是随着开发时间变长对一些底层代码/处理机制有所接触越发觉得算法的重要性,所以决定开始系统的学习(主要是刷力扣上的题目)和整理,也希望还没开始学习的人尽早开始。
系列文章收录《算法》专栏中。
问题描述
给定整数 n 和 k,返回 [1, n] 中字典序第 k 小的数字。
示例 1:
输入: n = 13, k = 2
输出: 10
解释: 字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。
示例 2:
输入: n = 1, k = 1
输出: 1
提示:
- 1 <= k <= n <= 10^9
剖析
关于字典序,看这个图就好了,可以利用十叉树的结构推算出避免一个一个遍历,跳过一个节点就需要跳过该节点及其所有子节点,如果k大于该节点及其子节点的话就换相邻树。很多时候并不需要构建树,而是利用树的结构。具体还是看代码吧。
代码
package com.study.algorithm.sort;
public class FindKthNumber {
public static int findKthNumber(int n, int k) {
int curr = 1;
k--;
//当k等于0就说明不需要查找了,当前值就是需要的值
while (k > 0) {
//最后返回以当前节点为跟节点包含的节点个数,如果是第一次的话那就是从第一小到第steps小的个数
int steps = getSteps(curr, n);
//如果k大于等于这个个数的话就需要继续往右遍历这一层,不需要往下走
if (steps <= k) {
//减去steps代表从右边从新开始
k -= steps;
//往右遍历,curr当然需要++
curr++;
} else {//如果k小于steps说明在steos范围内,就需要往下检查
//往下就需要*10
curr = curr * 10;
//往下需要--,跳过了一个节点
k--;
}
}
return curr;
}
public static int getSteps(int curr, long n) {
int steps = 0;
long first = curr;
long last = curr;
//只有右边小于等于才能统计steps
while (first <= n) {
//可能不是完全平衡树
steps += Math.min(last, n) - first + 1;
//最前面的节点:继续往下*10
first = first * 10;
//最后面的节点:继续往下*10+9
last = last * 10 + 9;
}
//最后返回以当前节点为跟节点包含的节点个数
return steps;
}
public static void main(String[] args) {
System.out.println(findKthNumber(1300, 15));
}
}