一文读懂算法中的时间复杂度和空间复杂度,O(1)、O(logn)、O(n)、O(n^2)、O(2^n) 附举例说明,常见的时间复杂度,空间复杂度

本文主要是介绍一文读懂算法中的时间复杂度和空间复杂度,O(1)、O(logn)、O(n)、O(n^2)、O(2^n) 附举例说明,常见的时间复杂度,空间复杂度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

时间复杂度和空间复杂度是什么

时间复杂度(Time Complexity)是描述算法运行时间长短的一个度量。空间复杂度(Space Complexity)是描述算法在运行过程中所需要的存储空间大小的一个度量。 
 
时间复杂度和空间复杂度是衡量算法性能的重要指标。在实际开发中,我们通常会选择时间复杂度和空间复杂度都较低的算法。 
 
时间复杂度可以用大O表示法来表示。大O表示法是用一个大写字母O来表示一个函数的增长率。例如,一个函数f(n)的增长率为O(n),表示当n趋于无穷大时,f(n)的增长率与n的增长率相同。 
 
空间复杂度也可以用大O表示法来表示。例如,一个函数f(n)的空间复杂度为O(n),表示当n趋于无穷大时,f(n)所需要的存储空间与n的增长率相同。 
 
在实际开发中,我们通常会选择时间复杂度和空间复杂度都较低的算法。例如,在排序算法中,我们通常会选择快速排序算法,而不是冒泡排序算法。这是因为快速排序算法的时间复杂度为O(nlogn),而冒泡排序算法的时间复杂度为O(n^2)。
 

常见的时间复杂度有哪些?

O(1):常数时间复杂度。表示算法运行时间与输入数据的大小无关。例如,求一个数的绝对值,无论这个数是多少,算法运行时间都是常数。 

例如:访问数组元素:如果我们有一个长度为n的数组,并且我们想要访问数组中的某个元素,那么无论n多大,访问该元素的时间复杂度都是O(1)。因为访问数组元素只需要一个固定的时间,与数组的大小无关。

算法实现:

    /*** 我们只处理数组中的第一个元素,无论数组中有多少个元素,* 算法的运行时间是固定的,所以时间复杂度为O(1)。** @param arr 数组*/public int constantTimeAlgorithm(int[] arr) {int firstElement = arr[0];// 在这里进行处理第一个元素的操作// ...return firstElement;}

O(logn):对数时间复杂度。表示算法运行时间与输入数据的对数成正比。例如,二分查找算法。

你可以这样理解:

假设你有一个长度为n的数列,你想找到某个数在数列中的位置。如果用顺序查找,你需要遍历整个数列,时间复杂度为O(n)。如果用二分查找,你只需要遍历数列的一半,时间复杂度为O(logn)。 随着n的增大,O(n)的增长速度要比O(logn)快得多。例如,当n=100时,O(n)的值为100,而O(logn)的值为7。当n=1000时,O(n)的值为1000,而O(logn)的值为10。 因此,O(logn)的时间复杂度比O(n)的时间复杂度要好得多。

复习一下计算过程:

当计算 log₂(100) 时,我们要找到一个数 x,使得 2 的 x 次方等于 100。换句话说,我们要求解以下方程:

2^x = 100

为了求解这个方程,我们可以使用对数的定义。根据定义,log₂(100) 就是满足 2 的 x 次方等于 100 的 x 值。

因此,我们可以将方程改写为:

x = log₂(100)

现在,我们需要计算 log₂(100) 的值。可以使用换底公式将其转化为常用对数或自然对数。换底公式如下:

log₂(100) = logₓ(100) / logₓ(2)

其中,x 可以是任意正数,我们可以选择常用对数(以 10 为底)或自然对数(以 e 为底)。这里我们选择常用对数。

所以,我们有:

log₂(100) = log₁₀(100) / log₁₀(2)

接下来,我们计算 log₁₀(100) 和 log₁₀(2) 的值:

log₁₀(100) ≈ 2     log₁₀(2) ≈ 0.30103

将这些值代入公式中,我们可以计算出 log₂(100) 的近似值:

log₂(100) ≈ log₁₀(100) / log₁₀(2) ≈ 2 / 0.30103 ≈ 6.6438561898

所以,log₂(100) 的近似值为 6.6438561898。

算法实现:

    /*** 代码中的binarySearch方法实现了对有序数组进行二分查找的算法。* 它将目标元素与数组的中间元素进行比较,若相等则返回中间元素的索引,若小于中间元素则在左子数组中继续查找,* 若大于中间元素则在右子数组中继续查找。通过不断缩小查找范围,直到找到目标元素或发现不存在目标元素。* * @param arr 数组* @param target 目标值* @return int*/public int binarySearch(int[] arr, int target) {int low = 0;int high = arr.length - 1;while (low <= high) {int mid = low + (high - low) / 2;if (arr[mid] == target) {return mid;} else if (arr[mid] < target) {low = mid + 1;} else {high = mid - 1;}}return -1; // 如果找不到目标元素,则返回-1}

O(n):线性时间复杂度。表示算法运行时间与输入数据的大小成正比。例如,冒泡排序算法。

你可以这样理解:

假设你有一个长度为n的数列,你想对这个数列进行排序。如果用冒泡排序,你需要遍历整个数列,然后将每个元素与它后面的元素进行比较,如果前一个元素比后一个元素大,就交换它们的位置。你需要重复这个过程,直到整个数列都被排序。 随着n的增大,O(n)的增长速度要比O(1)快得多。例如,当n=100时,O(n)的值为100,而O(1)的值为1。当n=1000时,O(n)的值为1000,而O(1)的值为1。 因此,O(n)的时间复杂度比O(1)的时间复杂度要差得多。

算法实现:

    /*** 这个方法计算整数数组的平均值* * 在这个算法中,我们遍历整个数组一次,所以时间复杂度是O(n),其中n是数组的长度。* 这是计算数组平均值的最佳时间复杂度,因为我们至少需要查看数组中的每个元素一次。** @param array 数组*/public static double calculateAverage(int[] array) {int sum = 0;for (int i = 0; i < array.length; i++) {sum += array[i];}return (double) sum / array.length;}

O(n^2):平方时间复杂度。表示算法运行时间与输入数据的平方成正比。例如,选择排序算法。

O(n^2) 是一个表示算法复杂度的概念。简单来说,当你运行一个O(n^2)的算法时,它的运行时间或步骤的数量会随着输入大小n的增加而增加。具体来说,这个算法的复杂度是指它的运行时间或步骤的数量与n的平方成正比。

举个例子,如果你有一个数组,你想计算每个元素与所有其他元素的组合,那么你需要对每个元素进行n次比较(n是数组的大小),总共需要进行n * n = n^2次比较。因此,这个算法的时间复杂度是O(n^2)。

总结一下,O(n^2) 表示当输入大小n增加时,算法的运行时间或步骤数量会以n的平方的速度增加。

代码实现:

    /*** 一个时间复杂度为O(n^2)的算法,常见的方法是使用嵌套循环。* 下面代码实现了一个冒泡排序算法。它通过嵌套循环遍历数组,并比较相邻的元素。* 如果前一个元素大于后一个元素,则交换它们的位置。通过多次遍历和交换,* 较大的元素会逐渐向数组的末尾冒泡。* 这个过程将重复执行n-1次,每次循环都需要比较n-i-1次。因此,总的比较次数为:** n-1 + n-2 + ... + 1 = n * (n-1) / 2,** 最终得到时间复杂度为O(n^2)。** @param arr 数组*/public void bubbleSort(int[] arr) {int n = arr.length;for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {// 交换arr[j]和arr[j + 1]int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}}

O(n^3):立方时间复杂度。表示算法运行时间与输入数据的立方成正比。

如果你有一个非常大的数组,并且你想计算每个元素与所有其他元素的组合的乘积,那么你需要对每个元素进行n次比较,然后再进行一次乘法操作。所以总共需要进行n * n = n^2次比较,以及n * n * n = n^3次乘法操作。因此,这个算法的时间复杂度是O(n^3)。

O(2^n):指数时间复杂度。表示算法运行时间与输入数据的指数成正比。例如,汉诺塔问题,斐波那契数列。

斐波那契数列:斐波那契数列是一个非常著名的数列,其中每个数字都是前两个数字的和。对于斐波那契数列的第n项,我们可以通过递归或迭代来计算。但是,由于递归的重复计算,其时间复杂度是O(2^n)。这是因为每次递归都会生成一个新的项,导致重复计算大量前面的项,因此总体时间复杂度是指数级的增长。

算法实现:

    /*** 这个代码中,generatePowerSet方法会生成一个数组的所有子集。* 我们通过两个嵌套的循环来实现这一点。外部循环遍历2^n个可能的子集* (因为每个元素都可以在子集中或不在子集中,所以总共有2^n个子集),* 内部循环则根据当前子集的二进制表示来决定是否将数组中的元素添加到子集中。* 如果二进制表示的某一位为1,那么就将对应的元素添加到子集中。* * @param array 数组* @return List<List<Integer>>*/public static List<List<Integer>> generatePowerSet(int[] array) {List<List<Integer>> powerSet = new ArrayList<>();int n = array.length;for (int i = 0; i < (1 << n); i++) {List<Integer> subset = new ArrayList<>();for (int j = 0; j < n; j++) {if (((i >> j) & 1) == 1) {subset.add(array[j]);}}powerSet.add(subset);}return powerSet;}

常见的空间复杂度有哪些?

算法的空间复杂度是评估算法在执行过程中所需额外存储空间的重要指标。以下是算法空间复杂度的一些常见类型:

  • 常数空间复杂度(O(1)):算法执行过程中仅需要固定大小的额外空间。无论输入规模大小,所需的额外空间保持不变。
  • 线性空间复杂度(O(n)):算法执行过程中所需的额外空间与输入规模线性相关。随着输入规模的增长,所需的空间也按比例增长。
  • 对数空间复杂度(O(log n)):算法执行过程中所需的额外空间与输入规模的对数成正比。即使输入规模较大,所需的额外空间也会相对较少。
  • 平方空间复杂度(O(n^2)):算法执行过程中所需的额外空间与输入规模的平方成正比。随着输入规模的增长,所需的空间会以平方的速度增长。
  • 立方空间复杂度(O(n^3)):算法执行过程中所需的额外空间与输入规模的立方成正比。随着输入规模的增长,所需的空间会以立方的速度增长。
  • 指数空间复杂度(O(2^n)):算法执行过程中所需的额外空间与输入规模的指数成正比。随着输入规模的增长,所需的空间会以指数的速度增长。

这些空间复杂度类型可以用于评估算法在处理不同规模输入时所需的额外存储空间的大小。选择合适的算法和数据结构可以优化空间复杂度,以适应不同规模的需求。

这篇关于一文读懂算法中的时间复杂度和空间复杂度,O(1)、O(logn)、O(n)、O(n^2)、O(2^n) 附举例说明,常见的时间复杂度,空间复杂度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/500978

相关文章

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

MySQL按时间维度对亿级数据表进行平滑分表

《MySQL按时间维度对亿级数据表进行平滑分表》本文将以一个真实的4亿数据表分表案例为基础,详细介绍如何在不影响线上业务的情况下,完成按时间维度分表的完整过程,感兴趣的小伙伴可以了解一下... 目录引言一、为什么我们需要分表1.1 单表数据量过大的问题1.2 分表方案选型二、分表前的准备工作2.1 数据评估

MySQL设置密码复杂度策略的完整步骤(附代码示例)

《MySQL设置密码复杂度策略的完整步骤(附代码示例)》MySQL密码策略还可能包括密码复杂度的检查,如是否要求密码包含大写字母、小写字母、数字和特殊字符等,:本文主要介绍MySQL设置密码复杂度... 目录前言1. 使用 validate_password 插件1.1 启用 validate_passwo

一文带你迅速搞懂路由器/交换机/光猫三者概念区别

《一文带你迅速搞懂路由器/交换机/光猫三者概念区别》讨论网络设备时,常提及路由器、交换机及光猫等词汇,日常生活、工作中,这些设备至关重要,居家上网、企业内部沟通乃至互联网冲浪皆无法脱离其影响力,本文将... 当谈论网络设备时,我们常常会听到路由器、交换机和光猫这几个名词。它们是构建现代网络基础设施的关键组成

MySQL ORDER BY 语句常见用法、示例详解

《MySQLORDERBY语句常见用法、示例详解》ORDERBY是结构化查询语言(SQL)中的关键字,隶属于SELECT语句的子句结构,用于对查询结果集按指定列进行排序,本文给大家介绍MySQL... 目录mysql ORDER BY 语句详细说明1.基本语法2.排序方向详解3.多列排序4.常见用法示例5.