题目来源
题目描述
题目解析
字典序,顾名思义,肯定就是按照字典中的顺序对字符串进行排序:实际上也就是从左到右第一个不同字符的大小顺序,谁的第一个不同字符更小,谁的字典序也就更小。举几个例子:
(1)“013”<“12”,第一个字符即为不同字符,‘0’<‘1’
(2)“21”<“23”,第二个字符即为不同字符,‘1’<‘3’
(3)“3242432”<“32484343”,第四个字符即为不同字符,‘2’<‘8’
注意⚠️,字符串的字典序和其长度毫无关系,千万不要混淆。
十叉树先序遍历
思路一
0
|
-----------------------------------
| | | | | | | | |
1 2 3 4 5 6 7 8 9
| | | | | | | | |
10~19 20~29 30~39 ............
对于输入 20,得到的答案是:
[1,10,11,12,13,14,15,16,17,18,19,2,20,3,4,5,6,7,8,9]
可以看出,其实就是上面那棵树的 先序遍历 DFS,只是把大于 20 的数字(还有 0)去掉了。
思路二
将[1, n]
中的所有数字按照字典序排序后,可以发现前缀相同的数字后聚集在一起。比如n=23,输出为[1,10,11,12,13,2,3,4,5,6,7,8,9],前缀为1的[1,10,11,12,13]便会聚集在一起,也就是说实际上是一个是十叉树的先序遍历过程
思路三
将 [1, n]
的数按照字典序添加到答案,本质上是对一颗节点数量为 n
,形态类似字典树的多阶树进行遍历,根节点为 0
,需要被跳过,因此我们可以从树的第二层开始搜索。
树中每个节点的值为其搜索路径所代表的数字,且每个节点有 [0, 9]
共 10
个子节点。
class Solution {
// 1 , 10 , 11 , 12, 13
void helper(int cur, int n, std::vector<int>& res){
if(cur > n){
return;
}
res.push_back(cur);
for (int i = 0; i < 10; ++i) {
helper(cur * 10 + i, n, res);
}
}
public:
vector<int> lexicalOrder(int n) {
std::vector<int> res;
for (int i = 1; i < 10; ++i) {
helper(i, n, res);
}
return res;
}
};
迭代写法
class Solution {
public:
vector<int> lexicalOrder(int n) {
// *10向下, +1向右
vector<int> ret(n);
int number = 1;
for (int i = 0; i < n; i++) {
ret[i] = number;
// 正常往下
if (number * 10 <= n) {
number *= 10;
} else {
// 向右,先回溯
while (number % 10 == 9 || number == n) { //到达边界了
number /= 10; //上一层
}
number++;
}
}
return ret;
}
};