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

解法一:不考虑时间效率的解法(略)

ps:我感觉是个程序员都能想到这第一种解法,时间复杂度O(nlogn)。这个方法没有什么意义,但是简单易懂,去小公司足够了,这里不讲了。

解法二:分析数字规律,时间复杂度O(logn).

这是我写这篇文章的初衷。《剑指offer》洋洋洒洒写了几十行代码,然而在leetcode上大神却只用了5行!当天晚上智障,脑子全是浆糊,竟然没有看懂什么意思=。=,我一度怀疑智商受到了碾压。然而在今天睡眠比较充足,头脑比较清醒的情况下终于理顺了思路~

其实这道题目很多地方都有讲,包括《编程之美》,但是也有20行左右的代码,没耐心了。其它的一些帖子讲的乱七八糟,这对于我这种爱简洁,爱干净,还有严重强迫症的人是不能忍的,下面强迫症患者要开始装逼了。。。

先上代码:

 package test;

 public class Question_32 {
public static int countDigitOne(int n) {
int ones = 0;
for (long m = 1; m <= n; m *= 10)
ones += (n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0);
return ones;
} public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(countDigitOne(12)); } }

核心代码只有line4~line9。leetcode原链接: https://discuss.leetcode.com/topic/18054/4-lines-o-log-n-c-java-python 牛客网链接: https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6 。

装逼模式开启:

我们从一个5位的数字讲起,先考虑其百位为1的情况。分3种情况讨论:

百位数字>=2  example: 56  当其百位为>=时,有以下这些情况满足(为方便起见,计312为a,56为b):

100 ~   199

100 ~  199

.....

100 ~ 199

余下的都不满足!

因此,百位>=2的5位数字,其百位为1的情况有(a/10+1)*100个数字   (a/10+1)=>对应于 0 ~ 31,且每一个数字,对应范围是100个数(末尾0-99)

百位数字 ==1 example: 56 当其百位为1时,有以下这些情况满足:

100 ~   199

100 ~  199

......

100 ~ 199

100 ~ 156

因此,百位为1的5位数字,共有(a/10)*100+(b+1)

百位数字 ==0 example: 56 当其百位为0时,有以下这些情况满足:

100 ~   199

100 ~  199

100 ~ 199

其余都不满足

因此,百位数为0的5位数字,共有(a/10)*100个数字满足要求

我们可以进一步统一以下表达方式,即当百位>=2或=0时,有[(a+8)/10]*100,当百位=1时,有[(a+8)/10]*100+(b+1)。用代码表示就是: [(a+8)/10]*100+(a%10==1)?(b+1):0;

为什么要加8呢?因为只有大于2的时候才会产生进位等价于(a/10+1),当等于0和1时就等价于(a/10)。另外,等于1时要单独加上(b+1),这里我们用a对10取余是否等于1的方式判断该百位是否为1。

Question:有缺陷或逻辑错误吗?

有人可能会有疑惑,比如11100,这个数在考虑百位为1的时候算作了一次,在考虑千位的时候也算了一次,在考虑万位为1的时候又算了一次,一共计了3次,这不是明显重复吗?

我的回答是,不重复!

分析:题目中要我们统计出现的1的个数,那么我们可以看到11100一共是3个1,如果剔除了重复的情况只考虑一次才会是问题。换言之,在计算从1到n整数中1的出现次数时,我们把10位出现1的情况个数加上百位出现1的情况个数一直加到最高位是1的情况的个数,这里面一个数可能被统计过多次;11100百位出现1,千位和万位都为1,那么被重复统计了3次

代码分析:

 public static int countDigitOne(int n) {
int ones = 0;
for (long m = 1; m <= n; m *= 10)
ones += (n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0);
return ones;
}

for (long m = 1; m <= n; m *= 10) 在这里的作用是,从个位开始考虑,再到十位,百位,千位,一直到超出这个数!为什么m要用long型呢?因为n可能没有超过整型的表达范围(int刚好可以表示n),而10*m恰恰有可能刚刚超过!ones += (n/m + 8) / 10 * m + (n/m % 10 == 1 ? n%m + 1 : 0); 这里ones用于表示1的个数,当m=100时,n/m其实代表的是a,而n%m代表的是b,此时考虑的是百位为1的情况;当m=1000,自然考虑的就是千位等于1的情况了~ 至于为什么加8,那个三目运算符是干嘛子用的上面都已经讲过了。

最后,总结一下。这道题网上答案太多了,但是我觉得只有这种方法最让人眼前一亮。抖机灵的不少,比如用java字符串处理的,自以为很厉害,其实根本没含金量(时间复杂度O(nlogn)啊!)关键是这还有赞同的,不知道算法分析是怎么学的。《剑指offer》和《编程之美》的答案可能曾经是最佳,但是现在被更好的方法替换了,而作者并不知情。一本好书看3遍,胜过3本好书看一遍!相信一个月后,我对这道题的印象可能就没有多少了,及时整理,利人利己,温故而知新~

 

《剑指offer》面试题32----从1到n整数中1出现的次数的更多相关文章

  1. 剑指Offer:面试题32——从1到n整数中1出现的次数(java实现)

    问题描述: 输入一个整数n,求1到n这n个整数的十进制表示中1出现的次数.例如输入12,从1到12这些整数中包含1的数字有1,10,11,12,1一共出现了5次. 思路:(不考虑时间效率的解法,肯定不 ...

  2. 【剑指Offer面试编程题】题目1373:整数中1出现的次数--九度OJ

    题目描述: 亲们!!我们的外国友人YZ这几天总是睡不好,初中奥数里有一个题目一直困扰着他,特此他向JOBDU发来求助信,希望亲们能帮帮他.问题是:求出1~13的整数中1出现的次数,并算出100~130 ...

  3. 剑指Offer的学习笔记(C#篇)-- 整数中1出现的次数(从1到n整数中1出现的次数)

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

  4. 【剑指Offer】31、从1到n整数中1出现的次数

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

  5. 【剑指Offer面试题】 九度OJ1517:链表中倒数第k个结点

    鲁棒性是指程序可以推断输入是否符合规范要求,并对不和要求的输入予以 合理的处理. 题目链接地址: http://ac.jobdu.com/problem.php?pid=1517 题目1517:链表中 ...

  6. 剑指offer——面试题32.1:分行从上到下打印二叉树

    void BFSLayer(BinaryTreeNode* pRoot) { if(pRoot==nullptr) return; queue<BinaryTreeNode*> pNode ...

  7. 剑指offer——面试题32:从上到下打印二叉树

    void BFS(BinaryTreeNode* pRoot) { if(pRoot==nullptr) { cout<<"empty binary tree!"< ...

  8. 【剑指offer 面试题38】数字在排序数组中出现的次数

    思路: 利用二分查找,分别查找待统计数字的头和尾的下标,最后做差加一即为结果. C++: #include <iostream> #include <vector> using ...

  9. 剑指offer——面试题15.2:判断两个整数m和n的二进制中相差多少位

    #include"iostream" using namespace std; int CountDifferentBit(int m,int n) { ,diff=m^n; wh ...

  10. 面试题32.从1到n整数中1出现的次数

    题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.例如输入12,从 1到12这些整数中包含1的数字中1,10,11和12,1一共出现了5次 本题可以直接变量1到n的n个数然后分别计 ...

随机推荐

  1. 《Spring1之 第一次站立会议(重发)》

    < 第一次站立会议(重发)> 昨天,我对我们团队要做的项目进行了相关资料的查找,我找到了服务器和客户端的相关代码以及找到了把它们通信所使用TCP/IP等协议: 今天,我把找到的代码和协议资 ...

  2. 作业45//Calculator::3.0

    计算器 github 我的天我到底要写什么 一,2.0及2.6的改动 做了计算部分 加入了判断输入是否合法 合法的定义是算式符合`数字+运算符+数字+运算符+数字`的格式 其中`"-&quo ...

  3. 【CSAPP笔记】4. 汇编语言——基础知识

    程序的机器级表示 计算机能读懂是机器代码(machine code)-- 用字节序列编码的低级操作 -- 也就是0和1.编译器基于编程语言的规则.目标机器的指令集和操作系统的规则,经过一系列阶段产生机 ...

  4. 1003 我要通过!| PAT (Basic Level) Practice

    1003 我要通过! (20 分) "答案正确"是自动判题系统给出的最令人欢喜的回复.本题属于 PAT 的"答案正确"大派送 -- 只要读入的字符串满足下列条件 ...

  5. diliucizuoye

    NABCD N(Need 需求) 互联网的高速发展,造就了二十一世纪这个追求高品质.高体验的信息时代,随其发展改变的是信息记录与分享方式,从传统的面对面交流.手机通话.写日记本,到现如今的社交平台.信 ...

  6. .Net用字符串拼接实现表格数据相同时合并单元格

    前言 最近在做项目通过GridView或Repeater绑定数据,如果两行或若干行某列值相同,需要进行合并单元格,但是实现过程中想到了字符串拼接,于是就没用绑定数据控件,而是用了html结合字符串实现 ...

  7. lr基本的概念

    并发用户数量:与服务器进行交互的在线用户数量   请求响应时间:从client端发出请求到得到响应的整个时间,它一般包括网络相应时间+server的响应时间   事务请求响应时间:完成这个事务所用的时 ...

  8. javascript中boolean类型和其他类型的转换

    在javascript中,if语句括号中的表达式返回值可以是任何类型,即:if(a)中的a可以是boolean.number.string.object.function.undefined中的任何类 ...

  9. git 常用命令(含删除文件)

    git 常用命令(含删除文件) Git常用操作命令收集: 1) 远程仓库相关命令 检出仓库:$ git clone git://github.com/jquery/jquery.git 查看远程仓库: ...

  10. 关于 Source Engine 2007 网络通信的分析

    最近在写自己的游戏引擎,主要是参考Quake和GoldSrc和SourceEngine2007,其中SourceEngine2007代码比较新一些. 对比了这几个引擎的代码,前两者代码比较简单,基于C ...