在leetcode中,不乏一些需要寻找某整型数n的所有因数的题目。
若用取余的方法从1到n去遍历查找,时间复杂度是O(n),在面对一些比较大的数字时,会花费非常多的时间。
比如寻找n=100的全部因数,使用以下的代码:
public List<Integer> findFactors(int n){
List<Integer> factors = new ArrayList<>();
for(int i=1;i<=n;i++){
if(n%i == 0){
factors.add(i);
}
}
return factors;
}
此时循环体中的内容执行100次。
而从数学的角度来思考,我们可以以sqrt(n)为界限进行分割。
显然,sqrt(n)左侧的每一个因数a,必然存在一个sqrt(n)右侧的一个因数b与之对应,使得b = n/a。这时我们仅仅遍历到1~10,就足以确定100的所有因数:
a | b |
---|---|
1 | 100 |
2 | 50 |
4 | 25 |
5 | 20 |
10 | 10 |
基于以上思想,可以将代码进行修改:
public static List<Integer> findFactors(int n){
List<Integer> factors = new ArrayList<>();
for(int i=1;i*i<=n;i++){
if(n%i == 0){
if(i*i == n){
factors.add(i);//防止添加两个重复的因数
break;
}
factors.add(i);
factors.add(n/i);
}
}
return factors;
}
此时循环体中的内容执行10次,时间复杂度降为O(√2)。
在做leetcode第507题完美数时,这一个小小的改变,使得通过全部测试用例所需要的时间从1700ms降低到了2ms,对性能的影响可见一斑。