空间复杂度与时间复杂度

173 阅读6分钟

文章目录

  1. 一个例子简单说明时间和空间复杂度
  2. 复杂度衡量标准

1. 一个例子简单说明时间和空间复杂度

操场上有500人,小明在操场上,而我们的目标是要找到他。假设我们采用两种方式,第一种是把操场上每个人都叫到办公室来一趟,一个一个聊天,确认他是否是小明。另外一种方式是我们大叫一声“小明请你出来”,然后在操场中间等着小明过来。

第一种方式:

时间复杂度:假设每个人来办公室都要花费我1一分钟的时间来谈话,那么这种方式的时间复杂度就是500 * 1分钟 = 500分钟

空间复杂度:假设每个人来办公室都占了一个位置,那么这种方式的空间复杂度就是1

第二种方式:

时间复杂度:假设操场上有10个人叫小明,听到我们的叫声以后走过来,我们再一个一个聊天识别,那么这种方式的时间复杂度就是:10 * 1分钟 = 10分钟

空间复杂度:第二种方式需要把所有人聚集在操场,所以这种方式的空间复杂度就是 500

第一种方式和第二种方式就像两种不同的算法,我们很多时候在保证空间复杂度一致的时候,不断的优化时间复杂度,让程序更快的执行

2. 复杂度衡量标准

在衡量算法复杂度的时候,一般用三个方式

最差情况用 Big O notation ,符号为 0(n)\mathcal{0}(n)
一般情况下用 Theta notation, 符合为 Θ(n)\Theta(n)
最好情况下用 Omega,符号为 Ω(n)\Omega(n)

什么叫最好/一般/最差情况呢?还是上面找小明的例子,如果我们把操作上的人一个一个叫到办公室里,而第一个被我们叫到的人恰好就是小明。这就是最好的情况。如果最后一个被我们叫到办公室的是小明,则是最差的情况。

3. 时间复杂度

就如上面找小明的例子,不同的方法或者说算法在时间和空间复杂度上有不同的效率。 常见的时间复杂度表示方法包括以下几种:O(1) , O(logn) , O(n) ,

3.1 O(1) - 常数时间复杂度
不管输入数据有多大,算法的执行时间都是恒定的。这种类型的算法被认为是最优的,因为它们的执行时间不受输入数据大小的影响。
例子:如果你本身就认识小明,你在操场上一眼就认出了小明,那么复杂度就是O(1)。

3.2 O(log n) - 对数时间复杂度

3.2.1 对简单数学概念的回顾

要理解这个复杂度,我们首先重温一些数学知识:指数和对数。指数和对数是数学中的两个基本概念,它们之间有密切的关系,但是它们表示的是完全不同的东西。让我们逐一进行解释。

3.2.2 指数 Exponentiation
指数涉及两个数字:基数和指数。当你看到像 (a^b) 这样的表达式时,(a) 是基数,(b) 是指数。这个表达式表示的是将 (a) 乘以自己 (b) 次。例如:(2^3) 表示 (2 * 2 * 2 = 8) (5^2) 表示 (5 * 5 = 25)

3.2.3. 对数 (Logarithm)
对数是指数的逆运算。它涉及到基数和结果。对数的表达式为logba=c\log_b a = c,其中 (b) 是基数,表示基数 (b) 需要被乘以自己多少次才能得到 (a)。结果 (c) 就是这个次数。例如:log28=3\log_2 8 = 3,因为 2 需要乘以自己三次才能得到 8 (即 (2^3 = 8) )。在数学学科中,当基数为10时,我们通常省略基数,只写loga\log a。 指数是乘法的另一种方式表示,而对数是指数的逆运算,它告诉我们一个基数需要被乘以自己多少次才能得到另一个特定的值。

3.2.4 回到O(log n)

假设我们有一个包含8个元素的列表,我们想知道最多需要几步才能找到一个元素(或确定它不存在)。每一步我们都将搜索区间减半:

  • 初始:8个元素
  • 第一步:4个元素
  • 第二步:2个元素
  • 第三步:1个元素

在三步之后,我们已经锁定了一个元素或确定了它不存在。这与我们的对数示例相匹配,因为log28=3 \log_2 8 = 3

举另外一个例子,你可以想象一下如何最快地将一堆纸片减少到1张:
如果你每次只拿走1张,那么需要n次 但如果你每次都将它们减半(无论是拿走一半,还是保留一半),那么你需要的时间是log2n \log_2 n

3.2.5 O(log n)容易引起的误解


首先,确切地说,计算机科学中的 O(logn)O(\log n) 和数学中的对数的基本定义是相同的。在这两个领域中,对数都表示基数需要乘以自己多少次来得到给定的值。但是,计算机科学中使用 O(logn) O(\log n) 时,通常的上下文和一些默认的假设可能与纯数学有些不同。


在计算机科学中,当我们说 O(logn)O(\log n),底数通常是2,因为二进制是计算机的基础。所以,这里的 O(logn)O(\log n)通常意味着 O(log2n)O(\log _2 n)


核心原因是计算机科学很大程度上是为了解决实际的、与计算机硬件和软件相关的问题而诞生的。因此,像二分查找、二叉树遍历等算法,它们自然地将问题的规模每次减半,使得O(log2n)O(\log _2 n)成为一个常见的复杂度。

由于这种模式在计算机科学中如此普遍,所以 O(logn)O(\log n) 成了一个默认表示O(log2n)O(\log _2 n)的缩写。综上所述,虽然计算机科学中的 O(logn)O(\log n) 在某些上下文中可能有其特定的意味,但它与数学中对数的定义并没有根本上的不同。这种差异更多的是基于历史、习惯和实际应用的需要。

  1. O(n) - 线性时间复杂度 算法的执行时间与输入数据的大小成正比。例如,简单查找算法和大多数简单排序算法。

  2. O(n log n) - 线性对数时间复杂度 这种复杂度出现在一些更高效的排序算法中,如归并排序、快速排序等。

  3. O(n^2), O(n^3), ... - 多项式时间复杂度 嵌套循环经常会导致这样的时间复杂度。例如,冒泡排序、插入排序和选择排序的时间复杂度都是 (O(n^2))。

  4. O(2^n) - 指数时间复杂度 这种算法的执行时间随着输入数据的增长而指数级增加。一个常见的例子是计算斐波那契数列的递归实现。

  5. O(n!) - 阶乘时间复杂度 这种复杂度出现在某些非常低效的算法中,例如旅行商问题的暴力求解方法。

reference:

1.白话数据结构 - 第一期 - 什么是BIG O www.bilibili.com/video/BV18F…
2. # Complexity:Asymptotic Notation(漸進符號) alrightchiu.github.io/SecondRound…