• 面试题32:从1到n整数中1出现的次数


    这是一道广为流传的google面试题。用最直观的方法求解并不是很难,但遗憾的是效率不是很高;而要得出一个效率较高的算法,需要比较强的分析能力,并不是件很容易的事情。当然,google的面试题中简单的也没有几道。

    硬上:O(nlogn)

     1 int NumberOf1(unsigned int n);
     2 
     3 int NumberOf1Between1AndN_Solution1(unsigned int n)
     4 {
     5     int number = 0;
     6 
     7     for(unsigned int i = 1; i <= n; ++ i)
     8         number += NumberOf1(i);
     9 
    10     return number;
    11 }
    12 
    13 int NumberOf1(unsigned int n)
    14 {
    15     int number = 0;
    16     while(n)
    17     {
    18         if(n % 10 == 1)
    19             number ++;
    20 
    21         n = n / 10;
    22     }
    23 
    24     return number;
    25 }

     方法二:

    我们用一个稍微大一点的数字21345作为例子来分析。我们把从1到21345的所有数字分成两段,即1-1235和1346-21345。

    先来看1346-21345中1出现的次数。1的出现分为两种情况:一种情况是1出现在最高位(万位)。从1到21345的数字中,1出现在10000-19999这10000个数字的万位中,一共出现了10000(104)次;另外一种情况是1出现在除了最高位之外的其他位中。例子中1346-21345,这20000个数字中后面四位中1出现的次数是2000次(2*103,其中2的第一位的数值,103是因为数字的后四位数字其中一位为1,其余的三位数字可以在0到9这10个数字任意选择,由排列组合可以得出总次数是2*103)。

    至于从1到1345的所有数字中1出现的次数,我们就可以用递归地求得了。这也是我们为什么要把1-21345分为1-1235和1346-21345两段的原因。因为把21345的最高位去掉就得到1345,便于我们采用递归的思路。

    分析到这里还有一种特殊情况需要注意:前面我们举例子是最高位是一个比1大的数字,此时最高位1出现的次数104(对五位数而言)。但如果最高位是1呢?比如输入12345,从10000到12345这些数字中,1在万位出现的次数就不是104次,而是2346次了,也就是除去最高位数字之后剩下的数字再加上1。

    int NumberOf1(const char* strN);
    int PowerBase10(unsigned int n);
    
    int NumberOf1Between1AndN_Solution2(int n)
    {
        if(n <= 0)
            return 0;
    
        char strN[50];
        sprintf(strN, "%d", n);
    
        return NumberOf1(strN);
    }
    
    int NumberOf1(const char* strN)
    {
        if(!strN || *strN < '0' || *strN > '9' || *strN == '')
            return 0;
    
        int first = *strN - '0';
        unsigned int length = static_cast<unsigned int>(strlen(strN));
    
        if(length == 1 && first == 0)
            return 0;
    
        if(length == 1 && first > 0)
            return 1;
    
        // 假设strN是"21345"
        // numFirstDigit是数字10000-19999的第一个位中1的数目
        int numFirstDigit = 0;
        if(first > 1)
            numFirstDigit = PowerBase10(length - 1);
        else if(first == 1)
            numFirstDigit = atoi(strN + 1) + 1;
    
        // numOtherDigits是01346-21345除了第一位之外的数位中1的数目
        int numOtherDigits = first * (length - 1) * PowerBase10(length - 2);
        // numRecursive是1-1345中1的数目
        int numRecursive = NumberOf1(strN + 1);
    
        return numFirstDigit + numOtherDigits + numRecursive;
    }
    
    int PowerBase10(unsigned int n)
    {
        int result = 1;
        for(unsigned int i = 0; i < n; ++ i)
            result *= 10;
    
        return result;
    }
  • 相关阅读:
    窗口程序及其反汇编
    PE文件结构及其加载机制(一)
    PE文件结构及其加载机制(三)
    RadAsm配置与第一个程序的编译
    另一个类型的窗口汇编程序及反汇编程序
    发现blogcn真的是做得不错!
    虚拟机学习
    这个blog的定位
    以前做的界面
    用Windows Server 2003搭建安全文件服务器 (转)
  • 原文地址:https://www.cnblogs.com/raichen/p/5662694.html
Copyright © 2020-2023  润新知