剑指offer | 整数中1出现的次数(从1到n整数中1出现的次数)(数位统计 logN复杂度 C++)

题目描述

原题链接

求出1 ~ 13的整数中1出现的次数,并算出100 ~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

算法

(数位计算) O ( l o g n ) O(logn) O(logn)
(1)计算每一位上的1出现的个数(从0到n)
(2) 总共循环 l o g n logn logn次,即n的位数

算法思路图解
例如下图是计算 c c c 这一位上(从0到n)出现1的个数

在这里插入图片描述

时间复杂度是 O ( l o g n ) O(logn) O(logn):下面代码时间复杂度是 O ( l o g 2 n ) O(log^2n) O(log2n),但其中一个是不会大于32的;另外我们还可以预处理left和right

空间复杂度是 O ( l o g n ) O(logn) O(logn):需要answer数组存数位信息

进阶题目:AcWing 338.计数问题 (类似整数中1出现的次数 数位统计DP C++)

C++代码1

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n) {
        if (!n) return 0;
        // n = 123 answer = [3, 2, 1]
        vector<int> answer; 
        while(n) answer.push_back(n % 10), n /= 10;
        
        int res = 0;
        for (int i = answer.size() - 1; i >= 0; i --) {
            int left = 0, right = 0, t = 1;
            for (int j = answer.size() - 1; j > i; j --) left = left * 10 + answer[j];
            for (int j = i - 1; j >= 0; j --) right = right * 10 + answer[j], t *= 10;
            
            res += left * t;
            if (answer[i] == 1) res += right + 1;
            if (answer[i] > 1) res += t;
        }
        
        return res;
    }
};

2021/2/21 更新

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n) {
        vector<int> digit;
        // n: 123456
        while (n) {
            digit.push_back(n % 10);
            n /= 10;
        }
        reverse(digit.begin(), digit.end());
        // digit: [1, 2, 3, 4, 5, 6]
        int sz = digit.size();

        int res = 0;
        for (int i = 0; i < sz; i ++) {
            int left = 0;
            for (int k = 0; k < i; k ++) left = left * 10 + digit[k];
            int right = 0, t = 1;
            for (int k = i + 1; k < sz; k ++) {
                right = right * 10 + digit[k];
                t = t * 10;
            }

            int c = digit[i];
            res += left * t;
            if (c == 0) res += 0;
            else if (c == 1) res += right + 1;
            else if (c > 1) res += t;
        }
        return res;
    }
};

C++ 代码2 暴力做法

class Solution {
public:
    int countOne(int n) {
        int ans = 0;
        while(n) {
            if (n % 10 == 1) ans ++;
            n /= 10;
        }
        
        return ans;
    }
    int NumberOf1Between1AndN_Solution(int n)
    {
        int res = 0;
        for (int i = 1; i <= n; i ++) {
            res += countOne(i);
        }
        return res;
    }
};

写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

捡起一束光

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

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

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

打赏作者

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

抵扣说明:

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

余额充值