java算法计算1到N个数里面一到底有多少个,统计1出现的总次数。首先是我做的,从1到n,对每个数字都数一下里面有多少个1,累加起来得到最终的结果。好大一个循环啊,循环里面还要再套循环,感觉挺垃圾的一个程序:
// 第0个元素表示当前数字的长度,第1个元素表示个位,第2个表示十位……
int[] num = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int n = 12345;
int count = 0;
for (int i = 1; i <= n; i++) {
// 统计当前数字的1的个数
for (int k = 1; k <= num[0]; k++) {
if (num[k] == 1)
count++;
}
// 数字增加并进位
for (int k = 1; k <= num[0] + 1; k++) {
if (++num[k] == 10) {
num[k] = 0;
} else {
break;
}
}
// 更新数字的长度
num[0] = num[num[0] + 1] > 0 ? num[0] + 1 : num[0];
}
System.out.println(count);
接下来高手出现了,通过字符串的处理来解决问题,思路非常新颖啊,代码很简短,看上去很美:
// 在1到N中求出num的个数
private static int count(int N, int num) {
if (N <= 0 || num < 0 || num > 9)
return 0;
StringBuffer sb = new StringBuffer();
for (int i = 1; i < N + 1; i++)
sb.append(String.valueOf(i));
int totalCount = sb.toString().length();
String temp = sb.toString().replaceAll(String.valueOf(num), “”);
return totalCount – temp.length();
}
对这个程序提出了一个小小的疑问,那就是字符串太长了,整数是4字节的,最大的整数有2147483647这么大,输入进去很有可能造成字符串长度不够啊,就算够了也十分耗费内存。我只是想到了这个问题,但是没想到如何解决,另一个高手解决了它:
int len = 0;
for(int i=1;i<=n;i++)
len += (i+”").replaceAll(“[^1]“, “”).length();
System.out.println(len);
以上各种方法都是对从1到n的每一个数进行解析,如果n很大,则循环次数那是相当多啊,效率比较低下。论坛里的各位朋友讨论的比较热烈,一致认为用数学归纳法总结出一套规律,然后根据规律来编程效率会大幅提高。受此影响,我又对问题进行了分析,还真让我找到了规律,HOHO~好开心啊,代码真的很漂亮,思路也清晰。先简单介绍一下算法,以21047这个数字为例:
首先最高位会出现多少次1呢? 10000~19999共10000个1
再看次高位是个1:1000~1999,11000~11999,21000~21047,共有2*1000+48个1
再看第三位是个0:100~199,1100~1199,……20100~20199,共有21*100个1
再看第四位是个4:10~19,110~119,210~219……21010~21019,共有210*10+10个1
最后一位是个7,共有2104*1+1个1
根据上述分析,总结第n位(从左向右数)出现1的规律如下:
第0~n-1位组成的数字乘以跨度(如上述例子,最高位跨度10000,次高位跨度1000,也是很有规律的),然后再根据当前位是大于1?等于1?小于1?来加上一个可变的数值。具体点就是,若当前位大于1,则加上跨度;若当前位等于1,则加上尾数;若当前位等于0,则加0;
代码如下:
private static int count1(int n) {
int count = 0;
String num = n+”";
int x = (int)Math.pow(10, num.length()-1);
for(int i=0; i
count += Integer.parseInt(“0″+num.substring(0, i))*x;
if(num.charAt(i)>’1′) {
count += x;
}else if(num.charAt(i)==’1′) {
count += Integer.parseInt(“0″+num.substring(i+1))+1;
}
x /= 10;
}
return count;
}