之前有小伙伴面试就被问到了字典序
首先说一个基础的字典序:
题目1: 已知一个数字 m ,求仍有m所含数字组成的最小的数字。
比如当前数字是 34672 下一个比他大的怎么找呢?
我们应该让低位尽可能变大一点就好
不改变个位以上的数字,个位及以下能否变大?不能
不改变十位以上的数字,十位及以下能否变大?不能
不改变百位以上的数字,百位及以下能否变大?
6以下有比6大的数字,故可以变大
- 找到6右面最小的比6大的数字,两个交换,变成了34762
- 然后由于百位已经变大了,所以百位以下要尽可能小,才能使得变化最小所以要把 6 2 尽量变小->变为升序 26 就可以了。
思路总结:
从个位往高位扫描,找到第一个 高位<低位的位置
将高位换成他右边尽可能小的比他大的数字
将他右边变成 高位<=低位的排序
举例子: 2 5 7 6 5 4 3 2 1 -> 2 5 7 6 5 4 3 2 1-> 2 6 7 5 5 4 3 2 1-> 2 6 1 2 3 4 5 5 7
题目2: 字典序排列:
算法起点: 字典序最小排列 1- n eg: 1 2 3 4 5
算法终点: 字典序最大排列 n-1 eg: 5 4 3 2 1
算法执行过程 (like 题目1 )
- 找到排列中最右一个升序的首位置 x=ai(上面的5)
- 找到排列中第 i 位右边最后一个比 ai大的位置 y=aj
- 交换 x 和 y
- 将i+1位到最后部分反转
题目3: 从小到大打印 1到 n位所有数字
看起来觉着还可以? 但是直接操作会有坑:直接加法会导致溢出 if n很大
所以我们应该用字符串去模拟加法:
写法和递归全排列差不多。
public void solve(int n){
if(n<=0)
return;
String temp="";
for(int i=0;i<10;i++){
String p=temp;
nextmax(n,p+String.valueOf(i),1);
}
}
public void nextmax(int length,String temp,int index){
//print
if(index>=length){
int k=0;
while(k<temp.length()){
if(temp.charAt(k)!='0') break;
k++;}
System.out.println(temp.substring(k));
return;
}
//next
for(int i=0;i<10;i++){
String p=temp;
nextmax(length,p+String.valueOf(i),index+1);
}
}
leetcode 440
![f6ebed243407725d86558d3406357b7c.png](https://img-blog.csdnimg.cn/img_convert/f6ebed243407725d86558d3406357b7c.png)
看到这个题我首先想到的是树,树的遍历,也想到了10叉树,但是我犯了一个错误,将root设为1 实际这里不能这样干,会失去你的树的可复制的性质,使得分类讨论变的比较麻烦。
看了lee215 大神的题解,感触颇深,最大的亮点在于观察上下界去计算每层的node数量,进而减少重复计算。我们可以建立一个字典树一样的结构。如图。
![95449d5fc388d0dd6b0b6096d055ba27.png](https://img-blog.csdnimg.cn/img_convert/95449d5fc388d0dd6b0b6096d055ba27.png)
我们最基本的思路就是遍历前缀树,每访问一个点就step++直到访问到需要的点。
我们可以先算出来这个数值在第一层哪个node集合里面
再去看对应第二层的哪个node集合
最后找到那个走了k步的node 就是我们的答案了。
下面说node集合数量怎么计算。
![ab45147ce26556a3cbff49ea02b4a7ed.png](https://img-blog.csdnimg.cn/img_convert/ab45147ce26556a3cbff49ea02b4a7ed.png)
通过图我们会发现每层的node他的上界实际是下一个node对应层的最小值。所以计算该层node数量只需要 (curr+1)10-curr*10就好,但是他还有一个上界就是最大值。
故每层的数量就是 min((curr+1)*10,max_value+1)-curr*10;
计算这个node 有多少节点就把符合条件的每一层加起来就好
代码:
public int cal(int max,int n1,int n2){
int step=0;
while(n1<=max){
step+=Math.min(n2,max+1)-n1;//当前层的node的数量
n1*=10;//指针指向下一层的起点
n2*=10;//指针指向下一层起点
}
return step;
}
整体代码
class Solution {
public int findKthNumber(int n, int k) {
int curr=1;
k-=1;
while(k>0){
long step=calstep(n,curr,curr+1);
if(k<step){//在这个范围内
curr*=10;//移动一层
k--;
}else{//移动相邻的下一个节点
k-=step;
curr=curr+1;
}
}
return curr;
}
public long calstep(int n,long n1,long n2){
int ans=0;
while(n1<=n){
ans+=Math.min(n+1,n2)-n1;
n1*=10;
n2*=10;
}
return ans;
}
}