Medium | LeetCode 400 | 剑指 Offer 44. 数字序列中某一位的数字
Medium | LeetCode 400 | 剑指 Offer 44. 数字序列中某一位的数字
数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
请写一个函数,求任意第n位对应的数字。
示例 1:
输入:n = 3
输出:3
示例 2:
输入:n = 11
输出:0
限制:
0 <= n < 2^31
首先观察数字的规律如下:
确定某一位的数字的基本步骤如下:
根据以上分析,可将求解分为三步:
-
确定 n 所在 数字 的 位数 ,记为 digit ;
将此数字依次减去9, 90, 900, 9000, 直到减不够为止, 减不够时剩下来的数的位数就是当前所要确定的数字的位数。比如n = 150, 减去99位, 还剩51位, 减900减不够。那么n所在数字的数就是一个三位数。
-
确定 n 所在的 数字 ,记为 num ;
第一步得到n所在数组是一个三位数, 并且剩下51, 所以当前数是 51 % 3 = 27个数, 则当前数字是 100 + (51 % 3) - 1 = 126;
还有一种计算方法:100 + (51 - 1) % 3 = 126, 这两种计算方法结果都是126, 那种方法对?还是都对?
答案是:num = start + (n - 1) / digit
这种方法是对的, 另一种是错的。为什么?因为要用除法对每三个数分一组, 比如1,2,3分一组, 4,5,6, 分一组, 最后一个数的除的数与前面数字的除得数是不一样的。如果先做减1的处理, 0,1,2分一组, 3,4,5分一组, 这样就能够通过除法的方式进行分组了。 -
确定 n 是 num 中的哪一数位,并返回结果
第二步的结果已经得到了是第多少个数字, 我们求得的结果是126。我们知道按三个三个分组, 51处在分组的最后面一位, 也就是126的最低位。3个3个分组, 然后取分组里某一位, 我们立马想到的是模运算。51%3 = 0, 显得不对。所以在进行模运算计算位数时, 也应该遵循减1的方法, 让下标从0开始。也就是
index = (n - 1) % digit;
以上三个问题解决了, 最终的代码就顺理成章的出来了。
public int findNthDigit(int n) {
int digit = 1;
long start = 1;
long count = 9;
// 第一步: 确定n所在数字位数
while(n > count) { // 最终减不够的时候跳出循环
n -= count;
// 位数和这个位数的最小值start同步增加
digit++;
start *= 10;
// count是这个位数的所有数字长度总和, 比如2位数有90个, 总长为180
count = digit * start * 9;
}
// 第二步:确定所在的数字
long num = start + (n - 1) / digit ;
// 第三步:确定n在数字的第几位
int index = (n - 1) % digit;
return String.valueOf(num).charAt(index) - '0';
}