剑指offer 打卡计划 | 每日进步一点点 | 第十五天

98 阅读2分钟

图片.png

剑指 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位数)。

图示过程:

图片.png

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~9919
10~99902180
100~99990032700

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位的第一个数是12位的第一个数是103位是1004位是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;
     }
 };