leetcode+数字1的个数 小安晋升分享(第233题哦)

本文深入解析LeetCode第233题“数字1的个数”,通过实例详细阐述了如何计算从1到n中数字1出现的次数,采用分治策略,逐位分析最高位对1的贡献,最终得出完整算法并提供Java代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数字1的个数

今天是小安开始Leetcode刷题的第233题,正文开始ing?

题目描述

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。题目原址
示例

输入: 13
输出:6
解释:数字 1 出现在以下数字中: 1, 10, 11, 12, 13 。

方法:暴力法

问题转化

求最高位对1的贡献

例如输入193,我们先求1作为最高位百位对1的贡献,假设结果为c1;然后我们将最高位1去掉,就剩下93,我们求得9作为最高位十位对1的贡献为c2;同样我们去掉9,剩下3,我们求得3作为最高位个位对1的贡献是c3。

答案 = c1+c2+c3。

那么,求最高位对1的贡献怎么求?按照以下规则:

如果n是一位数,最高位就是个位,由于没有比它更小的位数了,因此对1的贡献只有它本身(个位),如果各位数字大于等于1,贡献是1,否则贡献是0。

如果n是两位数,最高位就是十位,因此对1的贡献分为0-9部分和十位本身。0-9:贡献能力是1,再乘以十位数字就是贡献1的个数;十位:贡献大小取决于十位数字,如果十位数字大于1,则贡献是10;如果十位数字等于1,则贡献是去掉十位剩下的数+1。

如果n是三位数,最高位就是百位,因此对1的贡献分为0-99部分和它本身(百位)。0-99:贡献能力是20,百位:贡献大小取决于百位数字,如果百位数字大于1,则贡献是100;如果十位数字等于1,则贡献是去掉百位剩下的数+1。

如果n是四位数,最高位就是千位,因此对1的贡献分为0-999部分和它本身(千位)。0-999:贡献能力是300,千位:贡献大小取决于千位数字,如果千位数字大于1,则贡献是1000;如果千位数字等于1,则贡献是去掉千位剩下的数+1。

以此类推。。。。。。

举例说明

413是个三位数,我们这里先考虑最高位4对1的贡献,根据上面的规则,贡献分为0-99部分和百位部分。首先说0-99贡献:0-99共有20个1,因此贡献能力是20,一共有四次(0-99、100-199、200-299、300-399),因此共贡献了420 = 80个1;再说百位的贡献:因为百位数字是4,大于1,因此百位的贡献就是100(100、101、…、199一共100个 1)。因此,413最高位4对1的贡献 = 420+100 = 180。

你可能会问,这里只考虑最高位4对1的贡献,后面的13也贡献了6个1。没错,因此我们考虑完4对1的贡献后,就要将4剔除,剩下13,我们还是套用此规则。

13是个两位数,我们这里只找最高位1对1的贡献,根据上面的规则,贡献分为0-9部分和十位部分。首先说0-9贡献:0-9共有1个1,因此贡献能力是1,一共有1次(0-9),因此共贡献了11 = 1个1;再说十位的贡献:因为十位数字等于1,因此十位的贡献就是4(去掉十位后的数+1,即3+1,因为10、11、12、13)。因此,13最高位十位1对1的贡献 = 11+4 = 5。

然后剔除十位1,只剩下3了,3大于等于1,所以贡献是1。

因此结果 = 420+100 + 11+4 + 1 = 186。

0-9贡献能力是1,0-99贡献能力是20,0-999贡献能力是300,0-9999贡献能力是4000,这是怎么来的?

1 ∗ 10 + 10 = 20 ; 20 ∗ 10 + 1 0 2 = 300 ; 300 ∗ 10 + 1 0 3 = 400 1*10+10=20;20*10+10^2=300;300*10+10^3=400 110+10=20;2010+102=300;30010+103=400

代码实现

【java实现】

class Solution {
    public int countDigitOne(int n) {
        if (n < 1)
            return 0;
        int len = getLenOfNum(n);
        if (len == 1)
            return 1;
        int tmp = (int) Math.pow(10, len - 1);
        int first = n / tmp; // 获取n的最高位数字
        int firstOneNum = first == 1 ? n % tmp + 1 : tmp; // 获取n的最高位为1时有多少个数字
        int otherOneNUm = first * (len - 1) * (tmp / 10); // 在介于n % tmp到n之间的数字中,除了最高位为1,其余各个数字分别为1 的总数和
        return firstOneNum + otherOneNUm + countDigitOne(n % tmp);
    }
    private int getLenOfNum(int n) {
        int len = 0;
        while (n != 0) {
            len++;
            n /= 10;
        }
        return len;
    }
}
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值