当前位置: 代码迷 >> 综合 >> 【leecode 剑指offer】 1~n整数中1出现的次数
  详细解决方案

【leecode 剑指offer】 1~n整数中1出现的次数

热度:53   发布时间:2023-11-22 21:21:28.0

题目

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例

  • 示例1

输入:n = 12
输出:5

  • 示例2

输入:n = 13
输出:6

解题思路

将 11 ~ nn 的个位、十位、百位、…的 11 出现次数相加,即为 11 出现的总次数。

这边可以使用排列组合的方法解决,统计所有1出现的次数,我们可以吧所有整数理解为行李箱上的密码锁,先固定某一位上的数据控制为1,然后归纳所有数字的出现的次数。

我们假设 n = 13 (用个小点的数,比较容易举例)
在这里插入图片描述
我们需要统计小于等于 13 的数中,出现 1 的次数,

通过上图可知,个位上 1 出现 2 次,十位上 1 出现 4 次,

那么总次数为 2 + 4 = 6 次。

另外我们发现 11 这个数,会被统计 2 次,它的十位和个位都为 1 ,
而我们这个题目是要统计 1 出现的次数,而不是统计包含 1 的整数,所以上诉方法不会出现重复统计的情况。

我们题目已经有大概思路啦,下面的难点就是如何统计每一位中 1 出现的次数呢?

我们完全可以通过遍历 n 的每一位来得到总个数,见下图:
在这里插入图片描述
假设我们想要得到十位上 1 出现的次数,当前我们指针指向十位,

我们称之为当前位。num 则代表当前位的位因子,当前位为个位num = 1十位时为 10,百位时为 100…

那我们将当前位左边的定义为高位当前位右边的定义位低位

例:n = 1004 ,此时指针指向十位(当前位)num = 10,高位为百位,千位,低位为个位

而且我们某一位的取值范围为 0 ~ 9,那么我们可以将这 10 个数分为 3 类小于 1 (当前位数字为 0 ),等于 1(当前位数字为 1 ) ,大于 1(当前位上数字为 2 ~ 9),下面我们就来分别考虑三种情况。

我们进行举例的 n 为 1004,1014,1024。重点讨论十位上 3 种不同情况。大家阅读下方文字之前,先想象自己脑子里有一个行李箱的滚轮密码锁,我们固定其中的某一位,然后可以随意滑动其他位,这样可以帮助大家理解。

n = 1004
我们想要计算出小于等于 1004 的非负整数中,十位上出现 1 的次数。

也就是当前位为十位,数字为 0 时,十位上出现 1 的次数
在这里插入图片描述

解析:为什么我们可以直接通过高位数字 * num,得到 1 出现的次数
因为我们高位为 10,可变范围为 0 ~ 10,但是我们的十位为 0 ,所以高位为 10 的情况取不到,所以共有 10 种情况。
又当前位为十位,低位共有 1 位,可选范围为 0 ~ 9 共有 10 种情况,所以直接可以通过 10 * 10 得到。

其实不难理解,我们可以设想成行李箱的密码盘,在一定范围内,也就是上面的 0010 ~ 0919固定住一位为 1 ,只能移动其他位,看共有多少种组合。

好啦,这个情况我们已经搞明白啦,下面我们看另一种情况。

n = 1014

我们想要计算出小于等于 1014 的非负整数中,十位上出现 1 的次数。

也就是当前位为十位数字为 1 时,十位上出现 1 的次数

我们在小于 1014 的非负整数中,十位上为 1 的最小数字为 10最大数字为 1014,所以我们需要在 10 ~ 1014 这个范围内固定住十位上的 1 ,移动其他位。

其实然后我们可以将 1014 看成是 1004 + 10 = 1014

则可以将 10 ~ 1014 拆分为两部分 0010 ~ 0919 (小于 1004 ),1010 ~ 1014。

见下图
在这里插入图片描述

解析:为什么我们可以直接通过 高位数字 * num + 低位数字 + 1 即 10 * 10 + 4 + 1
得到 1 出现的次数
高位数字 * num 是得到第一段的次数,第二段为 低位数字 + 1,求第二段时我们高位数字和当前位已经固定,
我们可以改变的只有低位。
可以继续想到密码盘,求第二段时,把前 3 位固定,只能改变最后一位。最后一位最大能到 4 ,那么共有几种情况?

n = 1024

我们想要计算出小于等于 1024 的非负整数中,十位上出现 1 的次数。

也就是当前位为十位,数字为 2 ~ 9 时,十位上出现 1 的次数。其中最小的为 0010,最大的为 1019

我们也可以将其拆成两段 0010 ~ 0919,1010 ~ 1019

可以继续想到密码盘,求第二段时,把前 3 位固定,只能改变最后一位。最后一位最大能到 4 ,那么共有几种情况?

n = 1024

我们想要计算出小于等于 1024 的非负整数中,十位上出现 1 的次数。

也就是当前位为十位,数字为 2 ~ 9 时,十位上出现 1 的次数。其中最小的为 0010,最大的为 1019

我们也可以将其拆成两段 0010 ~ 0919,1010 ~ 1019
在这里插入图片描述

解析:为什么我们可以直接通过高位数字 * num + num, 10 * 10 + 10 得到 1 出现的次数
第一段和之前所说一样,第二段的次数,我们此时已经固定了高位和当前位,当前位为 1,低位可以随意取值,上诉例子中,当前位为 10,低位为位数为 1,则可以取值 0 ~ 9 的任何数,则共有 10 (num) 种可能。

好啦,这个题目大家应该理解的差不多啦。

算法实现

/*** @param {number} n* @return {number}*/
var countDigitOne = function(n) {
    //高位var high = n;//低位var low = 0;//当前位var cur = 0;var count = 0;var num = 1;while (high != 0 || cur != 0) {
    cur = high % 10;high = Math.floor(high/10) ;//这里我们可以提出 high * num 因为我们发现无论为几,都含有它 if (cur == 0) count += high * num;else if (cur == 1) count += high * num + 1 + low; else count += (high + 1) * num;//低位low = cur * num + low;                  num *= 10;}return count;};
注意事项

这边需要注意的是,在对高位high除10取整时一定要注意使用Math.floor()函数——向下取整,JS除法在运算中不会对除法四舍五入,所以这边一定要注意数据的准确性和一致性。如果不加Math.floor()这个方法,最后得出的结果是NAN

  相关解决方案