题目:

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

思路:

1、累加法

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

求每个整数1出现的个数:通过对10求余数,判断整数的个位是否为1,如果商不为0,则继续除以10再判断个位数字是否为1.

时间复杂度:O(nlogn)

2、递归

以21345为例,把1到21345的所有数字分为两段,1-1345,1346-21345。

先看1346-21345,1的出现分为两种情况,1出现在最高位,1出现在其他位。

考虑1出现在最高位:从1346到21345的数字中,1出现在10000-19999这10000个数字的万位中,一共出现10000次(最高位大于1的情况下),当最高位为1时,出现1的次数为除去最高位数字后的数字再加1,如1346-11345,最高位出现1的次数为1345+1=1346次。

考虑1出现在其他位:由于最高位是2,因此1346-21345可以分为两段,1346-11345,11346-21345,每一段剩下的4位中,每一位都可以选择为1,共有4种,而其他三位在0-9之间任意选择,因此根据排列组合原则,总共出现的次数是2*4*10^3=6000.

至于1-1345中1出现的次数,通过上述方法递归得到。这也是为什么要分成1-1345和1346-21345两段的原因,因为把21345的最高位去掉就编成1345,便于采用递归的思路。

总结一下以上的分析结果:

1、1出现在最高位:当最高位为1时,次数等于除去最高位后剩下的数字加1,当最高位大于1时,次数等于10的(位数-1)次方;

2、1出现在其他位:次数等于最高位数字*(总位数-1)*10的(剩下位数-1)次方

时间复杂度:

这种思路每次去掉最高位做递归,递归的次数和位数相同。一个数字有O(logn)位,因此时间复杂度为O(logn).

代码:

1、累加法

#include <iostream>

using namespace std;

int numberOf1(int n){
int count=0;
while(n){
if(n%10==1)
count++;
n=n/10;
}
return count;
} int numberOf1Between1AndN(unsigned int n){
int count=0;
for(unsigned int i=1;i<=n;i++){
count+=numberOf1(i);
}
return count;
} int main()
{
cout << numberOf1Between1AndN(12) << endl;
return 0;
}

2、递归

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h> using namespace std; int PowerBase10(unsigned int n){
int result=1;
for(unsigned int i=1;i<=n;i++)
result*=10;
return result;
} int numberOf1_Recursive(const char* strN){
if(strN==NULL || *strN<'0' || *strN>'9' || *strN=='\0')
return 0;
int first=strN[0]-'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 numOtherDigit=first*(length-1)*PowerBase10(length-2);
int numRecursive=numberOf1_Recursive(strN+1); return numFirstDigit+numOtherDigit+numRecursive;
} int numberOf1Between1AndN_Recursive(unsigned int n){
if(n<=0)
return 0;
char strN[50];
sprintf(strN,"%d",n);
return numberOf1_Recursive(strN);
} int main()
{
cout << numberOf1Between1AndN_Recursive(12) << endl;
return 0;
}

在线测试OJ:

http://www.nowcoder.com/books/coding-interviews/bd7f978302044eee894445e244c7eee6?rp=2

AC代码:

累加法:

class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
int count=0;
for(int i=1;i<=n;i++)
count+=numberOf1(i);
return count;
} int numberOf1(int n){
int count=0;
while(n){
if(n%10==1)
count++;
n=n/10;
}
return count;
}
};

递归:

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==NULL || *strN<'0' || *strN>'9' || *strN=='\0')
return 0;
int first=strN[0]-'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 numOtherDigit=first*(length-1)*PowerBase10(length-2);
int numRecursive=numberOf1(strN+1); return numFirstDigit+numOtherDigit+numRecursive;
} int PowerBase10(int n){
int result=1;
for(int i=0;i<n;i++)
result*=10;
return result;
}
};

(剑指Offer)面试题32:从1到n整数中1出现的次数的更多相关文章

  1. 剑指Offer:面试题32——从1到n整数中1出现的次数(java实现)

    问题描述: 输入一个整数n,求1到n这n个整数的十进制表示中1出现的次数.例如输入12,从1到12这些整数中包含1的数字有1,10,11,12,1一共出现了5次. 思路:(不考虑时间效率的解法,肯定不 ...

  2. 【剑指Offer面试编程题】题目1373:整数中1出现的次数--九度OJ

    题目描述: 亲们!!我们的外国友人YZ这几天总是睡不好,初中奥数里有一个题目一直困扰着他,特此他向JOBDU发来求助信,希望亲们能帮帮他.问题是:求出1~13的整数中1出现的次数,并算出100~130 ...

  3. 剑指Offer的学习笔记(C#篇)-- 整数中1出现的次数(从1到n整数中1出现的次数)

    题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...

  4. 【剑指Offer】31、从1到n整数中1出现的次数

      题目描述:   求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他 ...

  5. 【剑指Offer面试题】 九度OJ1517:链表中倒数第k个结点

    鲁棒性是指程序可以推断输入是否符合规范要求,并对不和要求的输入予以 合理的处理. 题目链接地址: http://ac.jobdu.com/problem.php?pid=1517 题目1517:链表中 ...

  6. 剑指offer——面试题32.1:分行从上到下打印二叉树

    void BFSLayer(BinaryTreeNode* pRoot) { if(pRoot==nullptr) return; queue<BinaryTreeNode*> pNode ...

  7. 剑指offer——面试题32:从上到下打印二叉树

    void BFS(BinaryTreeNode* pRoot) { if(pRoot==nullptr) { cout<<"empty binary tree!"< ...

  8. 【剑指offer 面试题38】数字在排序数组中出现的次数

    思路: 利用二分查找,分别查找待统计数字的头和尾的下标,最后做差加一即为结果. C++: #include <iostream> #include <vector> using ...

  9. 剑指offer——面试题15.2:判断两个整数m和n的二进制中相差多少位

    #include"iostream" using namespace std; int CountDifferentBit(int m,int n) { ,diff=m^n; wh ...

  10. 面试题32.从1到n整数中1出现的次数

    题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.例如输入12,从 1到12这些整数中包含1的数字中1,10,11和12,1一共出现了5次 本题可以直接变量1到n的n个数然后分别计 ...

随机推荐

  1. AVL树的旋转实现

    AVL树:带有平衡条件的二叉查找树,即一棵AVL树是其每个节点的左子树和右子树的高度最多相差1的二叉查找树.一般通过Single Rotate和Double Rotate来保持AVL树的平衡.AVL树 ...

  2. Excel学习笔记杂荟

    Excel学习 一.工具->选项 可以对整个excel里面的东西进行编辑,里面有隐藏行号,下拉档等选项,有文档作者信息. 隐藏网格等 二.单元格内容比较大可以右击单元格->设置单元格格式- ...

  3. 安卓 Pickers(选择器)

    概述 安卓提供了现成的对话框,让用户选择一个时间或日期.每一个选择器控制时间(小时,分钟,AM/PM)或日期(月,日,年)的每一部分的选择.使用这些选择器帮助 确保用户正确的,格式化的,和适合的选择一 ...

  4. Jquery Mobile设计Android通讯录第二章

    本文是jQuery Mobile设计Android通讯录系统教程的第二篇,在上一篇教程中(http://publish.itpub.net/a2011/0517/1191/000001191561.s ...

  5. PHP中最容易忘记的一些知识点总结

    1.require 和require_once 区别: 前者遇到即包含文件,后者会判断是否已经包含过了,如果包含过了,则不再包含文件.一可以节省资源,二可以避免重复定义的错误. 2.include 和 ...

  6. android中的style部分属性值介绍

    转自:http://blog.sina.com.cn/s/blog_70c759fd01013phv.html Android平台定义的主题样式: android:theme="@andro ...

  7. 将错误日志记录在txt文本里

    引言 对于已经部署的系统一旦出错对于我们开发人员来说是比较痛苦的事情,因为我们不能跟踪到错误信息,不能 很快的定位到我们的错误位置在哪,这时候如果能像开发环境一样记录一些堆栈信息就可以了,这时候我们就 ...

  8. hdu 3951(博弈规律)

    题意:给定围成一个圈的硬币n枚,然后每次可以取出连续的1-k枚,谁取完最后一枚谁就获胜. 分析:对于第二个人当第一个人取完后,他可以取成对称的形式,所以第二个人必胜. 代码: #include< ...

  9. CAT XQX ---- 增删改查架构说明 1

    View 层  --  以国家为例 1. 显示 数据库的 table 页面效果 对应代码: <table id="dg" title="国家信息" cla ...

  10. Page 63-64 Exercises 2.3.7 -------Introduction to Software Testing (Paul Ammann and Jeff Offutt)

    Use the following method printPrimes() for question a-d below //Find and prints n prime integers pri ...