文章目录
字典序排数
386. 字典序排数 - 力扣(LeetCode) (leetcode-cn.com)
迭代法实现DFS:
满足了题目的复杂度要求!
首先有n个数,那么肯定需要循环n次来计算每一个位置的字典数到底是多少。
然后我们再用一个x
表示在当前次数下应该是哪个字段数
具体地:
- 如果当前数*10<=n那么我们还可以继续按照10为一个步子继续遍历
- 如果不能够那么说明我们已经走到了尽头,接下里是回头落到前面各个位置从0-9去填充数据
- 然后
x++
,就是每次都在从最小位置写0-9(或者0-n)这几个数。
class Solution {
public List<Integer> lexicalOrder(int n) {
List<Integer> res = new ArrayList<>();
int x = 1;
while(res.size() < n){
//优先在该数后面添加0
while(x <= n){
res.add(x);
x *= 10;
}
//将上一位回退并进行加一操作
while(x % 10 == 9 || x > n){
x /= 10;
}
x++;
}
return res;
}
}
复杂度分析:
- 空间复杂度:
O(1)
- 时间复杂度:
O(n)
,其中n为整数的数目
递归法实现DFS
不满足复杂度要求,递归有栈开销!
- 将
[1, n]
的数按照字典序添加到答案,本质上是对一颗节点数量为n
,形态类似字典树的多阶树进行遍历,根节点为0
,需要被跳过,因此我们可以从树的第二层开始搜索。 - 树中每个节点的值为其搜索路径所代表的数字,且每个节点有
[0, 9]
共10
个子节点。
class Solution {
List<Integer> ans = new ArrayList<>();
public List<Integer> lexicalOrder(int n) {
dfs(1, n);
return ans;
}
public void dfs(int start, int n){
for(int i = 0; i <= 9; i++){
int a = start + i;
if(a > n || ans.size() >= n){
return;
}
ans.add(a);
int next = a * 10;
if(next <= n){
dfs(next, n);
}
}
}
}
- 时间复杂度:本质上在搜索一棵节点数量为
n
的多阶树(形态类似于字典树),复杂度为O(n)
调包法:
class Solution {
public List<Integer> lexicalOrder(int n) {
List<Integer> ans = new ArrayList<>();
for(int i = 1; i <= n; i++){
ans.add(i);
}
Collections.sort(ans, (a, b) ->{
return String.valueOf(a).compareTo(String.valueOf(b));
});
return ans;
}
}
字典序中第K小的数字
440. 字典序的第K小数字 - 力扣(LeetCode) (leetcode-cn.com)
本以为有了上一题的基础,这题可以AC了,没想到提交之后:40/69
说明还是哪里出问题了。
下面是看了n多题解后个人认为最容易理解的题解:
-
这里如果直接把for循环的n改成k会超时,故进行优化。
-
注意到题目无需返回序列,因此可以在上述代码的基础上进行剪枝优化:
- 通过计算得到某个节点下的子树节点的总数,设为nodes;
- 如果nodes比K小,那么这个部分则可以全部跳过,第K小的数肯定不在这些节点里面;
- 如果nodes比k大,则从里面找。
作者:livorth-u
链接:https://leetcode-cn.com/problems/k-th-smallest-in-lexicographical-order/solution/by-livorth-u-zvxp/ - 通过计算得到某个节点下的子树节点的总数,设为nodes;
class Solution {
public int findKthNumber(int n, int k) {
long cur = 1;
k--;//减掉k==0的情况,因为只有第一层不是十叉树
while(k > 0) {
// 以cur为根的子树节点有nodes个
int nodes = getNodes(n, cur);
// 如果个数比k少,那么这个部分都可以直接跳过
// 这里有等号,主要是因为上面减1了
if(k >= nodes) {
// 跳过全部
k = k -nodes;
// 往右移一位
cur++;
}
// 如果数量比k多,那么我们要找的结果就一定是以cur下的子节点
else {
// 跳过当前结点
k = k - 1;
// 往下走一层
cur = cur * 10;
}
}
return (int)cur;
}
// 获得以cur为根结点的子树节点数量
private int getNodes(int n, long cur) {
long next = cur + 1;
long totalNodes = 0;
//左闭右开
while(cur <= n) {
// 一次性求出下一层的节点个数和,要是没满就用n来减,要是满了就用next减
totalNodes += Math.min(n - cur + 1, next - cur);
// 进入下一层
next = next * 10;
cur = cur * 10;
}
return (int)totalNodes;
}
}
今日推歌
-----《哪里都是你》 队长
我忘掉你的所有
风里雨里一直大步往前走
我又怎么能够忘掉你的温柔
换不同的场景
但哪里都是你
要怎么能忘记 忘记你
所有人都知道I can’t let you go
每个人都知道 I can’t let you go