《剑指offer》第四十三题:从1到n整数中1出现的次数

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

#include <cstdio>
#include <cstring>
#include <cstdlib>

// ====================方法一====================
// 笨方法, 时间复杂度O(nlogn)
int NumberOf1(unsigned int n);

int NumberOf1Between1AndN_Solution1(unsigned int n)
{
    int number = 0;

    for (unsigned int i = 1; i <= n; ++i)
        number += NumberOf1(i);

    return number;
}

int NumberOf1(unsigned int n)
{
    int number = 0;
    while (n > 0)
    {
        if (n % 10 == 1)
            ++number;

        n = n / 10;
    }

    return number;
}

// ====================方法二====================
int NumberOf1(const char* strN);
int PowerBase10(unsigned int n);

int NumberOf1Between1AndN_Solution2(int n)
{
    if (n <= 0)
        return 0;

    char strN[50];
    sprintf_s(strN, "%d", n);

    return NumberOf1(strN);
}

int NumberOf1(const char* strN)
{
    if (!strN || *strN < ‘0‘ || *strN > ‘9‘ || *strN == ‘\0‘)
        return 0;

    int first = *strN - ‘0‘;  //输入数字首位, 字符串转数字
    unsigned int length = static_cast<unsigned int>(strlen(strN));

    //边界值
    if (length == 1 && first == 0)  //输入为 0
        return 0;
    if (length == 1 && first > 0)  //输入为 0~9
        return 1;

    // 假设strN是 21345
    //数字10000~19999中首位为1的数目
    int numFirstDigit = 0;
    if (first > 1)
        numFirstDigit = PowerBase10(length - 1);
    //首位数字为1, 如11345, 此时10000~11345
    else if (first == 1)
        numFirstDigit = atoi(strN + 1) + 1; //则1345 + 1为首位为1的数目. atoi转换字符串为数字

    // numOtherDigits是01346-21345除了第一位之外的数位中1的数目
    //此时(length - 1)表示四个位置, (length - 2)表示其余三位可能的取值0~9
    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)  //求10^n次方
{
    int result = 1;
    for (unsigned int i = 0; i < n; ++i)
        result *= 10;

    return result;
}

// ====================测试代码====================
void Test(const char* testName, int n, int expected)
{
    if (testName != nullptr)
        printf("%s begins: \n", testName);

    if (NumberOf1Between1AndN_Solution1(n) == expected)
        printf("Solution1 passed.\n");
    else
        printf("Solution1 failed.\n");

    if (NumberOf1Between1AndN_Solution2(n) == expected)
        printf("Solution2 passed.\n");
    else
        printf("Solution2 failed.\n");

    printf("\n");
}

void Test()
{
    Test("Test1", 1, 1);
    Test("Test2", 5, 1);
    Test("Test3", 10, 2);
    Test("Test4", 55, 16);
    Test("Test5", 99, 20);
    Test("Test6", 10000, 4001);
    Test("Test7", 21345, 18821);
    Test("Test8", 0, 0);
}

int main(int argc, char* argv[])
{
    Test();

    return 0;
}

测试代码

分析:第二种方法需要仔细分析规律。

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if (n <= 0)
            return 0;

        int number = 0;
        for (int i = 1; i <= n; ++i)
        {
            number += NumberOf1(i);
        }
        return number;
    }
    int NumberOf1(int n)
    {
        int number = 0;
        while(n)
        {
            if (n % 10 == 1)
                ++number;
            n = n / 10;
        }
        return number;
    }
};

牛客网-方法一

class Solution {
public:
    int NumberOf1Between1AndN_Solution(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 == ‘\0‘)
            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;

        int numFirstDigit = 0;
        if (first > 1)
            numFirstDigit = PowerBase10(length - 1);
        else if (first == 1)
            numFirstDigit = atoi(strN + 1) + 1;

        int numOtherDigir = first * (length - 1) * PowerBase10(length - 2);
        int numRecursive = NumberOf1(strN + 1);

        return numFirstDigit + numOtherDigir + numRecursive;
    }
    int PowerBase10(unsigned int n)
    {
        int result = 1;
        for (unsigned int i = 0; i < n; ++i)
            result *= 10;

        return result;
    }
};

牛客网-方法二

原文地址:https://www.cnblogs.com/ZSY-blog/p/12632996.html

时间: 2024-10-11 17:21:56

《剑指offer》第四十三题:从1到n整数中1出现的次数的相关文章

【剑指Offer学习】【面试题:二维数组中的查找】PHP实现

最近一直看剑指Offer.里面很多算法题.于是就想着用PHP来显示一下. 题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序. 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 拿到这个题目.我们第一个反应,就是遍历二维数组.然后逐个进行比较.我们不难用PHP进行实现.于是有了下面的代码 1 function TwoArrayFind($array,$search) { 2 $found = false; 3 4 if(e

一道算法题-从1到n整数中1出现的次数

1. 题目描述 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,1一共出现了5次. 2. 题目来源 第一次看到是在<剑指Offer>第2版上,面试题32.leetcode和牛客网上都有这道题. 3. 本文的目的 看了<剑指Offer>上的解法,我觉得不能算好: 这段解释描述有些不清晰,而且没有图,难以理解. 从书中给出的实现上来看,显得有些凌乱. 在这篇博客里,会给出一个我对这道题的解法,包括完整

剑指offer第四题方法总结

题目:输入一个字符串,要求将这个字符串中所有空格的位置都替换成"%20".例:输入I love you. 输出I%20love%20you. 分析:空格是一个字符,%20是三个字符,所以替换的时候要向后挪动. 这里有三种方法. 第一种:从前向后遍历字符串,遇到一个空格,则将这个空格之后的字符'\0'开始依次向后挪动两个字符,然后再放入%20,直到遍历完整个字符串.这种方法效率比较低. 第二种:重新创建一个字符数组,将这个字符串拷贝过来,遇到空格就替换成%20. 这种方法虽然效率高,但是

剑指offer第四天

25.复杂链表的复制 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) /* public class RandomListNode { int label; RandomListNode next = null; RandomListNode random = null; RandomListNode(int label) { t

《剑指offer》第五题(重要!从尾到头打印链表)

文件main.cpp // 从尾到头打印链表 // 题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值. #include <iostream> #include <stack> #include "List.h" using namespace std; void PrintListReversingly_Iteratively(ListNode* pHead)//解法一:使用栈 { stack<ListNode*> nodes;//定义

剑指offer第十八题 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10. 解题思路:一次去掉一个外圈,当最后只剩一行或一列时停止递归.(ps:这题就是绕,仔细点就解决了!!!) 1 import java.util.ArrayList; 2 public class Solution { 3 publ

《剑指offer》第十七题:打印1到最大的n位数

// 面试题17:打印1到最大的n位数 // 题目:输入数字n,按顺序打印出从1最大的n位十进制数.比如输入3,则 // 打印出1.2.3一直到最大的3位数即999. #include <cstdio> #include <memory> void PrintNumber(char* number); bool Increment(char* number); void Print1ToMaxOfNDigitsRecursively(char* number, int length

《剑指offer》之7-9题

7.斐波那契数列 问题描述 都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项. 实现思想 了解斐波那契数列的规律就Ok了.1,1,2,3,5,8,... 代码 function Fibonacci(n) { // write code here if(n==0||n==1){ return n; } var N1=1,N2=0; for(let i=2;i<=n;i++){ N1=N1+N2; N2=N1-N2; } return N1; } 8.跳台阶 问题描述 一只青

【剑指offer】 第三题 二维数组查找

package javaTrain; public class offer3 { public static void main(String args[]) { int[][] a = {{0,1,2,3},{1,2,3,4},{2,3,4,5},{6,7,8,9}}; System.out.println(find(a,10)); } public static boolean find(int[][] a,int num) { if(a == null) return false; int

剑指offer十四之链表中倒数第k个结点

一.题目 输入一个链表,输出该链表中倒数第k个结点. 二.思路 两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指正走(k-1)步,到达第k个节点.然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了. 三.代码 /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class So