1~N中求出1的个数

       今天在论坛看到了这个题目,挺有意思的,并且算法也很多样啊,下面就来介绍一下。
        首先是我做的,中规中矩,从1到n,对每个数字都数一下里面有多少个1,累加起来得到最终的结果。好大一个循环啊,循环里面还要再套循环,感觉挺垃圾的一个程序:
        // 第0个元素表示当前数字的长度,第1个元素表示个位,第2个表示十位……
        int[] num = 11000000000 };
        
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<num.length(); 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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值