给定一个十进制正整数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