题目描述
给定整数n和m,将1到n的这n个整数按字典序排列之后,求其中的第m个数字。
举例:对于n = 11,m = 4,按字典序排列依次为1, 10, 11, 2, 3, 4, 5, 6, 7, 8, 9,因此第4个数字为2。
输入:仅包含两个整数n和m | 样例输入: 11 4 |
---|---|
输出:仅包括一行,即所求排列中的第m个数字 | 样例输出:2 |
时间限制 C/C++语言:1000MS;其它语言:3000MS | 内存限制 C/C++语言:65536KB其它语言:589824KB |
数据范围
- 对于20%的数据, 1 <= m <= n <= 5 ;
- 对于80%的数据, 1 <= m <= n <= 10^7 ;
- 对于100%的数据, 1 <= m <= n <= 10^18.
思路一
n个int数据转为string类型,然后sort排序,得到m位置的string,再转为int型。
缺点:只通过了部分用例,数据量大时,会timeout。其二,数据会超过数组最大长度。
思路二
字典树的方法。十叉树,n+1个节点,根节点为0,不纳入计算,求深度遍历,第m个节点。
树的排列如下:
- 第一层是root 0;
- 第二层依次是:1,2,3,4,5,6,7,8,9;
- 第三层依次是:10,11,12,13,14,15,16,17,18,19,20,21……;
步骤:
1. 计算节点1分支中,小于n的节点个数p,得到p个节点。
2. 如果p>=m,说明第m个节点在1分支上,去掉1节点,则从第二层开始,重复操作,m=m-1。相当于重新回到最初,从10开始的第m个数,当前节点值为1。
3. 如果p< m,说明第m个节点在其他分支上,则从值为2的节点开始,重复操作,m=m-p。相当于回到最初,从2开始的第m个数。
4. 何时停止?m=0时,当前节点即为所求值。
代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
long n = scan.nextLong(), m = scan.nextLong();
long result = 1;
while(m!=0)
{
//当前节点为result,result分支上得到小于n的节点个数
long p=getCntOfChild(result,n);
if(m>p)
{
//第m个数不在result节点的分支上
m-=p; // 去掉result分支上小于n的节点
result++;// 当前节点设置为result右边第一个节点
}else {
m--;// 去掉result节点
if(m==0) //第m个数是result节点
break;
result*=10; //第m个数在result节点的分支上,当前节点设置为result节点分支最左节点
}
}
System.out.println(result);
}
// 找到小于n的,以p开头的树的个数
//例如:result=1,n=20,则 p=11,节点依次为:1,10,11,12,13,14,15,16,17,18,19
private static long getCntOfChild(long p,long n) {
long sum=1; //p节点
// p的下层有10个数
// n 需要小于当前 节点子节点最大值,也就是最右边节点
for(long level=10; n>=level*p;level*=10)
{
//n 大于最右节点
if(n>=level*p+level-1)
{
sum+=level; //当前 节点子节点个数为level
}else
{
// n 大于最左节点,n小于最右节点
sum+=(n-level*p+1);
}
}
return sum;
}
}
刷题感受
- 测试用例数据量大,考虑long数据类型,一般的sort等方法避免采用;
- 算法题,基本与数据结构相关,考虑数据特性,比如这里的树结构;
- 学会抽象,讲当前题目进行抽象成一般问题,极好解决,比如上述题目就是树节点遍历的问题。
- 祝好,以上,刷题不易,AC不易。