时间空间复杂度入门

简介: 本文介绍时间与空间复杂度入门知识,用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天前
|
存储 Java 索引
单/双链表代码实现
本文详解单/双链表的代码实现,涵盖增删查改操作。重点解析三大技巧:1)同时持有头尾节点引用以优化插入删除效率;2)使用虚拟头尾节点简化边界处理;3)避免内存泄漏的良好编程习惯。适合掌握链表基础后深入学习。
|
1天前
|
存储 算法 搜索推荐
线性结构检索:从数组和链表的原理初窥检索本质
本节深入解析数组与链表的存储特性及其对检索效率的影响。数组支持随机访问,适合二分查找,检索效率为O(log n);链表虽检索较慢,但插入删除高效,适用于频繁动态调整场景。通过改造链表结构,如结合数组提升检索性能,揭示了数据组织方式对检索的核心作用,帮助理解“快速缩小查询范围”这一检索本质。
|
1天前
|
存储 算法 Java
链表(链式存储)基本原理
链表是一种通过指针串联节点的线性结构,无需连续内存,支持高效增删。单链表仅有next指针,双链表增加prev指针以支持双向遍历。相比数组,链表插入删除灵活,无扩容负担,但不支持随机访问,查找需从头遍历。实际开发中常用双链表,配合虚拟头结点简化操作。
|
1天前
|
存储 数据采集 搜索推荐
状态检索:如何快速判断一个用户是否存在?
本文探讨如何高效判断用户是否存在,对比有序数组、二分查找树和哈希表后,引出更优方案:位图与布隆过滤器。位图以bit为单位存储,大幅节省空间;布隆过滤器通过多哈希函数降低冲突概率,虽有一定误判率,但查询效率达O(1),适用于注册去重、爬虫去重等场景,是提升系统性能的关键技术。
|
1天前
|
存储 Java API
数组(顺序存储)基本原理
本章讲解数组的底层原理,区分静态数组与动态数组。静态数组是连续内存空间,支持O(1)随机访问,但增删效率低,需搬移数据;通过手动实现动态数组,理解其扩容、插入、删除等操作的实现逻辑与时间复杂度,为后续数据结构打下基础。
|
1天前
|
SQL 算法 关系型数据库
熔断限流:业务如何实现自我保护?
本讲介绍RPC框架中业务的自我保护机制。面对高并发,服务端通过限流(如令牌桶、滑动窗口)防止过载,支持应用级、IP级配置,并可结合注册中心动态调整阈值;调用端则通过熔断机制避免因下游故障引发雪崩,熔断器在动态代理层拦截请求,实现快速失败与恢复,保障系统稳定性。
|
1天前
|
负载均衡 算法 网络协议
负载均衡:节点负载差距这么大,为什么收到的流量还一样?
本文探讨RPC框架中的自适应负载均衡机制。针对传统权重调节滞后问题,提出通过实时采集节点CPU、内存、请求耗时等指标,结合权重算法动态打分,自动调整节点最终权重,实现流量智能分配,提升系统稳定性与响应效率。
|
1天前
|
存储 缓存 搜索推荐
特别加餐丨倒排检索加速(二):如何对联合查询进行加速?
本文深入探讨联合查询的加速方法,针对倒排索引中复杂查询场景,系统介绍四种工业级优化技术:调整次序法通过优化求交/并集顺序降低计算代价;快速多路归并法利用跳表提升多列表合并效率;预先组合法提前计算高频查询结果;缓存法则借助LRU机制动态存储热点组合,显著提升检索性能。
|
1天前
|
存储 搜索推荐 数据库
倒排索引:如何从海量数据中查询同时带有「极」和「客」的唐诗?
本文介绍倒排索引技术,通过将内容作为关键词建立索引,实现高效检索。对比正排索引的O(n)遍历查询,倒排索引可在O(1)时间内定位含指定字的唐诗,并通过归并有序链表快速求交集,解决“同时含‘极’和‘客’”等多条件查询问题,广泛应用于搜索引擎、数据库全文检索等场景。
|
1天前
|
存储 搜索推荐 算法
特别加餐 | 倒排检索加速(一):工业界如何利用跳表、哈希表、位图进行加速?
本文深入解析工业界如何利用跳表、哈希表和位图加速倒排索引的交集运算。通过跳表实现快速跳跃查找,哈希表提升小集合匹配效率,位图及Roaring Bitmap优化存储与计算,结合实际场景分析各类技术的适用条件与性能权衡,揭示搜索引擎背后的高效检索原理。(238字)