剑指 Offer 44. 数字序列中某一位的数字
数字以0123456789101112131415…
的格式序列化到一个字符序列中。在这个序列中,第5
位(从下标0
开始计数)是5
,第13
位是1
,第19
位是4
,等等。
请写一个函数,求任意第n
位对应的数字。
示例 1:
输入: n = 3
输出: 3
示例 2:
输入: n = 11
输出: 0
限制:
0 <= n < 2^31
注意:
本题与主站 400 题相同:https://leetcode-cn.com/problems/nth-digit/
解题思路
首先我们知道的是,1
位数有 10
个,2
位数有 90
个,3
位数有 9 * 100
个,4
位数有 9 * 1000
个,以此类推,如下表
|数字范围 | 数量 |位数 | 占多少位 |
|–|–|–|–|
| 1 ~ 9 | 9 | 1 |9 |
| 10 ~ 99 |90 |2 | 180 |
| 100 ~ 999 |900 | 3 | 2700|
| …| … |… |… |
-
假如我们需要求的是序列的第
1001
位是什么,我们可以发现1001 > 9
,所以第1001
位肯定在1~9
这九位数字之后,接下来我们又发现(1001 - 9) > 180
,所以第1001
位也不可能是一个两位数中的某位,而(1001 - 9 - 180)< 2700
,因此可以断定序列的第1001
位是一个三位数中的某一位。 -
现在已经知道了序列的第
1001
位是一个三位数中的某一位,那到底是哪一个三位数呢,很简单,计算方法为(1001 - 9 - 180)/ 3
向上取整,即100 + ((1001 - 9 - 180 + 3 - 1)/ 3 ) - 1= 370
。看下方注1
中的上取整公式即可。 -
好的,现在也知道了它是属于
370
的某位的,那到底是哪一位,求余就好了(1001 - 9 - 180)% 3 = 2
;所以答案是370
中的第二位,即7
。
注1:
n / i
上取整的公式为(n + i - 1)/ i
原因:
- n
能整除i
时,则i - 1/i = 0
;
- n
不能整除i
时,由于n
是整数,则最少也会余1
,所以(i - 1 + 大于1小于i的数) / i = 1
,实现了上取整。
注2:
题目中第几位是从0
开始计数的,即第0
位,第1
位,第2
位...
,而我们上面例子刚好也是从0
开始计数,即0
就是第0
位,1
就是第1
位,也就是说,题目中表达的第1001
位指的就是我们表格中从1
开始计数的第1001
位。
总结上述过程为:
- 确定序列的第n
位应该是一个几位数中的某一位,比如是三位数
- 确定是几位数的第几个数,然后确定具体数值,比如是三位数的第271
个数,则数值是100 + 271 - 1 = 370
。
- 确定属于那个数的第几位,比如是370
的第二位,即7
时间复杂度: $O(log_{10}n)$ 也即$O(logn)$
总的时间复杂度即三步操作的时间复杂度
int
的范围 $2∗10^9$,所以最多是 10
位数,因此第一步操作的时间最多是$log_{10}n = 10$次,是 $O(1)$ 的时间复杂度,第二步除法向上取整,也是 $O(1)$ 的,第三步求是第几位数字是 $O(log_{10}n)$ 的,因为在二进制表示中取某一位可以每次右移1
位(即除以 2
),所以是 $O(log_2n)$级别的,同理在十进制中取某一位可以每次右移1
位(即除以10
),所以是$O(log_{10}n)$ 级别的
Java代码
class Solution {
public int findNthDigit(int n) {
//1.第n位是一个几位数中的某一位
//i表示是几位数,s表示该位数的数有多少个,如1位数9个,2位数的有90个...
//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.看它是几位数的第几个数,然后就可以知道它的数值了(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;//取出现在的个位就是我们的结果
}
}
写的很详细,蟹蟹