时间空间复杂度入门

简介: 本文介绍时间与空间复杂度入门知识,使用Big O表示法(如O(1)、O(n)、O(n²)),强调估算而非精确计算,保留最高次项。时间复杂度常由循环嵌套层数决定,空间复杂度看额外内存占用。分析以最坏情况为主,越小越好。结合多个代码示例,帮助初学者理解复杂度分析的基本方法与常见误区。

时间空间复杂度入门
对于初学者,你只需要记住以下几点:
1、时空复杂度用 Big O 表示法表示(类似O(1), O(n²), O(logn) 等)。它们都是估计值,不需要精确计算,且仅保留最高增长项。
比方说 O(2n²+3n+1)等同于 O(n²)O(n²),O(1000n+1000) 等同于 O(n)O(n)。
2、我们分析算法的复杂度时,一般分析的是最坏情况的复杂度。它们都是越小越好。比方说时间复杂度 O(n)的算法比 O(n²)的算法执行效率高,空间复杂度 O(1)的算法比 O(n)的算法内存消耗小。
当然,一般我们要说明这个 n 代表什么,比如 n 代表输入的数组的长度。
4、如何估算?现在你可以简单理解:时间复杂度大部分情况下就是看 for 循环的最大嵌套层数;空间复杂度就看算法申请了多少空间来存储数据。
注意
以上的分析方法中,有些细节并不严谨:
1、按照 for 循环的嵌套层数来估算时间复杂度是简化的方法,其实不完全准确。
2、大部分时候我们是分析最坏情况下的复杂度,但是对于数据结构 API 的复杂度衡量,我们会分析平均复杂度。
举几个例子来说比较直观。
时间/空间复杂度案例分析
示例一,时间复杂度 O(n),空间复杂度 O(1):
算法包含一个 for 循环遍历 nums 数组,所以时间复杂度是 O(n),其中 n 代表 nums 数组的长度。我们的算法只使用了一个 sum 变量,这个 nums 是题目给的输入,不算在我们算法的空间复杂度里面,所以空间复杂度是 O(1)。
示例二,时间复杂度 O(n),空间复杂度 O(1):
其实只有当 n 是 10 的倍数时,算法才会执行 for 循环,时间复杂度是 O(n)。其他情况下算法会直接返回,时间复杂度是 O(1)。但是算法复杂度只考察最坏情况,所以这个算法的时间复杂度是 O(n),空间复杂度是 O(1)。
示例三,时间复杂度 O(n²),空间复杂度 O(1):
Java
运行代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
// 数组是否存在两个数,它们的和为 target?
boolean hasTargetSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return true;
}
}
}
return false;
}
算法包含两个 for 循环嵌套,所以时间复杂度是 O(n²),其中 n 代表 nums 数组的长度。
我们的算法只使用了 i, j 两个变量,这是常数级别的空间消耗,所以空间复杂度是 O(1)。
你也许会说,内层的 for 循环并没有遍历整个数组,且有可能提前 return,算法实际执行的次数应该是小于 n²的,时间复杂度还是 O(n²)吗?
是的,还是 O(n²)。前面说了 Big O 表示法是估计值,不需要精确计算。具体到不同的输入,算法的实际执行次数确实会小于 n²,但我们不需要关心。
简单说就是:看到嵌套 for 循环,时间复杂度就是 O(n²)。
示例四,时间复杂度 O(n),空间复杂度 O(n):
Java
运行代码
复制代码
1
2
3
void exampleFn(int n) {
int[] nums = new int[n];
}
这个函数中创建了一个大小为 n 的数组,所以空间复杂度是 O(n)。
申请数组空间及初始化数组也需要时间,所以时间复杂度也是 O(n)。
时间复杂度并不仅仅体现在你看得到的 for 循环,每一行代码都可能有隐藏的时间复杂度。所以说要了解常见数据结构的实现原理,这是准确分析时间复杂度的基础。
示例五,时间复杂度 O(n),空间复杂度 O(n):
Java
运行代码
复制代码
1
2
3
4
5
6
7
8
// 输入一个整数数组,返回一个新的数组,新数组的每个元素是原数组对应元素的平方
int[] squareArray(int[] nums) {
int[] res = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
res[i] = nums[i] * nums[i];
}
return res;
}
算法初始化 res 数组需要 O(n)的时间复杂度,包含一个 for 循环,时间复杂度也是 O(n),总的时间复杂度是还是 O(n)其中 n 代表 nums 数组的长度。
我们声明了一个新的数组 res,这个数组的长度和 nums 数组一样,所以空间复杂度是 O(n)。

相关文章
|
1天前
|
监控 负载均衡 网络协议
健康检测:这个节点都挂了,为啥还要疯狂发请求?
本文探讨RPC框架中服务健康检测的挑战与优化。通过真实案例揭示:当节点网络异常、心跳间歇失败时,仅依赖心跳机制易导致“半死不活”节点持续接收请求。提出结合业务请求可用率(成功次数/总调用次数)动态评估节点状态,弥补传统心跳机制的不足,实现更精准的健康判断,提升系统稳定性与可用性。
|
1天前
|
负载均衡 安全 数据库
异常重试:在约定时间内安全可靠地重试
本节讲解RPC框架中的异常重试机制,重点探讨如何在超时控制、节点避让和异常判定的基础上实现安全可靠的重试。需确保业务逻辑幂等,设置重试白名单,并在每次重试前重置超时时间,避免耗时超标,提升系统容错性与稳定性。(239字)
|
1天前
|
Java Maven 数据安全/隐私保护
Nexus仓库
Nexus是一款开源仓库管理工具,支持Maven、NPM、Docker等格式。本文介绍其在Linux与Docker环境下的安装配置,包括JDK部署、OSS版下载、仓库创建、用户权限管理及密码重置方法,并涵盖私服搭建、持久化存储、资源上传与匿名访问设置,助力企业高效构建私有仓库体系。
|
1天前
|
存储 搜索推荐 定位技术
14 | 空间检索(下):「查找最近的加油站」和「查找附近的人」有何不同?
本文探讨了动态调整查询范围的高效检索方案,重点介绍如何利用四叉树和前缀树优化“查找最近的k个目标”场景。针对GeoHash固定范围查询的局限性,提出通过非满四叉树实现动态分裂与回溯查询,在保证效率的同时节省存储空间;并引出前缀树对GeoHash字符串编码的高效索引方法。最后拓展至高维场景,简述k-d树的适用性与挑战,为近邻搜索提供系统性解决方案。
|
1天前
|
存储 数据采集 监控
RFID助力钢材应用智能化管理
RFID技术通过在钢材上绑定耐高温抗金属标签,实现生产、仓储、出库全流程数据化管理,自动采集信息,提升追溯精度与库存准确率至99.5%以上,减少60%人工操作,助力钢铁企业智能化升级。
|
1天前
|
存储 关系型数据库 MySQL
为什么MySQL索引结构是B+tree
MySQL采用B+树作为索引结构,因其非叶子节点仅存键和指针,使每页可存储更多键,树层级更浅,查询更快。所有数据集中于叶子节点,查询效率稳定,且叶子节点形成双向链表,支持高效范围查询。
|
1天前
|
存储 NoSQL 定位技术
13 | 空间检索(上):如何用 Geohash 实现「查找附近的人」功能?
本文介绍了如何高效实现“查找附近的人”功能,提出基于Geohash的区域编码与索引方案。通过将二维坐标转为一维编码,结合非精准与精准检索策略,利用跳表、二叉树等数据结构提升查询效率,适用于大规模地理位置服务场景。
|
1天前
|
敏捷开发 Java 测试技术
为什么要单元测试
本文探讨单元测试在现代软件开发中的核心价值,打破“写单测费时”的误区。通过测试金字塔模型,阐述单元测试如何提升代码质量、加速迭代、增强重构信心,并揭示冰激凌筒等反面模式的危害,倡导研发自测、高效交付的工程实践。
|
1天前
|
存储 算法 Java
链表(链式存储)基本原理
链表是一种通过指针串联节点的线性结构,无需连续内存,支持高效增删。单链表仅有next指针,双链表增加prev指针以支持双向遍历。相比数组,链表插入删除灵活,无扩容负担,但不支持随机访问,查找需从头遍历。实际开发中常用双链表,配合虚拟头结点简化操作。
|
1天前
|
存储 数据采集 搜索推荐
状态检索:如何快速判断一个用户是否存在?
本文探讨如何高效判断用户是否存在,对比有序数组、二分查找树和哈希表后,引出更优方案:位图与布隆过滤器。位图以bit为单位存储,大幅节省空间;布隆过滤器通过多哈希函数降低冲突概率,虽有一定误判率,但查询效率达O(1),适用于注册去重、爬虫去重等场景,是提升系统性能的关键技术。