3步拆解数字序列难题:从LeetCode-Book掌握数位查找核心算法
你是否曾在面试中遇到这样的问题:给定一个无限序列1,2,3,...,n,如何快速定位第n位数字?这个看似简单的问题,实则隐藏着对数学规律和算法效率的双重考验。本文将基于LeetCode-Book项目中的经典题型,通过三步拆解法,带你掌握数字序列数位查找的通用解题框架,并提供Python/Java/C++多语言实现方案。
问题定义与数学规律
在开始解题前,我们需要明确几个关键概念:
- 数位(Digit):数字序列中单个数字字符,如序列"101112..."中的'0'、'1'都是独立数位
- 数字(Number):由多个数位组成的整数,如10、11、12等
- 位数(Digit Length):一个数字包含的数位数量,如10是两位数
通过观察可以发现数字序列的分布规律:
- 1位数(1-9):共9个数字,9×1=9个数位
- 2位数(10-99):共90个数字,90×2=180个数位
- 3位数(100-999):共900个数字,900×3=2700个数位
通用公式为:count = 9 × start × digit,其中start是该位数的起始数字(1,10,100...)。这一规律是解题的核心基础,完整推导过程可参考[剑指 Offer 44. 数字序列中某一位的数字.md](https://link.gitcode.com/i/5028a4064437751c88ba9fedcafb5ea2/blob/c12faa39f70b5ddbcd2830db88ac74fea599442b/sword_for_offer/docs/剑指 Offer 44. 数字序列中某一位的数字.md?utm_source=gitcode_repo_files)。
三步解题框架
1. 确定目标数位所在数字的位数
通过循环减去不同位数的数位总量,定位目标数位所在数字的位数:
digit, start, count = 1, 1, 9
while n > count:
n -= count # 减去当前位数的数位总量
start *= 10 # 移动到下一位数的起始数字
digit += 1 # 位数加1
count = 9 * start * digit # 计算当前位数的数位总量
这段代码实现了动态调整位数的过程,例如当n=150时:
- 初始n=150 > 9(1位数总量),减去9后n=141,进入两位数处理
- 141 > 180(2位数总量)不成立,确定目标在两位数范围内
2. 定位具体数字
确定位数后,计算目标数位所在的具体数字:
long num = start + (n - 1) / digit;
公式推导:
- start是当前位数的起始数字(如两位数的start=10)
- (n-1)/digit计算从start开始的偏移量(整数除法)
- 以n=150为例:n=150-9=141,(141-1)/2=70,num=10+70=80
3. 确定数位位置并返回结果
最后一步是找到数字中的具体数位:
string s = to_string(num);
int res = s[(n - 1) % digit] - '0';
以n=150为例:
- (141-1)%2=0 → 取数字80的第0位(从0开始计数)
- '80'的第0位是'8',转化为整数8
完整代码实现
Python实现
class Solution:
def findNthDigit(self, n: int) -> int:
digit, start, count = 1, 1, 9
while n > count: # 1.确定位数
n -= count
start *= 10
digit += 1
count = 9 * start * digit
num = start + (n - 1) // digit # 2.确定数字
return int(str(num)[(n - 1) % digit]) # 3.确定数位
Java实现
class Solution {
public int findNthDigit(int n) {
int digit = 1;
long start = 1;
long count = 9;
while (n > count) { // 1.确定位数
n -= count;
start *= 10;
digit += 1;
count = digit * start * 9;
}
long num = start + (n - 1) / digit; // 2.确定数字
return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.确定数位
}
}
C++实现
class Solution {
public:
int findNthDigit(int n) {
int digit = 1;
long start = 1;
long count = 9;
while (n > count) { // 1.确定位数
n -= count;
start *= 10;
digit += 1;
count = digit * start * 9;
}
long num = start + (n - 1) / digit; // 2.确定数字
return to_string(num)[(n - 1) % digit] - '0'; // 3.确定数位
}
};
复杂度分析与优化建议
时间复杂度
- O(log n):循环次数取决于数字n的位数,最多执行log₁₀n次
- 数字转字符串操作的时间复杂度为O(digit),而digit = O(log n)
空间复杂度
- O(log n):主要来自数字转字符串时存储的字符数
优化方向
- 避免字符串转换:通过数学运算直接提取数位
def get_digit(num, index): return num // (10 ** (digit - index - 1)) % 10 - 边界处理:针对n=0等特殊情况增加防御性判断
实际应用与扩展
数位查找算法不仅适用于面试题,还可应用于:
- 日志系统中的行号定位
- 大数据处理中的分片索引
- 密码学中的随机数生成
该算法的核心思想可推广到各类序列定位问题,更多类似题型可参考:
- [LCR 163. 找到第 k 位数字.md](https://link.gitcode.com/i/5028a4064437751c88ba9fedcafb5ea2/blob/c12faa39f70b5ddbcd2830db88ac74fea599442b/leetbook_ioa/docs/LCR 163. 找到第 k 位数字.md?utm_source=gitcode_repo_files)
- [剑指 Offer 刷题计划.md](https://link.gitcode.com/i/5028a4064437751c88ba9fedcafb5ea2/blob/c12faa39f70b5ddbcd2830db88ac74fea599442b/sword_for_offer/剑指 Offer 刷题计划.md?utm_source=gitcode_repo_files)
掌握这种数学建模与分步拆解的思维方式,能帮助你解决更多复杂的算法问题。建议结合项目中的[题目分类.md](https://link.gitcode.com/i/5028a4064437751c88ba9fedcafb5ea2/blob/c12faa39f70b5ddbcd2830db88ac74fea599442b/leetbook_ioa/docs/?utm_source=gitcode_repo_files# 0.2 题目分类.md)进行系统性练习,逐步提升算法设计能力。
总结
本文通过三步法框架,基于LeetCode-Book项目中的经典题型,详细讲解了数字序列数位查找问题的解题思路。关键要点包括:
- 利用数学规律动态确定数字位数
- 通过整数运算定位目标数字
- 高效提取数字中的特定数位
这种解题模式体现了算法设计中的"分而治之"思想,将复杂问题拆解为可管理的小步骤。建议读者尝试修改参数(如n=190、n=1000等)进行测试,深入理解各步骤的计算过程。完整代码与更多解题思路可查阅项目中的sword_for_offer/codes/目录。
关注项目README.md获取最新更新,下期我们将解析"数字序列中的中位数查找"问题,带你进一步拓展算法应用边界。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



