面试题——字典序(今日头条2017秋招真题)

题目描述

给定整数n和m,将1到n的这n个整数按字典序排列之后,求其中的第m个数字。
举例:对于n = 11,m = 4,按字典序排列依次为1, 10, 11, 2, 3, 4, 5, 6, 7, 8, 9,因此第4个数字为2。
  • 1
  • 2
  • 3
输入:仅包含两个整数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;  
    }  
}

刷题感受

  1. 测试用例数据量大,考虑long数据类型,一般的sort等方法避免采用;
  2. 算法题,基本与数据结构相关,考虑数据特性,比如这里的树结构;
  3. 学会抽象,讲当前题目进行抽象成一般问题,极好解决,比如上述题目就是树节点遍历的问题。

猜你喜欢

转载自blog.csdn.net/weixin_30363263/article/details/82024432