160. 相交链表
链接:https://leetcode-cn.com/problems/count-primes/
题目描述见链接内容。
解法1:暴力法
二话不说,用暴力法解决这个问题,遍历n
,对所有计数调用isPrime
方法,isPrime
中要注意的一个点是,结束循环的条件是x * x < n
,不是n / 2
,遍历到开方之前就可以完成所有的判断了
var countPrimes = function (n) {
let count = 0;
for (let i = 2; i < n; i++) {
if (isPrime(i)) {
count++;
}
}
return count;
};
function isPrime(n) {
let x = 2;
while (x * x <= n) {
if (n % x === 0) {
return false;
}
x++;
}
return true;
}
在计算到5000000
用例时超时了
解法2:埃氏筛
这个算法由希腊数学家埃拉多塞提出,成为埃拉多塞筛法,简称埃氏筛
如果x
是质数,那么x
的倍数2x
/3x
…一定不是质数,所以声明一个数组isPrimes
来表示i
是不是质数,0
代表不是,1
代表是。从小到大遍历x
,如果它是合数,那么它一定是某个小于x
的质数的y
整数倍。
也就是说,在遍历到y
时,就会将上面提到的x
标记为isPrimes[x] = 0
。
对于一个质数x
,其实不必从2x
开始标记,应该直接从x * x
开始标记,因为2x
、3x
这些数一定在x
之前被其他的数字标记过了,例如标记2
的倍数、3
的倍数时
var countPrimes = function (n) {
let result = 0;
// 初始化,1 代表是质数,0代表不是
const isPrimes = new Array(n).fill(1);
for (let i = 2; i < n; i++) {
if (isPrimes[i]) {
result += 1;
// 从 i * i 开始标记,因为2i、3i...一定在 i * i 之前被标记过了,递增策略是 j += i, 这样相当于按照 i 的倍数增加进行标记
for (let j = i * i; j < n; j += i) {
isPrimes[j] = 0;
}
}
}
return result;
};
- 时间复杂度:
${O(N Log(Log(N)))}$
- 空间复杂度:
${O(N)}$
- 执行用时:232ms, 在所有JavaScript提交中击败了71%的用户,内存消耗:77,2MB,在所有JavaScript提交中击败了46%的用户
解法3:线性筛
相对于埃氏筛,多增加了一个primes
数组来存放当前得到的质数集合,从小到达进行遍历,如果当前的数字x
是质数,就加入primes
数组
另一点与埃氏筛不同的是,标记过程不再当x
是质数时进行,而对每个x
都进行,不再标记x
的倍数1x
、2x
…,而是标记质数集合primes
中每个质数与x
的乘积,并且在x % primes[i]
时结束标记
关键点是:因为如果x
可以被primes[i]
整除,那么对于下一个要标记的合数x * primes[i + 1]
,一定会在(x / primes[i]) * primise[i + 1]
时被标记。这样就保证了每个合数只会被其最小的质因数筛去,只被标记一次
在对headA
的遍历过程中,可以把headA
的每一个节点存到一个Set中,然后在遍历headB
时,只需要判断当前在节点在之前的Set中是否存在即可
var countPrimes = function (n) {
// 初始化,1 代表是质数,0代表不是
const isPrimes = new Array(n).fill(1);
const primes = [];
for (let i = 2; i < n; i++) {
if (isPrimes[i]) {
primes.push(i);
}
// 边界条件有两个
for (let j = 0; j < primes.length && i * primes[j] < n; j++) {
isPrimes[i * primes[j]] = 0;
// 如果 i 可以被 primes[j] 整除,就停止标记,因为下一个 primes[j + 1] 会在 (i / primes[i]) * primes[j + 1] 时被标记
if (i % primes[j] === 0) {
break;
}
}
}
return primes.length;
};
- 时间复杂度:
${O(N)}$
- 空间复杂度:
${O(N)}$
- 执行用时:224ms, 在所有JavaScript提交中击败了74%的用户,内存消耗:100.6MB,在所有JavaScript提交中击败了12%的用户