440. 字典序的第K小数字 + 字典树 + 前缀 + 字典序

440. 字典序的第K小数字

LeetCode_440

题目描述

方法一:暴力法(必超时)

package com.walegarrett.interview;

/**
 * @Author WaleGarrett
 * @Date 2021/2/25 19:49
 */

/**
 * 题目描述:给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字。
 * 注意:1 ≤ k ≤ n ≤ 109。
 */

import java.util.Arrays;

/**
 * 解法一:使用暴力法
 */
public class LeetCode_440 {
    public int findKthNumber(int n, int k) {
        String[] result = new String[n];
        for(int i=1;i<=n;i++){
            String now = String.valueOf(i);
            result[i-1] = now;
        }
        Arrays.sort(result);
        return Integer.parseInt(result[k-1]);
    }
}

方法二:思维

/**
 * 方法二:使用字典树
 */
class LeetCode_440_2 {
    public int findKthNumber(int n, int k) {
        long prefix = 1;
        long cntK = 1;
        /**
         * 以下的实现基于一个事实:所有前缀相同的元素肯定都在一起,或者说在一个连续的区间。
         * 解题的关键就是先找到对应的区间,然后找到区间的某一个值。
         */
        while(cntK<k){
            //获取某个前缀的所有序列字典序
            long cnt = getCount(prefix, n);
            if(cntK+cnt>k){
                /**
                 * 说明以数字i开头的数字串太多了,并且第k个数字一定是以数字prefix开头。
                 * 此时数字prefix更新为10*i,缩小搜索范围。
                 * 位置p向前移动一位,因为新数字i字典序向后移动一位了。
                 */
                prefix*=10;
                cntK++;
            }else if(cntK+cnt<=k){
                /**
                 * 说明将以数字prefix开头的数字串都算进去,也不够。
                 * 说明数字prefix要增加到prefix+1。
                 *  同时,位置p要跨过count个数字。
                 */
                prefix++;
                cntK += cnt;
            }
        }
        return (int)prefix;
    }

    //获取某个前缀的所有序列的字典序
    long getCount(long prefix, long n){
        long cnt = 0;
        for(long a=prefix,b=prefix+1; a<=n; a*=10,b*=10){
            cnt+=Math.min(b, n)-a;
        }
        return cnt;
    }
}
posted @ 2021-02-26 21:41  Garrett_Wale  阅读(130)  评论(0编辑  收藏  举报