1. 题目
2. 解法
2.1 解法一(二分查找+数学思维法)
- 时间复杂度,位数的上限为,二分查找找到对应位数的执行次数是(这里将看作一个整体,表示要从一组数中利用二分查找找到的这个数,所以找到这个数的执行次数为, 子函数的输入为, 循环执行的时间复杂度为, 所以最终的findNthDigit函数内while循环的时间复杂度为
- 空间复杂度
- 数学思维理解:第N位数字
- 代码
class Solution {
public:
int findNthDigit(int n) {
int low = 1, high = 9; // 确定n对应的数的位数的上下限(n最小在一位数中,n最大在9位数中)
while (low < high) { // 使用二分法确定n具体在几位数当中
int mid = (high - low) / 2 + low;
if (totalDigits(mid) < n) { // mid位数字及之前位数字之和小于n
low = mid + 1; // 更新下限
} else {
high = mid; // 更新上限
}
}
int d = low; // 最终low值存储的是n在low位数当中
int prevDigits = totalDigits(d - 1); // 算出之前d位数据之前的数位对应的数据的累加和
int index = n - prevDigits - 1; // 从d位数开始定位,索引从0开始
int start = (int) pow(10, d - 1); // 开始进行数据定位和提取
int num = start + index / d; // 确定第n位数具体在哪个数当中
int digitIndex = index % d; // 确定第n位数具体是num中的第几位(第一位数,索引为0)
int digit = (num / (int) (pow(10, d - digitIndex - 1))) % 10; // 对num进行除法截断和取余,提取出第n位数
return digit;
}
int totalDigits(int length) { // 这里的数学规律很重要
int digits = 0;
int curLength = 1, curCount = 9;
while (curLength <= length) {
digits += curLength * curCount;
curLength++; // 增加数位
curCount *= 10; // 初值为9,累计乘10
}
return digits; // 返回length位及之前位所有单个数据(一个数字一个数据)的累加个数
}
};
2.2 解法二(数学思维法)
- 时间复杂度,指的是确定位数范围的循环的时间复杂度
- 空间复杂度
- 数学思维理解:第N位数字
- 代码
class Solution{
public:
int findNthDigit(int n) {
int d = 1, count = 9; // 从1开始,表示d位数有9个
while ( n > (long) d * count) { // 这里为什么用long
n -= d * count; // n不断减少是为了确定,n到底是在几位数当中
d++; //位数增加
count *= 10; //d位数的个数增加十倍
}
int index = n-1; // (这里的n已经是将其他位排除的剩余数了)index即为目标数字在所有d位数中的下标,index的最小可能取值是0
int start = (int) pow(10, d-1); // 从d位数的第一个数字开始
int num = start + index / d; // 该运算确定第n个数在从start开始第几个数内, 这个数的数值就是num
int digitIndex = index % d; // 该元素确定第n个数是在num中从左向右数的第几位(索引从0开始)
int digit = (num / (int) (pow(10, d - digitIndex - 1)) ) % 10;
// 公式理解:幂是为了截断——确定了第n个数是在num中从左向右数的第digitIndex位,那么对应的,第n个数就是在从右向左数的第d-digitIndex-1位
// 为了提取出最终的值,用除法做截断,再进行取余运算,方能提取出第n位数
return digit;
}
};