算法题解之求数字1的个数及相应的推广

        给定一个十进制正整数n,求1-n的所有整数中出现"1"的个数,比如:n=2,则出现1个1;n=12,则出现5个"1"。接下来通过两种方法进行求解,首先第一种便是暴力求解法,即遍历1-n,并判断每个数的每位上的数字是否为1,进行相加,因此时间复杂度为O(n*logn)。另外一种的思路便是通过依次统计每一位上为1的情况,最终相加,因此该时间复杂度为O(log(n的位数))。可以看出第二种方法大大降低了时间复杂度。

1.暴力求解法

        暴力求解思路比较清晰,通过依次遍历1-n的每个数,依次求每个数中"1"的个数并累加,便是结果,以下是暴力求解法的代码。

package countofone;
import java.util.*;
public class Method1 {
	
	public static int solve(int n)
	{
		int count=0;
		for(int i=1;i<=n;i++)
		{
			int t=i;
			while(t!=0)
			{
				count+=(t%10==1)?1:0;
				t/=10;
			}
		}
		return count;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		while(sc.hasNextInt())
		{
			int n=sc.nextInt();
			System.out.println(solve(n));
		}

	}

}

运行以上代码,并测试,结果如下:

12345
8121
123
57
1
1
5
1
12
5
2.位数规律法

        通过从个位开始到最高位依次假定该位上为1的可能情况,并将每一位的情况进行累加,便是结果。这需要一些数学找规律的功底。比如n=12345,首先个位数为5,大于1,高位为1234,此时1,11,21......12341都符合要求,因此个位为1的情况为1234+1=1235(这里高位是指当前位左边的数字,低位为当前位右边的数字,当前位为假定为1的位置的数字);十位数为4,大于1,低位为5,高位为123,此时10-19,110-119......12310-12319,都是符合要求的,所以十位数为1的个数有(123+1)*10=1240;百位数为3,大于1,低位为45,高位为12,此时100-199,1100-1199......12100-12199都符合要求,因此百位数为1的情况为(12+1)*100=1300;千位上为2,同理得到(1+1)*1000=2000;万位上为1,此时高位没有,低位为2345,此时10000-12345都符合要求,因此为2346。因此12345包含1的个数为8121,根据方法1的测例可知完全正确。由上可知,当当前位大于1时,只需要考虑高位,如果当前位等于1,则不仅需要考虑高位,还需要考虑低位。那么推理可得当前位小于1的情况呢?举例n=130,个位为0,小于1,当个位为1,则1,11......121,总数为13*1=13;十位为3,大于1,高位为1,低位为0,(1+1)*10+0=20;百位为1,等于1,高位为0,低位为30,所以0*100+30+1=31.总数为64。所以小于1的区别与大于1的区别在于高位加不加1的区别,都不需要考虑低位。只有当当前位等于1,才需要考虑低位。代码如下:

package countofone;

import java.util.Scanner;

public class Method2 {

	public static int solve(int n) {
		int count=0;
		int factor=1;
		int lower=0;
		int higher=0;
		int curr=0;
		while(n/factor!=0)
		{
			lower=n%factor;
			curr=(n/factor)%10;
			higher=n/(factor*10);
			if(curr==0)
				count+=higher*factor;
			else if(curr==1)
				count+=higher*factor+lower+1;
			else
				count+=(higher+1)*factor;
			
			factor*=10;
		}
		return count;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		while(sc.hasNextInt())
		{
			int n=sc.nextInt();
			System.out.println(solve(n));
		}

	}

}

测试结果如下:

12345
8121
64
17
130
64
226
153
3.推广

        假设求x的个数呢,比如0-9中任意一个数字。修改代码如下,只需要将1的位置设置为一般性的x即可。

package countofone;
import java.util.*;
public class GeneralMethod {
	public static int solve(int n,int x) {
		int count=0;
		int factor=1;
		int lower=0;
		int higher=0;
		int curr=0;
		while(n/factor!=0)
		{
			lower=n%factor;
			curr=(n/factor)%10;
			higher=n/(factor*10);
			if(curr<x)
				count+=higher*factor;
			else if(curr==x)
				count+=higher*factor+lower+1;
			else
				count+=(higher+1)*factor;
			
			factor*=10;
		}
		return count;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int x=sc.nextInt();
		while(sc.hasNextInt())
		{
			int n=sc.nextInt();
			System.out.println(solve(n,x));
		}

	}

}

测试结果如下:

2
128
32
10
1
12345
5121

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值