0400.第N位数字

1. 题目

2. 解法

2.1 解法一(二分查找+数学思维法)

  • 时间复杂度O(\log_{10}n \times \log\log_{10}n),位数的上限为O(\log_{10}n),二分查找找到对应位数的执行次数是O(\log\log_{10}n)(这里将\log_{10}n看作一个整体,表示要从一组数中利用二分查找找到\log_{10}n的这个数,所以找到这个数的执行次数为O(\log\log_{10}n), 子函数的输入为\log_{10} n, 循环执行的时间复杂度为O(\log_{10}n), 所以最终的findNthDigit函数内while循环的时间复杂度为O(\log_{10}n \times \log\log_{10}n)
  • 空间复杂度O(1)
  • 数学思维理解:第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 解法二(数学思维法)

  • 时间复杂度O(\log_{10}n),指的是确定位数范围的循环的时间复杂度
  • 空间复杂度O(1)
  • 数学思维理解:第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;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值