news 2026/10/1 21:58:27

【LeetCode 204. 计数质数】从暴力枚举到埃拉托斯特尼筛法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【LeetCode 204. 计数质数】从暴力枚举到埃拉托斯特尼筛法
如果你也是因为超时问题而来,请跳转至【LeetCode 204. 计数质数】从暴力枚举到打表预处理

题目描述

给定整数 n ,返回所有小于非负整数 n 的质数的数量。

示例 1 输入:n = 10 输出:4 解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。 示例 2: 输入:n = 0 输出:0 示例 3: 输入:n = 1 输出:0 提示: 0 <= n <= 5 * 10^6

解题思路演进

这道题是经典的数论基础题。根据数据范围 n <= 5 * 10^6,我们可以推导出不同算法的时间复杂度表现。

方法一:暴力枚举(会超时 TLE)

最直观的想法是:遍历从 2 到 n-1 的每一个数字 i,然后判断 i 是否为质数。判断质数的方法是尝试用 2 到 sqrt(i) 之间的数字去整除 i。

代码实现:

class Solution { public: bool isPrime(int x) { for (int i = 2; i * i <= x; i++) { if (x % i == 0) return false; } return true; } int countPrimes(int n) { int ans = 0; for (int i = 2; i < n; i++) { if (isPrime(i)) ans++; } return ans; } };

复杂度分析:

  • 时间复杂度:O(N根号N​)。当N=5×106时,计算量达到十亿级别,在 LeetCode 上必定超时。
  • 空间复杂度:O1。

方法二:埃拉托斯特尼筛法(Sieve of Eratosthenes)

既然暴力法会超时,我们需要一种更高效的算法。埃拉托斯特尼筛法(简称埃氏筛)是一种古老且经典的质数筛选算法。

核心思想:
如果 x 是质数,那么 x 的倍数(2x, 3x, 4x...)一定不是质数。我们可以从 2 开始遍历,将当前数字的倍数全部标记为“合数”。遍历结束后,未被标记的数字就是质数。

在实现埃氏筛时,有一个极其重要的优化细节:内层循环从 i * i 开始,而不是 2 * i。

for (int j = i * i; j < n; j += i) { isPrime[j] = false; }

为什么可以从 i * i 开始?
假设当前遍历到的质数是 i。对于 i 的倍数 i * k:

  • 如果 k < i,那么 i * k 必然已经被比 i 更小的质数(比如 k 的某个质因数)筛选过了。
  • 例如:当 i = 5 时,5 * 2 = 10(已被 2 筛掉),5 * 3 = 15(已被 3 筛掉),5 * 4 = 20(已被 2 筛掉)。
  • 因此,为了避免重复标记(重复计算),我们从 i * i 开始标记即可,这是 i 的倍数中第一个尚未被更小质数标记的数字。

代码实现 (C++)

class Solution { public: int countPrimes(int n) { // 边界条件:小于等于 2 的数没有质数 if (n <= 2) return 0; // 创建布尔数组,isPrime[i] 表示数字 i 是否为质数 // 初始默认全部为 true (质数) vector<bool> isPrime(n, true); // 0 和 1 不是质数 isPrime[0] = false; isPrime[1] = false; // 从 2 开始筛,只需要遍历到 sqrt(n) 即可 for (int i = 2; i * i < n; i++) { if (isPrime[i]) { // 优化:从 i * i 开始标记,步长为 i for (int j = i * i; j < n; j += i) { isPrime[j] = false; } } } // 统计所有标记为 true 的数字 int count = 0; for (int i = 2; i < n; i++) { if (isPrime[i]) count++; } return count; } };

复杂度分析:

  • 时间复杂度:ONloglogN。这是埃氏筛的经典复杂度,非常接近于线性时间,对于5×106的数据量可以轻松通过。
  • 空间复杂度:ON。需要一个长度为N的布尔数组来记录状态。由于 vector<bool> 在 C++ 中经过了位压缩优化,实际占用内存非常小。

进阶拓展:线性筛(欧拉筛)

虽然埃氏筛已经足够优秀,但在某些极端情况下,可能会提到线性筛(欧拉筛)。

埃氏筛的痛点:一个合数可能会被多个质数重复标记。例如 12,会被 2 标记一次(2 * 6),也会被 3 标记一次(3 * 4),存在冗余计算。

线性筛的核心思想:保证每个合数只会被它的最小质因数筛掉。这样时间复杂度可以降到严格的O(N)。

线性筛代码示例:

class Solution { public: int countPrimes(int n) { vector<int> primes; // 存储已找到的质数 vector<bool> isPrime(n, true); // 标记数组 int ans = 0; for (int i = 2; i < n; i++) { if (isPrime[i]) { primes.push_back(i); ans++; } // 核心:用当前质数 primes[j] 去筛 i * primes[j] for (int j = 0; j < primes.size() && i * primes[j] < n; j++) { isPrime[i * primes[j]] = false; // 保证每个合数只被它的最小质因数筛掉 if (i % primes[j] == 0) break; } } return ans; } };
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 21:56:58

Spring Boot+小程序实现社区新生儿疫苗预约系统设计与实战

做社区新生儿疫苗预约这个小程序项目&#xff0c;前后花了大概三周时间。核心需求很简单&#xff1a;社区医院或卫生服务中心的儿保科&#xff0c;每天要接待大量新生儿接种疫苗&#xff0c;电话预约、纸质登记、到现场排队&#xff0c;整个流程混乱且容易出错。家长们不知道什…

作者头像 李华
网站建设 2026/10/1 21:55:45

容器起来之后第一件事:把 rustfsadmin 换掉

一条 docker ps 打出来&#xff0c;镜像、端口、存储卷都正常&#xff0c;只有环境变量里那对默认值看着眼熟。RustFS 的默认凭据是 rustfsadmin / rustfsadmin&#xff0c;官方文档写明它只为第一次启动方便&#xff0c;正式部署都该换掉。这条要求文档里提得不多&#xff0c;…

作者头像 李华
网站建设 2026/10/1 21:54:47

禾川PLC-HCA1(三菱FX1S)编程

目录&#xff1a; 一、禾川与三菱对照表 二、HCA1-16x14YR连接电脑 1、命名规则 2、编程线的连接 3、GX Works2设置 三、定时器应用 1、继电器介绍截图 2、100mS时基定时器编程 3、10mS时基定时器编程 前置知识&#xff1a;三菱FX系列PLC-编程1 一、禾川与三菱对照表 …

作者头像 李华
网站建设 2026/10/1 21:53:56

华为HCIP网络工程师认证—DHCP、NAT和 PPPOE

在大型企业网络中&#xff0c;会有大量的主机或设备需要获取IP地址等网络参数。如果采用手工配置&#xff0c;工作量大且不好管理&#xff0c;如果有用户擅自修改网络参数&#xff0c;还有可能会造成IP地址冲突等问题。使用动态主机配置协议DHCP(Dynamic Host ConfigurationPro…

作者头像 李华
网站建设 2026/10/1 21:53:35

ChipON IDE开发KF32单片机:从建工程到调试的完整实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 21:53:33

微信小程序订阅消息报错:TAP gesture手势校验原理与解决方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华