一、题目
在无限的整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …中找到第 n 个数字。
注意:
n 是正数且在32位整数范围内 ( n < 231)。
示例 1:
输入:
3
输出:
3
示例 2:
输入:
11
输出:
0
说明:
第11个数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 里是0,它是10的一部分。
二、解决
1、归纳法
思路:
为了下面推导方便,厘清一下几个概念:
- 数位: 将 12345… 每一位称为数位,记为 n;
- 数字: 将 11,12,13称为数字,记作 num;
- 位数:10 是两位数, 999 是三位数,记为 digit;
- 起始数字:每 digit 位数的起始数字(即:1,10,100,…),记为 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*start*digit count=9∗start∗digit。
根据上面分析,可将求解分解为三步(宏观到微观,逐层定位):
- 位数digit:确定 n n n 所在数字的位数,记为 d i g i t digit digit;
- 数字num:确定 n n n 所在数字,记为 n u m num num;
- 数位 pos :稳定 n n n 是 n u m num num 中的哪一数位,并返回结果。
下面逐步分析:
1.位数digit: 确定
n
n
n 所在数字的位数,记为digit
循环减去一位数、两位数、…、digit位数、…的数位数量count,直到count之和超出 n 。
2. 数字num:确定所求数位所在的数字
经过循环相减后,所求数位 在从数字start开始的第 [(n-1)/digit] 个数字中(start为第 0 个数字)。
num = start + (n-1)/ digit
3. 数位pos:确定所求数位在 num 的哪一数位
数位在所求数字的第 (n-1) % digit
位。(首个数位为第 0 位,也为了与最后数组索引保存一致。)
[0, 9]这个区间,长度为9,每个数字只有1位,共有
9
∗
1
9*1
9∗1个数字
[10, 99]这个区间,长度为90,每个数字只有2位,共有
90
∗
2
90*2
90∗2个数字
[100, 999]这个区间,长度为900,每个数字只有3位,共有
900
∗
3
900*3
900∗3个数字
过程:区间–>具体数字–>数字特定位
代码:
class Solution {
public int findNthDigit(int n) {
int digit = 1;
long start = 1;
long count = 9;
// 1. 区间
while (n > count) {
n -= count;
digit += 1;
start *= 10;
count = digit * start * 9;
}
// 2. 数字
long num = start + (n - 1) / digit;
// 3. 数位
return Long.toString(num).charAt((n - 1) % digit) - '0';
}
}
时间复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn),求数位 n 对应数字 num 的位数 digit 最多循环
O
(
l
o
g
n
)
O(logn)
O(logn)次。
空间复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn),将 num 转化为字符串使用
O
(
l
o
g
n
)
O(logn)
O(logn) 时间。
2、思考
43 求1的数量,可以遍历数位;
44 求第n个数字,可以通过遍历位数,确定区间,再确定到数,再定位到数字。
这都是复杂问题拆解为简单问题的,然后分步骤解决。做菜也是一样,拆解为多个简单问题并解决,然后组合衔接起来就是一个相对复杂的问题。
延申:如何找到问题?如何找到多种解法&优劣?多种测试案例保证健壮性?
三、参考
1、面试题44. 数字序列中某一位的数字(迭代 + 求整 / 求余,清晰图解)
2、Just explain, no code