今天我们来看一道贼棒的题目,题目不长,很经典,也很容易理解,我们一起来看一哈吧,

大家也可能做过这道题,那就再复习一下,如果没做过的话,可以看完文章,自己去 AC 一下,不过写代码的时候,要自己完全写出来,这样才能有收获,下面我们看题目吧。

leetcode 233. 数字 1 的个数

题目描述

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

示例 1:

输入:n = 13
输出:6

示例 2:

输入:n = 0
输出:0

太喜欢这种简洁的题目啦,言简意赅,就是让咱们找出小于等于 n 的非负整数中数字 1 出现的个数

大家看到这个题目的第一印象,可能会这样想,哦,让我们求 1 的个数。

呐我们直接逐位遍历每个数的每一位,当遇到 1 的时候,计数器 +1,不就行了。

嗯,很棒的方法,可惜会超时。(我试了)

或者说,我们可以先将所有数字拼接起来,然后再逐位遍历,这样仍会超时。(我也试了)

大家再思考一下还有没有别的方法呢?

既然题目让我们统计小于等于 n 的非负整数中数字 1 出现的个数。

那我们可以不可这样统计。

我们假设 n = abcd,某个四位数。

那我们完全可以统计每一位上 1 出现的次数,个数上 1 出现的次数,十位上 1 出现的次数,百位 ,千位。。。

也就是说小于等于 n 的所有数字中,个位上出现 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 种不同情况。大家阅读下方文字之前,先想象自己脑子里有一个行李箱的滚轮密码锁,我们固定其中的某一位,然后可以随意滑动其他位,这样可以帮助大家理解。

注:该比喻来自与网友 ryan0414,看到的时候,不禁惊呼可太贴切了!

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

解析:为什么我们可以直接通过高位数字 * num + num, 10 * 10 + 10 得到 1 出现的次数

第一段和之前所说一样,第二段的次数,我们此时已经固定了高位和当前位,当前位为 1,低位可以随意取值,上诉例子中,当前位为 10,低位为位数为 1,则可以取值 0 ~ 9 的任何数,则共有 10 (num) 种可能。

好啦,下面我们看一下题目代码吧

注:下方代码没有简写,也都标有注释

class Solution {public int countDigitOne(int n) {// 高位int high = n;// 低位int low = 0;// 当前位int cur = 0;int count = 0;int num = 1;while(high != 0 || cur != 0) {cur = high % 10;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;}
}

时间复杂度 : O(logn) 空间复杂度 O(1)

剑指 Offer 43. 1~n 整数中 1 出现的次数(可能是最简洁易懂的)相关推荐

  1. 剑指Offer(43)1~n整数中1出现的次数

    题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...

  2. 【LeetCode笔记】剑指Offer 43. 1~n 整数中1出现的次数(Java、数位dp、偏数学)

    文章目录 题目描述 思路 && 代码 二刷 打卡第九天啦- 题目描述 有点像数字序列中的某一位 思路 && 代码 主体思路:从低到高,计算出每一位出现的1的个数. 三种 ...

  3. 《剑指offer》【剑指Offer 43.1 ~n整数中1出现的次数】

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

    题目 输入一个整数 n ,求1-n这n个整数的十进制表示中1出现的次数. 例如,输入12,1-12这些整数中包含1 的数字有1.10.11和12,1一共出现了5次. 示例 示例1 输入:n = 12 ...

  5. 【LeetCode】剑指 Offer 43. 1~n 整数中 1 出现的次数

    [LeetCode]剑指 Offer 43. 1-n 整数中 1 出现的次数 文章目录 [LeetCode]剑指 Offer 43. 1-n 整数中 1 出现的次数 package offer;pub ...

  6. 剑指offer:二维数组中的查找

    目录 题目 解题思路 具体代码 题目 题目链接 剑指offer:二维数组中的查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺 ...

  7. 利用指针数组计算2个3行4列矩阵的和_[剑指 Offer] 04. 二维数组中的查找

    难度:简单 题目链接:剑指 Offer 04. 二维数组中的查找 题目描述: 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数, ...

  8. 剑指 Offer 04. 二维数组中的查找

    剑指 Offer 04. 二维数组中的查找 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个高效的函数,输入这样的一个二维数组和一个 ...

  9. 【LeetCode】剑指 Offer 16. 数值的整数次方

    [LeetCode]剑指 Offer 16. 数值的整数次方 文章目录 [LeetCode]剑指 Offer 16. 数值的整数次方 package offer;public class Soluti ...

  10. 【LeetCode】剑指 Offer 04. 二维数组中的查找

    [LeetCode]剑指 Offer 04. 二维数组中的查找 文章目录 [LeetCode]剑指 Offer 04. 二维数组中的查找 package offer;public class Solu ...

最新文章

  1. 如何通过日期属性对数组进行排序
  2. BLOG地址变更--博客园启用二级域名
  3. [NOIP2021] 数列(计数dp)
  4. Java高级语法笔记-模板类
  5. Dubbo 新增本地 IDE 插件,快速创建样例工程
  6. 表单提交数据丢失的问题
  7. Ios开发之定位CLLocationManager
  8. 易车与中国海洋报社达成深度合作 开拓海洋主题汽车内容
  9. python中intvar_Python的IntVar设置
  10. python自动化办公教程百度云-用Python自动办公,做职场高手,16章完整版百度云盘...
  11. 防范非法用户入侵系统秘籍
  12. MRP专题二:计划策略(MTO)
  13. ARM Linux ELF加壳方案
  14. 2021数据库MYSQL语句梳理(Navicat)
  15. 音质好的蓝牙耳机有哪些?音质好的蓝牙耳机测评
  16. 解决Photoshop CS3 输入中文不能显示 输入文字不能显示 输入文字显示缓慢
  17. vue大屏展示高度自适应
  18. android 单点跟长按的区别,单点触控和多点触控区别是什么?原理分析
  19. PC-DMIS 2019示值误差程序MPEE
  20. 闲人闲谈PS之三十三——项目承诺成本管理

热门文章

  1. 电信诈骗?一招让骗子血本无归!
  2. Golang之变量去哪儿
  3. 吉长江:基于学习的视频植入技术是未来趋势
  4. C/C++学习之路_九:文件操作
  5. 算法工程师眼中的AI岗位
  6. 下篇 | 说说无锁(Lock-Free)编程那些事(下)
  7. FFMPEG结构体分析之AVFormatContext
  8. ffmpeg源码学习之time_base
  9. 入门启发:音视频的简单理解
  10. Springboot Gitlab Jenkins Maven Docker 持续集成/持续交付