好算法的特点:时效高 与 存储低
时效是指时间效率,即算法的执行时间,对于同一个问题的多种不同解决方法,执行时间越短的算法效率越高,越长的算法效率越低。存储是指算法在执行时所需的存储空间,主要是算法程序运行是占用的内存空间。
时间复杂度
”不依赖于所用机器或者编程语言的度量指标,这种度量指标可以帮助我们判断算法的优劣,并且可以用来比较算法的具体实现“
大O表示法
「数量级」函数用来描述当规模 n 增加时,T(n) 函数中增长最快的部分,这个数量级函数我们一般用「大 O」表示,记做 O(f(n))。它提供了计算过程中实际步数的近似值。这里的 n 一般指的是「数据的规模大小」
最好情况、最坏情况、平均情况
有时候算法的运行时间还取决于「具体数据」而不仅仅是「问题的规模大小」。对于这样的算法,我们把它们的执行情况分为「最优情况」、「最坏情况」和「平均情况」。
某个特定的数据集能让算法的执行情况极好,这就是最「最好情况」,而另一个不同的数据会让算法的执行情况变得极差,这就是「最坏情况」。不过在大多数情况下,算法的执行情况都介于这两种极端情况之间,也就是「平均情况」。
「最优情况」: 没有参考价值,因为反映的只是最乐观最理想的情况。
「平均情况」: 对算法的全面评价,完整全面的反映了算法的性质,但这种衡量并没有什么保证,并不是每个运算都能在这种情况内完成。
「最坏情况」: 提供了一种保证,保证运行时间将不会再坏了,所以一般所算的时间复杂度是最坏情况下的时间复杂度。
常见的数量级函数
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n)
1. 常数函数
无论 n 为多少,以下两段代码就是 运行 3 次和运行 8 次的区别。这种与数据的规模大小 n 无关,执行时间恒定的算法我们就叫它具有 O(1) 的时间复杂度。
n = 100 # 1 次
sum = (1 + n) *n / 2 # 1 次
print(sum) # 1 次
上述时间复杂度O(1)
a = 100 # 1 次
sum = (1 + n) * n / 2 # 1 次
sum = (1 + n) * n / 2 # 1 次
sum = (1 + n) * n / 2 # 1 次
sum = (1 + n) * n / 2 # 1 次
sum = (1 + n) * n / 2 # 1 次
sum = (1 + n) * n / 2 # 1 次
sum = (1 + n) * n / 2 # 1 次
sum = (1 + n) * n / 2 # 1 次
print(sum) # 1 次
上述时间复杂度O(1)
2. 对数函数
cnt = 1
while cnt < n:
cnt *= 2 # O(1)
上述的代码可以解释成 cnt 乘以多少个 2 以后才能大于等于 n,我们假设个数是 x,也就是求 2^x = n,即 x = log2n,所以这个循环的时间复杂度就是 O(logn)。
空间复杂度
类比于时间复杂度的讨论,一个算法的空间复杂度是指该算法所耗费的存储空间,计算公式计作:S(n) = O(f(n))。
其中 n 也为数据的规模,f(n) 在这里指的是 n 所占存储空间的函数。
一般情况下,我们的程序在机器上运行时,刨去需要存储程序本身的输入数据等之外,还需要存储对数据操作的「存储单元」。如果输入数据所占空间和算法无关,只取决于问题本身,那么只需要分析算法在实现过程中所占的「辅助单元」即可。如果所需的辅助单元是个常数,那么空间复杂度就是 O(1)。
「空间换时间」:在求解斐波那契数列数列的时候我们可以直接用公式去递归求,用哪个求哪个,同样也可以先把很多结果都算出来保存起来,然后用到哪个直接调用,这就是典型的用空间换时间的做法。