计算从1到n的所有整数中数字1出现的次数 使用cpp

 

  1. 核心思想:
    • 我们从个位开始,逐位向高位分析。
    • 对于每一位,我们计算在这一位上数字1出现的次数。
    • 最后,我们将所有位上1出现的次数相加。
  2. 分类讨论: 对于每一位,我们将其分为三种情况: a) 当前位是0 b) 当前位是1 c) 当前位大于1
  3. 详细解释: 假设我们正在分析的数字是abcde,现在我们关注c这一位: a) 如果c是0:
    • 1只会出现在更高位的变化中,即00100到ab100的所有数中。
    • 次数:ab * 100
    b) 如果c是1:
    • 除了上面的情况,还包括ab100到ab1de的所有数。
    • 次数:ab * 100 + (de + 1)
    c) 如果c大于1:
    • 包括00100到ab100,以及ab100到ab199的所有数。
    • 次数:(ab + 1) * 100
  4. 代码实现:
    • 我们使用一个辅助函数countForPosition来计算每一位上1出现的次数。
    • 在这个函数中,我们首先计算出当前位的高位数字、低位数字和位数。
    • 然后根据当前位的数字(0、1或大于1)应用相应的计算公式。
    • 主函数countOnes遍历每一位,调用countForPosition并累加结果。
  5. 优化:
    • 我们使用字符串来处理数字,这样可以方便地获取每一位的数字和进行子串操作。
    • 通过将计算逻辑封装在一个类中,我们提高了代码的可读性和可维护性。

这个算法的时间复杂度是O(log n),因为我们只需要遍历数字的每一位。空间复杂度是O(log n),主要用于存储数字的字符串表示。

#include <iostream>
#include <string>

class DigitCounter {
private:
    // 计算1到n中1出现的次数
    int countOnes(int n) {
        if (n <= 0) return 0; // 如果n <= 0,直接返回0

        std::string numStr = std::to_string(n); // 将整数n转换为字符串
        int len = numStr.length(); // 获取字符串长度
        int result = 0; // 用于存储1的总出现次数

        for (int i = 0; i < len; ++i) {
            result += countForPosition(numStr, i); // 逐位计算1的出现次数并累加
        }

        return result; // 返回总的1的出现次数
    }

    // 计算某一位上的数字为1的情况
    int countForPosition(const std::string& numStr, int pos) {
        int len = numStr.length(); // 数字的总长度
        int currentDigit = numStr[len - 1 - pos] - '0'; // 当前位的数字,从右到左计算
        int count = 0; // 当前位1的出现次数

        // 计算高位数字
        int higher = 0; 
        if (pos < len - 1) {
            higher = std::stoi(numStr.substr(0, len - 1 - pos)); // 提取当前位之前的高位数字
        }

        // 计算低位可能性
        int lower = 0;
        if (pos > 0) {
            lower = std::stoi(numStr.substr(len - pos)); // 提取当前位之后的低位数字
        }

        // 计算位数权重,如个位是1,十位是10,百位是100
        int power = 1;
        for (int i = 0; i < pos; ++i) {
            power *= 10;
        }

        // 根据当前位的数字分三种情况讨论
        if (currentDigit == 0) {
            count = higher * power; // 当前位为0时,1出现次数由高位决定
        } else if (currentDigit == 1) {
            count = higher * power + lower + 1; // 当前位为1时,1出现次数由高位和低位决定
        } else {
            count = (higher + 1) * power; // 当前位大于1时,1出现次数由高位决定且比higher多1
        }

        return count; // 返回当前位的1的出现次数
    }

public:
    // 公开接口,用于计算1到n之间的1的出现次数
    int countDigitOne(int n) {
        return countOnes(n); // 调用私有方法countOnes进行计算
    }
};

int main() {
    DigitCounter counter; // 创建DigitCounter对象
    int n = 2314; // 示例输入数字
    std::cout << "Number of digit 1 appearances from 1 to " << n 
              << " is: " << counter.countDigitOne(n) << std::endl; // 输出结果
    return 0; // 程序结束
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值