剑指 Offer(第2版)面试题 44:数字序列中某一位的数字

文章讲述了如何解决剑指Offer面试题44,找到数字序列中第N位的数字,提供了两种解法:一是枚举法,时间复杂度O(n),二是利用数学方法跳过部分位数,优化到O(logn),并分析了两种方法的时间和空间复杂度。
摘要由CSDN通过智能技术生成

剑指 Offer(第2版)面试题 44:数字序列中某一位的数字

剑指 Offer(第2版)面试题 44:数字序列中某一位的数字

题目来源:

  1. AcWing 57. 数字序列中某一位的数字
  2. LeetCode 400. 第 N 位数字

解法1:枚举

从 0 开始枚举每个数字,求出该数字的位数,累加起来。如果位数之和小于等于 n,继续枚举下一个数字。

当累加数位大于 n 时,那么第 n 位数字一定在这个数字中,我们再从该数字中找出对应的那一位。

代码:

class Solution
{
public:
	int digitAtIndex(int n)
	{
		if (n < 10)
			return n;
		int cur = 0;
		int length = 0;
		while (length <= n)
		{
			length += digitLength(cur);
			cur++;
		}
		for (int i = 0; i < length - n; i++)
			cur /= 10;
		return cur % 10;
	}
	// 辅函数
	int digitLength(int x)
	{
		int len = 0;
		while (x)
		{
			len++;
			x /= 10;
		}
		return len;
	}
};

复杂度分析:

时间复杂度:O(n)。

空间复杂度:O(1)。

很不幸,这样做超时了。

解法2:数学

一个一个枚举的找太慢了,我们需要跳着找。

我们以找序列的第 1001 位为例。

序列的前 10 位是 0 ~ 9 这 10 个只有一位的数字。显然第 1001 位在这 10 个数字之后,跳过这些一位数,我们从后面的序列找第 991(1001 - 10 = 991)位的数字。

接下来是 90 个二位数 10 ~ 99,总共 180 位。由于 991 > 180,所以再跳过这些二位数,继续找后面的第 811(991 - 180 = 811)位的数字。

接下来是 900 个三位数 100 ~ 999,总共 2700 位。由于 811 < 2700,所以第 811 位是某个三位数的一位。由于 811 = 3 * 270 + 1,这意味着第 811 位是从 100 开始的第 270 个数字,即 370 的中间一位,也就是 7。

代码:

class Solution
{
public:
	int digitAtIndex(int index)
	{
		if (index < 0)
			return -1;
		int digits = 1;
		while (true)
		{
			int numbers = countOfIntegers(digits);
			if (index < (long long)digits * numbers)
				return digitAtIndex(index, digits);
			index -= digits * numbers;
			digits++;
		}
		return -1;
	}
	int digitAtIndex(int index, int digits)
	{
		int number = beginNumber(digits) + index / digits;
		int indexFromRight = digits - index % digits;
		for (int i = 1; i < indexFromRight; i++)
			number /= 10;
		return number % 10;
	}
	// 辅函数 - 返回 digits 位数的总个数
	int countOfIntegers(int digits)
	{
		if (digits == 1)
			return 10;
		int count = pow(10, digits - 1);
		return 9 * count;
	}
	// 辅函数 - 返回 digits 位数的第一个数字
	int beginNumber(int digits)
	{
		if (digits == 1)
			return 0;
		return pow(10, digits - 1);
	}
};

PS:digits * numbers 可能会很大,比如 9 * 108,这样会爆 int,改为 long long。

复杂度分析:

时间复杂度:O(logn),所求数位 n 对应数字 num 的位数 digits 最大为 O(log⁡n)。while 循环内最多 O(log⁡n) 次。

空间复杂度:O(1)。

上面的是书上的版本,辅函数很多,下面是简化版本:

class Solution
{
public:
	int digitAtIndex(int n)
	{
		int digit = 1;	// 位数
		long start = 1; // digit 位数的起始数字
		long count = 9; // digit 位数的位数总和
		while (count < n)
		{
			n -= count;
			digit++;				   // 1,  2,  3, ...
			start *= 10;			   // 1, 10, 100, ...
			count = 9 * digit * start; // 9, 180, 2700, ...
		}
		// 所求数位在 digit 位数中,是从 start 开始的第 n 个数位
		long num = start + (n - 1) / digit;
		// 所求数位在数字 num 中的第 (n - 1) % digit 位
		return to_string(num)[(n - 1) % digit] - '0';
	}
};

对应题解:400. 第 N 位数字(清晰图解)

复杂度分析:

时间复杂度:O(logn),所求数位 n 对应数字 num 的位数 digit 最大为 O(log⁡n);第一步最多循环 O(log⁡n) 次;第三步中将 num 转化为字符串使用 O(log⁡n) 时间;因此总体为 O(log⁡n)。

空间复杂度:O(log⁡n),将数字 num 转化为字符串 str(num) ,占用 O(log⁡n) 的额外空间。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

UestcXiye

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

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

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

打赏作者

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

抵扣说明:

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

余额充值