【中等】力扣算法题解析LeetCode400:第 N 位数字

关注文末推广名片,即可免费获得本题测试源码

题目来源:LeetCode 400. 第 N 位数字

问题抽象: 在无限整数序列 1,2,3,4,5,...,10,11,... 连接形成的字符串中,找到 n 位十进制数字(从 1 开始计数),满足以下核心需求:

  1. 序列特性

    • 序列由所有正整数按升序连接而成(如 "123456789101112...");
    • n 位指从左向右数第 n 个字符(如 n=3 时字符为 '3'n=10 时字符为 '1'(来自 "10"))。
  2. 分组定位

    • 按数字位数分组
      • 1 位数(1-9):共 9 个数字,占 9 位;
      • 2 位数(10-99):共 90 个数字,占 180 位;
      • 3 位数(100-999):共 900 个数字,占 2700 位;
      • 一般化:k 位数占位 9×10^{k-1}×k
    • 定位步骤
      1. 确定 n 所在数字的位数 k(通过循环减去低位组总位数);
      2. 计算该组内的目标数字:num = 10^{k-1} + ⌊(n-1)/k⌋
      3. 提取数字的第 r 位:r = (n-1) mod k(从左到右索引)。
  3. 边界处理

    • 最小值n=1 返回 1(序列首位);
    • 大数处理n 最大为 2^31-1(需高效定位,避免遍历);
    • 位数溢出k 最大为 10(因 10^{10} 已超过 2^31);
    • 跨组处理:如 n=10 位于 2 位数组(数字 10 的首位 '1')。
  4. 计算约束

    • 时间复杂度 O(log n):位数 k 的循环次数为 O(log₁₀ n)
    • 空间复杂度 O(1):仅存储位数、偏移量等常数变量;
    • 输入范围:1 ≤ n ≤ 2^31-1
  5. 特殊案例

    • n=3'3'(来自数字 3);
    • n=10'1'(来自 "10" 的首位);
    • n=11'0'(来自 "10" 的末位);
    • n=1000'3'(来自数字 370 的某一位)。

输入:整数 n(正整数)
输出:第 n 位数字(整数 0-9)。


解题思路

题目要求在无限整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, … 中找到第 n 位数字。解题的核心是分三步定位:

  1. 确定数字的位数(digit)

    • 不同位数的数字占据不同的位数范围:
      • 1 位数:1~9,共 9 个数字,总位数 9
      • 2 位数:10~99,共 90 个数字,总位数 180
      • 3 位数:100~999,共 900 个数字,总位数 2700
      • 以此类推
    • 循环减去各范围的总位数,直到 n 落在当前位数范围内。
  2. 确定具体的数字(num)

    • 根据剩余位数 n 和当前位数 digit,计算目标数字:
      num = start + (n - 1) / digit
    • start 是当前位数范围的起始数字(如 1 位数的 1,2 位数的 10)
  3. 确定数字中的具体位

    • 将数字转为字符串,通过 (n - 1) % digit 定位字符位置
    • 将字符转换为数字后返回

代码实现(Java版)🔥点击下载源码

class Solution {
    public int findNthDigit(int n) {
        int digit = 1;      // 当前数字位数(1,2,3...)
        long start = 1;     // 当前位数范围的起始数字(1,10,100...)
        long count = 9;     // 当前位数范围内的总位数(9,180,2700...)
        
        // 1. 确定n所在的数字位数
        while (n > count) {
            n -= count;     // 减去已跳过的总位数
            digit++;        // 位数增加
            start *= 10;    // 起始数字扩大10倍
            count = 9 * start * digit; // 计算新位数范围的总位数
        }
        
        // 2. 确定具体的数字
        long num = start + (n - 1) / digit; 
        
        // 3. 确定数字中的具体位
        return Long.toString(num).charAt((n - 1) % digit) - '0';
    }
}

代码说明

  1. 变量作用

    • digit:当前处理的数字位数(1位、2位等)
    • start:当前位数范围的起始数字(1位从1开始,2位从10开始)
    • count:当前位数范围的总位数(9、180等)
  2. 核心循环

    • 不断减去低位数范围的总位数,直到 n 落在当前位数范围内
    • 每次循环更新位数、起始数字和总位数:
      • 位数 digit 增加 1
      • 起始数字 start 扩大 10 倍
      • 总位数 count = 9 × start × digit
  3. 定位数字

    • (n - 1) / digit:计算当前位数范围内的第几个数字(从0开始)
    • start + (n - 1) / digit:得到目标数字
  4. 定位具体位

    • (n - 1) % digit:数字中的字符索引(0表示最高位)
    • 将目标数字转为字符串后取对应字符,转换为数字返回

复杂度分析:时间复杂度:O(log₁₀n),循环次数与数字位数相关。空间复杂度:O(1),仅使用常数级额外空间。


提交详情(执行用时、内存消耗)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达文汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值