题目
算法思路
与【JZ-43】一样,重点在于寻找规律。
参考
将101112…中的每一位,即1,0,1,1,1,2,…称为数位,记为
n
n
n;
将10,11,12,…称为数字,记为
n
u
m
num
num;
将数字包含的数位个数称为位数,记为
d
i
g
i
t
digit
digit,例如数字13的位数为2;
将每
d
i
g
i
t
digit
digit 位数的起始数字记为
s
t
a
r
t
start
start,结束数字记为
e
n
d
end
end,例如
d
i
g
i
t
digit
digit 为2时,
s
t
a
r
t
start
start等于10,
e
n
d
end
end等于99。
数字范围 | 位数 | 数字个数 | 数位个数 |
---|---|---|---|
1~9 | 1 | 9 | 9 |
10~99 | 2 | 90 | 180 |
100~999 | 3 | 900 | 2700 |
… | … | … | … |
s t a r t start start~ e n d end end | d i g i t digit digit | 9* s t a r t start start | 9* s t a r t start start* d i g i t digit 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. 确定 digit:
各 d i g i t digit digit 下数位个数 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 依次减去一位数、两位数、…的 c o u n t count count,直到 n n n 小于等于当前 d i g i t digit digit 下的数位个数 c o u n t count count。
int digit = 1;
int start = 1;
int count = 9;
while(n > count){
n -= count;
start *= 10;
digit += 1;
count = 9 * start * digit;
}
则循环结束后,所求的数位一定在某个 d i g i t digit digit 位数中,且为从 s t a r t start start 开始的第 n n n 个数位。
2. 确定 num:
显然有num = start + (n - 1) / digit;
,例如
d
i
g
i
t
digit
digit 为 2 时,
s
t
a
r
t
start
start 等于 10,数位和数字的对应关系为:
1 | 0 | 1 | 1 | 1 | 2 | 1 | 3 | … | |
---|---|---|---|---|---|---|---|---|---|
n n n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | … |
( n − 1 ) / 2 (n - 1) / 2 (n−1)/2 | 0 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | … |
3. 确定对应 num 中的哪一数位
到这里我们已知所求的数位在
d
i
g
i
t
digit
digit 位的数字
n
u
m
num
num 中,先将数字
n
u
m
num
num 转换为字符串
s
s
s,显然有 res = s[(n - 1) % digit]
例如
d
i
g
i
t
digit
digit 为 2 时,
s
t
a
r
t
start
start 等于 10,数位和数字的对应关系为:
1 | 0 | 1 | 1 | 1 | 2 | 1 | 3 | … | |
---|---|---|---|---|---|---|---|---|---|
n n n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | … |
( n − 1 ) % 2 (n - 1) \% 2 (n−1)%2 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | … |
具体代码
class Solution {
public int findNthDigit(int n) {
if(n == 0)return 0;
int digit = 1;
long start = 1;
long count = 9;//有可能会超出int的范围
//1.确定n所在的数字的位数
while(n > count){
n -= count;
start *= 10;
digit += 1;
count = 9 * start * digit;
}
//2.确定n所在的数字
long num = start + (n - 1) / digit;
//3.确定n对应数字num中的哪一数位
String s = Long.toString(num);//先将数字num转换为字符串
return s.charAt((n - 1) % digit) - '0';//利用ASCII码相减得到该位置上字符的十进制值
}
}
注意:
- 要考虑变量是否会超出 int 的范围
- 关于最后字符串相减,
s.charAt(i)-'0'
,两个字符相减实际上是ASCII码相减,这样得到的结果才会是 s 中第 i 个字符的十进制值
最后确定结果的时候也可以不用把数字转为字符串,即:
int index = (n - 1) % digit;
while(index < (digit - 1)){
num /= 10;
digit--;
}
return num % 10;
复杂度分析
- 时间复杂度: O ( l o g n ) O(logn) O(logn)。 n n n 所在的数字的位数 d i g i t digit digit最大为 O ( l o g n ) O(logn) O(logn),第一步的时间开销为 O ( l o g n ) O(logn) O(logn),第二步的时间开销为 O ( 1 ) O(1) O(1),第三步将数字转换为字符串的时间开销为 O ( l o g n ) O(logn) O(logn)
- 空间复杂度: O ( l o g n ) O(logn) O(logn)。将数字转换为字符串额外占用了 O ( l o g n ) O(logn) O(logn)的空间。