zoukankan      html  css  js  c++  java
  • 【剑指Offer学习】【面试题32:求从1到n的整数中1出现的次数】

    题目:输入一个整数n求从1 到n这n个整数的十进制表示中1 出现的次数。


    举例说明:

    比如输入12 ,从1 到12 这些整数中包括1 的数字有1、10、11 和12。1 一共出现了5 次。

    题解思路:

    第一种:不夸虑时间效率的解法

    累加1 到n 中每一个整数1 出现的次数。

    我们能够每次通过对10 求余数推断整数的个位数字是不是1 。

    假设这个数字大于10。除以10 之后再推断个位数字是不是1 。

    另外一种:从数字规律着手明显提高时间效率的解法

    21345 作为样例来分析。我们把从1 到21345 的全部数字分为两段, 一段是从1 到1345。还有一段是从1346 到21345。

    我们先看从1346 到21345 中1 出现的次数。

    1 的出现分为两种情况。首先分析1出如今最高位(本例中是万位)的情况。从1346 到21345 的数字中, 1出如今10000~19999 这10000 个数字的万位中, 一共出现了10000(10^4)个。

    值得注意的是, 并非对全部5 位数而言在万位出现的次数都是10000
    个。对于万位是1 的数字比方输入12345, 1 仅仅出如今10000~ 12345 的万位。出现的次数不是10^4 次。而是2346 次,也就是除去最高数字之后剩下的数字再加上1 (即2345+1=2346 次)。

    接下来分析1出如今除最高位之外的其它四位数中的情况。样例中1346~21345 这20000 个数字中后4 位中1 出现的次数是2000 次。

    由于最
    高位是2。我们能够再把1346~21345 分成两段, 1346~11345 和1 1346~21345 。

    每一段剩下的4 位数字中, 选择当中一位是1 ,其余三位能够在0~9 这10 个数字中随意选择。因此依据排列组合原则,总共出现的次数是2*10^3=2000

    至于从l 到1345 中1 出现的次数,我们就能够用递归求得了。

    这也是我们为什么要把1~21345 分成1~ 1 345 和1346~21345 两段的原因。由于把21345 的最高位去掉就变成1345 。便于我们採用递归的思路。

    本题採用另外一种解法

    代码实现:

    public class Test32 {
    
        /**
         * 题目:输入一个整数n求从1 到n这n个整数的十进制表示中1 出现的次数。
         * @param n 最大的数字
         * @return 1-n中,各个数位1出现的次数
         */
        public static int numberOf1Between1AndN(int n) {
            if (n <= 0) {
                return 0;
            }
    
            String value = n + "";
            int[] numbers = new int[value.length()];
    
            for (int i = 0; i < numbers.length; i++) {
                numbers[i] = value.charAt(i) - '0';
            }
    
            return numberOf1(numbers, 0);
        }
    
        /**
         * 求0-numbers表的数字中的1的个数
         *
         * @param numbers 数字,如{1, 2, 3, 4, 5}表示数字12345
         * @param curIdx  当前处理的位置
         * @return 1的个数
         */
        private static int numberOf1(int[] numbers, int curIdx) {
    
            if (numbers == null || curIdx >= numbers.length || curIdx < 0) {
                return 0;
            }
            // 待处理的第一个数字
            int first = numbers[curIdx];
    
            // 要处理的数字的位数
            int length = numbers.length - curIdx;
    
            // 假设仅仅有一位且这一位是0返回0
            if (length == 1 && first == 0) {
                return 0;
            }
    
            // 假设仅仅有一位且这一位不是0返回1
            if (length == 1 && first > 0) {
                return 1;
            }
    
            // 假设numbers是21345
            // numFirstDigit是数字10000-19999的第一个位中的数目
            int numFirstDigit = 0;
            // 假设最高位不是1,如21345,在[1236, 21345]中。最高位1出现的仅仅在[10000, 19999]中。出现1的次数是10^4方个
            if (first > 1) {
                numFirstDigit = powerBase10(length - 1);
            }
            // 假设最高位是1,如12345。在[2346, 12345]中。最高位1出现的仅仅在[10000, 12345]中。总计2345+1个
            else if (first == 1) {
                numFirstDigit = atoi(numbers, curIdx + 1) + 1;
            }
    
            // numOtherDigits。是[1346, 21345]中。除了第一位之外(不看21345中的第一位2)的数位中的1的数目
            int numOtherDigits = first * (length - 1) * powerBase10(length - 2);
            // numRecursive是1-1234中1的的数目
            int numRecursive = numberOf1(numbers, curIdx + 1);
    
            return numFirstDigit + numOtherDigits + numRecursive;
        }
    
        /**
         * 将数字数组转换成数值,如{1, 2, 3, 4, 5},i = 2,结果是345
         * @param numbers 数组
         * @param i 開始黑气的位置
         * @return 转换结果
         */
        private static int atoi(int[] numbers, int i) {
            int result = 0;
            for (int j = i; j < numbers.length; j++) {
                result = (result * 10 + numbers[j]);
            }
            return result;
        }
    
        /**
         * 求10的n次方,假定n不为负数
         * @param n 幂,非负数
         * @return 10的n次方
         */
        private static int powerBase10(int n) {
            int result = 1;
            for (int i = 0; i < n; i++) {
                result *= 10;
            }
            return result;
        }
    
        public static void main(String[] args) {
            System.out.println(numberOf1Between1AndN(1)); // 1
            System.out.println(numberOf1Between1AndN(5)); // 1
            System.out.println(numberOf1Between1AndN(10)); // 2
            System.out.println(numberOf1Between1AndN(55)); // 16
            System.out.println(numberOf1Between1AndN(99)); // 20
            System.out.println(numberOf1Between1AndN(10000)); // 4001
            System.out.println(numberOf1Between1AndN(21345)); // 18821
            System.out.println(numberOf1Between1AndN(0)); // 0
        }
    }
    

    执行结果:

    这里写图片描写叙述

  • 相关阅读:
    第一次站立会议
    构建之法阅读笔记03
    软件工程概论学习进度条03
    四则运算三
    构建之法阅读笔记02
    软件工程概论学习进度条02
    四则运算二
    构建之法阅读笔记01
    软件工程概论学习进度条1
    软件工程个人作业01
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7233961.html
Copyright © 2011-2022 走看看