问题描述:
给定一个十进制整数N,求出从1到N的所有整数中出现”1”的个数。
例如:N=2,1,2出现了1个“1”。
N=12,1,2,3,4,5,6,7,8,9,10,11,12。出现了5个“1”。
解题思路:
1.计算从1到N经历的每个数中1的个数相加
其中数字i(1=<i<=N)中1的数目:分解出i的个位、十......最高位,分别判别是否等于1,等于则总数目加1
#include <iostream>
using namespace std;
typedef unsigned long long int mm;
mm numberof1(mm N){
mm num=0;
for(mm i=1;i<=N;i++){
mm m=0,j=i;
while(i!=0){
m=i%10;
if(m==1)
num++;
i=i/10;
}
i=j;
}
return num;
}
此程序时间复杂度:
编程之美书上写的是: 。
此程序很简单,需注意:求i各个位上数的过程中会破坏i,故要在求之前保存好i,在判别完后恢复i。
2.使用列举法寻找规律,进行数学归纳,用代码表示
归纳过程不再赘述,编程之美书上有,这里更正书里的一个错误:135页倒数第二段最后一句,114应改为14,123+1改为13+1。
归纳规律为:十进制表示第i为出现1的次数与第i位上的数字有关,设第i位上的数为p,i位以上的数字为高位数字,以下的数字为低位数字,如1700030,对于第三位来说,高位数字为1700,低位数字为30,则p满足以下规则:
p=0 高位数字*(10^(i-1))
p=1 高位数字*(10^(i-1))+低位数字+1
p=2 (高位数字+1)*(10^(i-1))
程序如下:
mm quicknumof1(mm N){
mm low=0,cur=0,high=0,num=0,factor=1;
while((N/factor)!=0){
low=N-(N/factor)*factor;
cur=(N/factor)%10;
high=(N/factor)/10;
switch(cur){
case 0:
num+=high*factor;
break;
case 1:
num+=high*factor+(low+1);
break;
default:
num+=(high+1)*factor;
break;
}
factor*=10;
}
return num;
}
程序中注意:
1.要准确的求取低位数字、当前位数字、高位数字;
2.每个case之后要break,降低时间复杂度
3.记得factor要累乘,从而target不同位
此程序的时间复杂度为N的十进制位数,即
改进:
对于上述分类归纳,可以进一步归纳,可以看出,无论p为几,该位“1”的个数均包括高位数字*(10^(i-1)),P不同时附加的值不同。P为0或1,附加的值是(低位数字+1)*p;p为其他,附加的值是10^(i-1)。
在程序中,只需要定义变量addnum,利用条件表达式,addnum=p>1?10^(i-1):(低位数字+1)*p;从而避免了繁琐的switch语句。
扩展问题:
给定一个十进制整数N,求出从1到N的所有整数二进制表达中出现”1”的个数。
对于这个问题,我们同样枚举二进制表达每一位上出现“1”的次数,总结归纳,可得:
第i位上“1”出现的次数=高位*(2^(i-1))+第i位数字+低位数字
程序为:
mm quicknumofbinary1(int N){
int lowdigit=0,curdigit=0,highdigit=0,num=0,factor=1;
while((N/factor)!=0){
lowdigit=N-(N/factor)*factor;
curdigit=(N/factor)%2;
highdigit=(N/factor)/2;
num+=highdigit*factor+curdigit+lowdigit;
factor*=2;
}
return num;
}
此程序的时间复杂度为N的二进制位数,即 。