前言:算法小白,无任何编程项目经历,最近入坑leetcode,分享下自己刷题的经验与感悟。我自认为编程能力中等偏下,刚开始刷中等以上题基本上不会,有时候想一个小时也没有好思路,可能自身资质不行。这里就先列举一道我认为比较经典的数学题目,我尽量用最通俗易懂的语言解释。(持续更新)
1~n整数中1出现的次数
题目介绍:输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
输入限制:1 <= n < 231
问题分析
一般 leetcode 上题对时间复杂度要求较高,一般10^5 左右大小的数组遍历,就不能用O(N2) 来写,否则会超时。一般 109 左右大小,基本上O(N) 也 gg。这个题显然是个数学问题,我们得想办法借用公式来判断,然后让其复杂度为O(logN)。
我刚开始分析也没啥头绪,随便找了几个数试了下,然后发现了点规律,PS:这种题一定多动手。
比如27,1-9有1个1,10-19的十位数有10个1,个位数有一个1,20-27有1个1,总个数就是13。
我们再抽象一下,比如78,相当于1-9是一部分,10-19 是一部分,值固定为10+1,然后20-69是一部分,值相当于5倍的0-9,70-78是一部分,值相当于1-8中的1。
这个时候,我发现,如果10-19,比如说17,就不能用这个拆分方式了,值固定不了。我用117举例,1-99为一部分,然后117-100 = 17,100-117的百位有17+1个1,十个位00-17与1-17的1相同,一加起来就48个。
思路形成
这么分析一波,貌似这题就很easy了,有手就行。上述过程大家看懂的话,那接下来就轻松愉快了,整理一下,分2种情况(首位1开头,非1开头),2个公式。
1开头:把n的位数搞出来,比如k,那么先求 10k -1中1的个数,加上最高位的1:n - 10k + 1,加上剩余位的1:n - 10k 中1的个数。
非1开头:先求 10k -1中1的个数,然后加上10k ,加上(n % 10k -1) 倍的 n - 10k 中的1的个数,最后加上剩余的n % 10k 中的1的个数。
萌新代码(供参考)
java版本,但是基本任何语言都可以看懂。
public int countDigitOne(int n) {
return fun(n);
}
public int fun(int x){
if(x == 0)
return 0;
if(x > 0 && x <= 9)
return 1;
String s = String.valueOf(x);
int p = (int)Math.pow(10, s.length()-1);
int h = s.charAt(0) - '0';
int l = x - p*h;
if(h == 1)
return fun(p-1) + l+1 + fun(l);
else
return fun(l) + h * fun(p-1) + p;
}
总结
感谢各位花时间看我的文章,其实我刷题基本不看题解的,因为我觉得,想不出来思路,即使把别人思路背下来,过段时间几瓶酒下去就会忘光,还是白扯。所以刷算法题要多思考,实在不会了,看别人题解,想想为什么要这样,为什么你想不想出来,弥补自己的不足。希望各位看过我文章的朋友都能快速上算法车。