时间空间复杂度入门

简介: 初学者掌握算法复杂度要点:用Big O表示法估算时间与空间复杂度,忽略常数项和低阶项,关注最坏情况。时间复杂度常由循环嵌套层数决定,空间复杂度看额外内存使用。结合实例理解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)

// 输入一个整数数组,返回所有元素的和

int getSum(int[] nums) {

   int sum = 0;

   for (int i = 0; i < nums.length; i++) {

       sum += nums[i];

   }

   return sum;

}

算法包含一个 for 循环遍历 nums 数组,所以时间复杂度是 O(n),其中 n 代表 nums 数组的长度。我们的算法只使用了一个 sum 变量,这个 nums 是题目给的输入,不算在我们算法的空间复杂度里面,所以空间复杂度是 O(1)。

示例二,时间复杂度 O(n),空间复杂度 O(1)

// 当 n 是 10 的倍数时,计算累加和,否则返回 -1

int sum(int n) {

   if (n % 10 != 0) {

       return -1;

   }

   int sum = 0;

   for (int i = 0; i <= n; i++) {

       sum += i;

   }

   return sum;

}

其实只有当 n 是 10 的倍数时,算法才会执行 for 循环,时间复杂度是 O(n)。其他情况下算法会直接返回,时间复杂度是 O(1)。但是算法复杂度只考察最坏情况,所以这个算法的时间复杂度是 O(n),空间复杂度是 O(1)。

示例三,时间复杂度 O(n²),空间复杂度 O(1)

// 数组是否存在两个数,它们的和为 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)

void exampleFn(int n) {

   int[] nums = new int[n];

}

这个函数中创建了一个大小为 n 的数组,所以空间复杂度是 O(n)

申请数组空间及初始化数组也需要时间,所以时间复杂度也是 O(n)

时间复杂度并不仅仅体现在你看得到的 for 循环,每一行代码都可能有隐藏的时间复杂度。所以说要了解常见数据结构的实现原理,这是准确分析时间复杂度的基础。

示例五,时间复杂度 O(n),空间复杂度 O(n)

// 输入一个整数数组,返回一个新的数组,新数组的每个元素是原数组对应元素的平方

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)


相关文章
|
9月前
|
JavaScript 前端开发 图形学
Three.js:Web 最重要的 3D 渲染引擎的技术综述
Three.js 是 Web 实时 3D 图形的事实标准,作为 WebGL 的结构化抽象层,它通过场景图、缓冲几何体、材质系统与高效渲染循环,简化 GPU 编程。本文深入解析其架构设计、性能优化关键点及底层原理,揭示高性能 3D 应用背后的工程技术。
1030 1
|
人工智能 JSON 自然语言处理
除了MCP我们还有什么?
本文详细描述 agents.json ,涵盖了其背景、工作原理、与 OpenAPI 的关系等内容。
1437 94
除了MCP我们还有什么?
|
人工智能 缓存 运维
探秘 AgentRun丨通过无代码创建的 Agent,如何用高代码进行更新?
AgentRun 打破 AI Agent 开发困局,无代码快速验证想法,一键转高代码实现深度定制。60 秒创建 Agent,支持多模型、工具集成与 Prompt 优化;业务增长后可平滑演进,保留配置生成高质量代码,助力从原型到生产的持续迭代。
587 31
|
9月前
|
敏捷开发 Dubbo Java
需求开发人日评估
本文介绍敏捷开发中工时评估的关键方法——人日估算。涵盖开发、自测、联调、测试及发布各阶段的时间分配,并提供常见需求如Excel导入导出、单表增删改查、跨服务调用等的参考人日,助力团队科学规划迭代周期。(238字)
 需求开发人日评估
|
数据采集 人工智能 自然语言处理
打通模型与现实世界的最后一公里?MCP极速入门指南
本文重点讲述如何快速实战上手MCP。
1863 94
打通模型与现实世界的最后一公里?MCP极速入门指南
|
9月前
|
人工智能 安全 API
AI 智能体的分类及开发
AI智能体是大模型的高阶应用,具备自主思考、规划与执行能力。本文详解其开发(LangGraph/AutoGen)、评估(成功率/幻觉率)、合规(标识与备案)、上线(容器化/可观测性)及验收要点,助力构建安全、高效、可落地的智能体系统。#AI智能体 #AI应用
|
Oracle 关系型数据库 数据库
手把手教你Oracle DataGuard主备切换(switchover)
手把手教你Oracle DataGuard主备切换(switchover)
2560 4
|
数据采集 存储 数据库连接
Requests与BeautifulSoup:高效解析网页并下载资源
Requests与BeautifulSoup:高效解析网页并下载资源
|
传感器 芯片 SoC
分辨GPIO定义
GPIO(通用输入输出接口)是微控制器上的引脚,用于连接外部设备,可配置为输入或输出模式。引脚编号有物理编号(BOARD模式)和BCM编号两种,前者按实际位置编号,后者基于芯片内部通道。GPIO引脚可读取外部信号(输入)或发送信号(输出),具体功能和配置需参考芯片手册。
|
存储 运维 监控
开源日志Graylog
【10月更文挑战第21天】
2377 8

热门文章

最新文章