Skip to main content

高频面试

算法数据结构算法数据结构About 4 min网站的作者

如何高效寻找素数

素数

如果一个数只能被1和它本身整除,那么这个数就是素数。

实现一个函数,输入一个正整数n,函数返回区间[2,n)中素数的个数。

函数签名如下:int countPrimes(int n)

一般实现

/***
 * @Description: [2, n)素数的个数
 * @Author: Mr.Tong
 */
int countPrimes(int n) {
    int count = 0;
    for (int i = 2; i < n; i++) {
        if (isPrime(i)) {
            count++;
        }
    }
    return count;
}

/***
 * @Description: 判断一个数是不是素数
 * @Author: Mr.Tong
 */
boolean isPrime(int n) {
    for (int i = 2; i < n; i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

分析问题:该算法的时间复杂度为O(n^2),使用isPrime函数一个一个的进行判断不是高效的做法,而且就算是使用isPrime函数,也是存在计算冗余的。

稍加优化

isPrime函数是可以进行优化的,优化代码如下:

/***
 * @Description: 判断一个数是不是素数
 * @Author: Mr.Tong
 */
boolean isPrime(int n) {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}





 






i不需要遍历到n,只需要遍历到sqrt(n)即可,这是因为sqrt(n)是反转临界点,一个数等于两个数相乘,中间的临界点就是sqrt(n)*sqrt(n)[2,sqrt(n)]之间如果找不到可整除的因子,那么[sqrt(n),n]之间也肯定找不到可整除的因子了,因为是后半部分是前半部分的因子交换所得,这就叫做“乘法因子的交换性”。

高效实现

使用“筛数法”进行实现,2是素数,那么在n的范围中,2的倍数就不再是素数了;3是素数,那么在n的范围中,3的倍数就不再是素数了。

/***
 * @Description: 筛数法实现求解素数个数
 * @Author: Mr.Tong
 */
int countPrimes(int n) {
    //各个数的标记
    boolean[] isPrime = new boolean[n];
    //填充都为true
    Arrays.fill(isPrime, true);
    //从头开始,把所有n范围内的素数的倍数都标记为false
    for (int i = 2; i < n; i++) {
        //如果i为素数
        if (isPrime[i]) {
            //实际标记过程
            for (int j = 2 * i; j < n; j += i) {
                isPrime[j] = false;
            }
        }
    }
    //求解true的个数
    int count = 0;
    for (int i = 0; i < isPrime.length; i++) {
        if (isPrime[i]) count++;
    }
    //最后结果去掉两个,因为0和1也都是true
    return count - 2;
}










 



 












上述的过程仍然是可以进行优化的,优化的地方主要有两点:

  • 外层循环:因为乘法因子的对称性,遍历范围可以设置为[2,sqrt(n)]
  • 内层循环:j每次都从2开始存在冗余计算,只需要从i的平方开始标记即可。

重新优化后的代码:

/***
 * @Description: 筛数法实现求解素数个数
 * @Author: Mr.Tong
 */
int countPrimes(int n) {
    //各个数的标记
    boolean[] isPrime = new boolean[n];
    //填充都为true
    Arrays.fill(isPrime, true);
    //从头开始,把所有n范围内的素数的倍数都标记为false
    for (int i = 2; i * i <= n; i++) {
        //如果i为素数
        if (isPrime[i]) {
            //实际标记过程
            for (int j = i * i; j < n; j += i) {
                isPrime[j] = false;
            }
        }
    }
    //求解true的个数
    int count = 0;
    for (int i = 0; i < isPrime.length; i++) {
        if (isPrime[i]) count++;
    }
    //最后结果去掉两个,因为0和1也都是true
    return count - 2;
}









 
 
 
 
 
 
 
 
 
 








这样的算法有一个名字,叫做Sieve of Eratosthenes,该算法的时间复杂度为O(NloglogN)

如何高效进行模幂运算

如何处理数组指数

如何处理mod运算

如何高效求幂

如何运用二分搜索算法

如何高效解决接雨水问题

如何去除有序数组的重复元素

如何寻找最长回文子串

如何运用贪心思想玩跳跃游戏

如何运用贪心算法做时间管理

如何判定括号合法性

如何调度考生的座位

Union-Find算法详解

Union-Find算法应用

一行代码就能解决的算法题

Nim游戏

石子游戏

电灯开关问题