编程之美 2.4 “1”的数目及扩展问题

问题描述:


      给定一个十进制整数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的二进制位数,即 





                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值