剑指offer第二版面试题43:1~n整数中1出现的次数(java)

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

分析思路1:不考虑时间效率的解法,靠它想拿offer有点难
如果在面试的时候碰到这个问题,应聘者大多能想到最直观的方法,也就是累加1到n中每个整数1出现的次数。我们可疑每次通过对10求余数判断整数的个位数字是不是1。如果这个数字大于10,除以10之后再判断个位数字是不是1。

代码如下:
/**
* 1到n整数中1出现的次数
*/
public class OccurrenceNum {

public int times(int n){
    if(n < 1){
        return 0;
    }

    int count = 0;
    for(int i = 1; i <= n; i++){
        count += core(i);
    }
    return count;
}

public int core(int num){
    int times = 0;
    while(num != 0){
        if(num%10 == 1){
            times++;
        }
        num = num / 10;
    }
    return times;
}

public static void main(String[] args) {
    OccurrenceNum test = new OccurrenceNum();
    System.out.println(test.times(120));
}

}
从上述思路中,我们对每个数字都要做出发和求余运算以求出该数字中1出现的次数。如果输入数字n,n有O(logn)位,我们需要判断每一位是不是1,那么它的时间复杂度是O(n*logn)。当输入n非常大的时候,需要大量的计算,运算效率不高。面试官不会满意这种算法

分析思路2:从数字规律着手明显提高时间效率的解法,能让面试官耳目一新
如果希望不用计算每个数字的1的个数,那就只能寻找1在数字中出现的规律了。为了找到规律,我们不妨用一个稍微大一点的数字比如21345作为例子来分析。我们把从1到21345的所有数字分为两段,一段是从1到1345,另一段是从1346到21345.划分的原因是便于利用递归的思路,因为21345去掉最高位就是1345.我们先看1346到21345中1出现的次数。1的出现分为两种情况:1出现在最高位和1出现在其他位。1346到21345,1出现在10000-19999这10000个数字的最高位中,一共出现了10000个,即10^最高位。我们可以发现一般情况:如果是1346到11345,1出现在10000-11345的最高位中,一共出现2346次,也就是除去最高数字之后剩下的数字+1,当万位大于1时,1出现在最高位的次数是10^最高位。

接下来分析1出现在除最高位之外的其他四位数中的情况。1346-21345这20000个数字中后4位出现的次数,分成两段,1346-11345和11346-21345,每一段剩下的4位数字中,选择其中一位是1,其余三位可以在0-9这10个数字中任意选择,根据排列组合原则,总共出现次数2*4*10^3=8000次。我们可以发现一般情况:如果是1346-n1345那么,可以划分为n段,即1出现在除最高位之外的其他四位数的次数是n*4*1000;

代码如下:
/**
* 1到n整数中1出现的次数(使用数学规律)
*
*/
public class OccurrenceNum2 {

public int numberOf1Between1AndN(int n){
    if(n<=0)
        return 0;

    String strN = String.valueOf(n);
    return numberOf1(strN);
}

public int numberOf1(String strN){
    if(strN.length() == 1){
        return 1;
    }
    if(new Integer(strN) <= 0){
        return 0;
    }

    int times = 0;

    Integer m = new Integer(strN);
    Integer n = new Integer(strN.substring(1))+1;

    //计算最高位1的个数
    if(new Integer(strN.substring(0, 1)) > 1){
        times += Math.pow(10, strN.length()-1);
    }else{
        times += new Integer(strN.substring(1))+1;
    }

    //计算其他位1的个数
    times += new Integer(strN.substring(0,1)) * (strN.length()-1) * Math.pow(10, strN.length()-2);

    return times + numberOf1(strN.substring(1));
}

public static void main(String[] args) {
    OccurrenceNum2 test = new OccurrenceNum2();
    System.out.println(test.numberOf1Between1AndN(234565));
}

}

猜你喜欢

转载自blog.csdn.net/weixin_37672169/article/details/80950378