剑指 Offer 43. 1~n 整数中 1 出现的次数
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情。
思路
(数位统计)
我们按位来进行计算,分为中间位,前位,和后位,分情况进行计算,循环中间位的值。
以abcdef为例,中间位为c,前位取00 ~ ab - 1,后位取000 ~ 999
1、c = 1 则个数 += ab * 1000 (def位数)。
2、前位取ab:
- 如果
c = 0,则不加; - 如果
c = 1, 则个数+= def + 1; - 如果
c > 1, 则个数+= 1000(def位数)。
图示过程:
c++代码
class Solution {
public:
int countDigitOne(int n) {
if(!n) return 0;
vector<int> nums;
while(n) nums.push_back(n % 10), n /= 10;
long long res = 0;
for(int i = nums.size() - 1; i >= 0; i--){
int left = 0, right = 0, t = 1;
for(int j = nums.size() - 1; j > i; j--) left = left * 10 + nums[j];
for(int j = i - 1; j >= 0; j--) right = right * 10 + nums[j], t *= 10;
res += left * t;
if(nums[i] == 1) res += right + 1;
else if(nums[i] > 1) res += t;
}
return res;
}
};
剑指 Offer 44. 数字序列中某一位的数字
思路
(数位统计) O(logn)
首先我们知道,1 位数有 10 个,2 位数有 90 个,3 位数有 9 * 100 个,4 位数有 9 * 1000 个,以此类推,如下表:
| 数字范围 | 数量 | 位数 | 占多少位 |
|---|---|---|---|
| 1~9 | 9 | 1 | 9 |
| 10~99 | 90 | 2 | 180 |
| 100~999 | 900 | 3 | 2700 |
1、确定序列的第n位应该是一个几位数中的某一位
假如我们需要求的是序列的第1001位是什么,我们可以发现1001 > 9,所以第1001位肯定在1~9这九位数字之后,接下来我们又发现(1001 - 9) > 180,所以第1001位也不可能是一个两位数中的某位,而(1001 - 9 - 180) < 2700,因此可以断定序列的第1001位是一个三位数中的某一位。
2、确定是几位数的第几个数,然后确定具体数值
现在已经知道了序列的第1001位是一个三位数中的某一位,那到底是哪一个三位数呢,很简单,计算方法为base + (n + i - 1)/i - 1。( [n / i]上取整 = [(n + i - 1) / i])
base表示这一位的第一个数是多少,1位的第一个数是1,2位的第一个数是10,3位是100,4位是1000。
i是位数。
n是序列的第n位。
因此,100 + (( 1001 - 9 - 180 + 3 - 1)/ 3 ) - 1 = 370
3、确定属于那个数的第几位
ok,现在也知道了它是属于370的某位的,那到底是哪一位,求余就好了
(1001 - 9 - 180) % 3 = 2,所以答案是370中的第二位,即7。
c++代码
class Solution {
public:
int findNthDigit(int n) {
//n++ 从第1位开始计数
//n-- 先把0去掉,一位数字变成了9个
// i枚举的是位数,从1位开始枚举
// s表示这一位一共有多少个数,1位有9个数
// base表示这一位的第一个数是多少,1位的第一个数是1,2位的第一个数是10,3位是100,4位是1000
long long i = 1,s = 9, base = 1;
while( n > i*s )
{
n -= i * s;
i++;
s *= 10;
base *= 10;
}
//确定i位数的第n位是属于哪个数 [n/i]上取整 = [(n+i-1)/i]
int number = base + (n + i - 1)/i - 1;
//确定那个数的第几位
int r = n % i ? n % i : i; // 除不尽就是第几位,除尽了就是最后一位
for(int j = 0; j < i - r; j++) number /= 10;
return number % 10;
}
};