前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >每天一道剑指offer-整数中1出现的次数

每天一道剑指offer-整数中1出现的次数

作者头像
乔戈里
发布2019-03-06 10:41:44
7820
发布2019-03-06 10:41:44
举报
文章被收录于专栏:Java那些事

整数中1出现的次数(从1到n整数中1出现的次数)

题目描述

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

解析
遍历一遍不就完了吗

当然,你可从1遍历到n,然后将当前被遍历的到的数中1出现的次数累加到结果中可以很容易地写出如下代码:

代码语言:javascript
复制
public int NumberOf1Between1AndN_Solution(int n) {    if(n < 1){        return 0;    }    int res = 0;    for(int i = 1 ; i <= n ; i++){        res += count(i);    }    return res;}
public int count(int n){    int count = 0;    while(n != 0){        //取个位        count = (n % 10 == 1) ? ++count : count;        //去掉个位        n /= 10;    }    return count;}

但n多大就会循环多少次,这并不是面试官所期待的,这时我们就需要找规律看是否有捷径可走

不用数我也知道

51234这个数为例,我们可以先将 51234划分成 1~1234(去掉最高位)和 1235~51234两部分来求解。下面先分析 1235~51234这个区间的结果:

  1. 所有的数中,1在最高位(万位)出现的次数 对于 1235~51234,最高位为1时(即万位为1时)的数有 10000~19999这10000个数,也就是说1在最高位(万位)出现的次数为10000,因此我们可以得出结论:如果最高位大于1,那么在最高位上1出现的次数为最高位对应的单位(本例中为一万次);但如果最高位为1,比如 1235~11234,那么次数就为去掉最高位之后的数了, 11234去掉最高位后是 1234,即1在最高位上出现的次数为 1234
  2. 所有的数中,1在非最高位上出现的次数 我们可以进一步将 1235~51234按照最高位的单位划分成4个区间(能划分成几个区间由最高位上的数决定,这里最高位为5,所以能划分5个大小为一万子区间):
  • 1235~11234
  • 11235~21234
  • 21235~31234
  • 31235~41234
  • 41235~51234 而每个数不考虑万位(因为1在万位出现的总次数在步骤1中已统计好了),其余四位(个、十、百、千)取一位放1(比如千位),剩下的3位从 0~9中任意选( 10*10*10),那么仅统计1在千位上出现的次数之和就是: 5(子区间数)*10*10*10,还有百位、十位、个位,结果为: 4*10*10*10*5。 因此非高位上1出现的总次数的计算通式为: (n-1)*10^(n-2)*十进制最高位上的数(其中 n为十进制的总位数) 于是 1235~51234之间所有的数的所有的位上1出现的次数的综合我们就计算出来了

剩下 1~1234,你会发现这与 1~51234的问题是一样的,因此可以做递归处理,即子过程也会将 1~1234也分成 1~234235~1234两部分,并计算 235~1234而将 1~234又进行递归处理。

而递归的终止条件如下:

  1. 如果 1~n中的 n1<=n<=9,那么就可以直接返回1了,因为只有数1出现了一次1
  2. 如果 n==0,比如将 10000划分成的两部分是 0~0(10000去掉最高位后的结果)1~10000,那么就返回0
代码语言:javascript
复制
public int NumberOf1Between1AndN_Solution(int n) {    if(n < 1){        return 0;    }    return process(n);}
public int process(int n){    if(n == 0){        return 0;    }    if(n < 10 && n > 0){        return 1;    }    int res = 0;    //得到十进制位数    int bitCount = bitCount(n);    //十进制最高位上的数    int highestBit = numOfBit(n, bitCount);    //1、统计最高位为1时,共有多少个数    if(highestBit > 1){        res += powerOf10(bitCount - 1);    }else{        //highestBit == 1        res += n - powerOf10(bitCount - 1) + 1;    }    //2、统计其它位为1的情况    res += powerOf10(bitCount - 2) * (bitCount - 1) * highestBit;    //3、剩下的部分交给递归    res += process(n % powerOf10(bitCount - 1));    return res;}
//返回10的n次方public int powerOf10(int n){    if(n == 0){        return 1;    }    boolean minus = false;    if(n < 0){        n = -n;        minus = true;    }    int res = 1;    for(int i = 1 ; i <= n ; i++){        res *= 10;    }    return minus ? 1 / res : res;}
public int bitCount(int n){    int count = 1;    while((n /= 10) != 0){        count++;    }    return count;}
public int numOfBit(int n, int bit){    while(bit-- > 1){        n /= 10;    }    return n % 10;}

笔者曾纠结,对于一个四位数,每个位上出现1时都统计了一遍会不会有重复,比如 11111这个数在最高位为1时的 10000~19999统计了一遍,在统计非最高位的其他位上为1时又统计了4次,总共被统计了5次,而这个数1出现的次数也确实是5次,因此没有重复。

出自:http://www.zhenganwen.top

已获授权

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员乔戈里 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 整数中1出现的次数(从1到n整数中1出现的次数)
    • 题目描述
      • 解析
        • 遍历一遍不就完了吗
        • 不用数我也知道
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档