剑指 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 ( l o g 10 n ) O(log_{10}n) O(log10n) 也即 O ( l o g n ) O(logn) O(logn)
总的时间复杂度即三步操作的时间复杂度
int 的范围
2
∗
1
0
9
2∗10^9
2∗109,所以最多是 10 位数,因此第一步操作的时间最多是
l
o
g
10
n
=
10
log_{10}n = 10
log10n=10次,是
O
(
1
)
O(1)
O(1) 的时间复杂度,第二步除法向上取整,也是
O
(
1
)
O(1)
O(1) 的,第三步求是第几位数字是
O
(
l
o
g
10
n
)
O(log_{10}n)
O(log10n) 的,因为在二进制表示中取某一位可以每次右移 1 位(即除以 2),所以是
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n)级别的,同理在十进制中取某一位可以每次右移 1 位(即除以 10),所以是
O
(
l
o
g
10
n
)
O(log_{10}n)
O(log10n) 级别的
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;//取出现在的个位就是我们的结果
}
}

剑指Offer44题解

被折叠的 条评论
为什么被折叠?



