剑指offer刷题- 数字序列中某一位的数字

Posted 王六六的IT日常

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer刷题- 数字序列中某一位的数字相关的知识,希望对你有一定的参考价值。

数字序列中某一位的数字

题目描述:数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

请写一个函数,求任意第n位对应的数字。

参考:K神题解

解题思路:

  1. 101112... 101112... 101112...中的每一位称为数位,记为 n n n
  2. 10 , 11 , 12 , . . . 10,11,12,... 10,11,12,...称为数字,记为 n u m num num
  3. 数字 10 10 10是一个两位数,称此数字的位数 2 2 2,记为 d i g i t digit digit
  4. d i g i t digit digit位数的起始数字(即: 1 , 10 , 100 , . . . 1,10,100,... 1,10,100,...),记为 s t a r t start start

    d i g i t digit digit 下的数位数量 c o u n t count count 的计算公式:
    c o u n t = 9 × s t a r t × d i g i t count = 9 \\times start \\times digit count=9×start×digit

分为三步:

  • 确定 n n n位数 d i g i t digit digit
  • 确定 n n n所在的数字 n u m num num
  • 确定 n n n n u m num num中的哪一位数,返回结果

1.确定 n n n为几位数

循环执行 n n n 减去 一位数、两位数、… 的数位数量 c o u n t count count,直至 n ≤ c o u n t n \\leq count ncount时跳出循环。
由于 n n n 已经减去了一位数、两位数、… 、( d i g i t − 1 digit - 1 digit1)位数的数位数量 c o u n t count count,因此此时的 n n n 是从起始数字 s t a r t start start开始计数的。
举例:

		int digit = 1; //一位数
        long start = 1;//一位数从1开始
        long count = 9; //一位数有9个数字
        while (n > count)  
            n -= count;
            digit++;
            start *= 10; 
            count = digit * start * 9;
        

2. 确定 n n n 所在的数字 n u m num num

所求数位 在从数字 s t a r t start start 开始的第 [ ( n − 1 ) / d i g i t ] [(n - 1) / digit] [(n1)/digit]个 数字 中( s t a r t start start 为第 0 个数字)。

这里取 n − 1 n-1 n1的原因是:当 n n n 对应 n u m num num 中的最后一位时,不会由于进位,错误的寻找到下一个数字。

long num = start + (n - 1) / digit;

3. 确定 n n n n u m num num 的哪一数位

所求数位 r e s res res 为数字 n u m num num 的第 ( n − 1 ) % d i g i t (n−1)\\%digit (n1)%digit 位( 数字的首个数位为第 0 位)
这里取n-1使得num中各位的位置坐标从左到右从0开始增加,即 0 , 1 , . . . . , ( d i g i t − 1 ) 0,1,....,(digit-1) 0,1,....,(digit1),更容易理解。

s = str(num) //将num转化为 string
//获得 num 的 第 (n - 1) % digit 个数位,并转化为 long
res = long(s[(n - 1) % digit]) 
res = Long.toString(num).charAt((n - 1) % digit) - '0';
class Solution 
    public int findNthDigit(int n) 
        int digit = 1;
        long start = 1;
        long count = 9;
        while (n > count)  // 1.
            n -= count;
            digit += 1;
            start *= 10;
            count = digit * start * 9;
        
        long num = start + (n - 1) / digit; // 2.
        return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.
    

class Solution 
    public int digitAtIndex(int n) 
        //1.确定是几位数
        //i表示是几位数,s表示该位数的数有多少个,如1位数9个,2位数的有90个,3位数900个...
        //base表示一个几位数的起点,如一位数起点是1,二位数起点是10...
        //当输入的n是int的最大值时,位数为9位,此时s *= 10执行九次的话会int越界的,故用long接收
        long i = 1,s = 9,base = 1;
        while(n > i * s)
            n -= i * s;
            i++;
            s *= 10;
            base *= 10;
        

        //2.确定是几位数的第几个数,求出这个数数值num
        // 已经知道了序列的第n位是一个几位数中的某一位,那到底是哪一个几位数呢
        // (n + i - 1) / i  为 n/i 的上取整公式
        long num = base + (n + i - 1) / i - 1;

        //3.确定属于那个数的第几位
        //求余数即可
        //r = 0 表示是最后一位(也就是 i,几位数就是几),r != 0,则r是几就是第几位
        long r = n % i == 0 ? i : n % i;

        //取出num的第r位,去掉后面的i - r位即可
        //如12345的第三位,后面还有两位45,我们将这两位去掉才好取出顺数的第三位
        for(int j = 1; j <= i - r;j++)
            num /= 10;
        

        return (int)num % 10;//取出现在的个位就是我们的结果
    

start的值应该一直小于n的值,n为int,start应该用int的范围就够了,只是count的值在第一步最后一次循环时可能会超限,应该用long。最后确定结果的时候也可以不用转为字符数组。

public int findNthDigit(int n) 
        if(n==0) return 0;
        //由于是n=0时对应开始的0,这里不需要进行减操作n--;,但是如果n=1对应开始的0则需要减操作
        //排除n=0后,后面n从1开始。
        int digit = 1;
        int start = 1;
        long count = 9; //count的值有可能会超出int的范围,所以变量类型取为long
        while(n>count)//不能带=号,此时n从1开始,n=count时,属于上一个循环的最后一个数字
            n=(int)(n-count);//这里(int)不能省略
            digit++;
            start = start*10;
            count = (long)start*9*digit;
            //这里的long不能省略,否则,会先按照int类型进行计算,然后赋值给long型的count,超过int大小限制时,会出现负数
        

        int num = start + (n-1)/digit;
        int index = (n-1)%digit;//index最大取digit-1,即此时num坐标从左到右为0,1,...,digit-1,共digit位
        while(index<(digit-1))
        //最后的结果是num中的第index个数字,index从左到右从0开始递增,考虑到踢出右侧末尾的数字比较简单,我们从右侧开始依次踢出数字
            num = num/10;
            digit--;
        
        return num%10;//此时num的右侧末尾数字即为结果
    

以上是关于剑指offer刷题- 数字序列中某一位的数字的主要内容,如果未能解决你的问题,请参考以下文章

《剑指offer》第四十四题:数字序列中某一位的数字

剑指offer:数字序列中某一位的数字

剑指Offer对答如流系列 - 数字序列中某一位的数字

剑指 Offer 44. 数字序列中某一位的数字(找规律)

剑指 Offer 44. 数字序列中某一位的数字

剑指 Offer 44. 数字序列中某一位的数字