在上一篇文章里,有看到一个简单算法题的2个解法,咱们运用了复杂度分析来判断哪一个解法更合适。
这里的复杂度,就是用于衡量程序的运行效率的重要度量因素。python
虽然有句俗话“无论是白猫仍是黑猫,抓到老鼠就是好猫”,这句话是站在结果导向的,没错。可是若是
有个程序要去处理海量数据,一个程序员写的要执行2天,而另外一个程序员只要半小时,那么第二种显然更适合
咱们的实际需求。程序员
复杂度是一个关于输入数据量n的函数。算法
要表示复杂度很简单,用大写O加上括号O()
将复杂度包起来就行了。好比这个代码的复杂度是f(n),那就能够写成
O(f(n))
。数组
在计算复杂度的时候,有三点须要咱们记住:函数
O(1)
表示特殊复杂度举个例子,将一个列表反转,不用reverse()。测试
def demo_1(): a = [1, 2, 3, 4, 5] b = [0 for x in range(0,5)] #第一个for循环 n = len(a) for i in range(n): # 第二个for循环 b[n - i - 1] = a[i] print(b) if __name__ == "__main__": demo_1() ===============运行结果================== D:\Programs\Python\Python36\python.exe D:/练习/leecode/fuzadu.py [5, 4, 3, 2, 1] Process finished with exit code 0
能够看到我先用了一个for循环建立了一个跟a列表等长度,元素全是0的列表。
而后再用一个for循环将a里的元素倒序放入b,最终获得一个跟a反序的列表。code
其中,每个for循环的时间复杂度都是O(n)
,2个加起来就是O(n)+O(n)
,也等于O(n+n)
,也等于O(2n)
。
也就是至关于 一段 O(n)
复杂度的代码前后执行两遍,它们的复杂度是一致的。it
有了上面的例子,这个也就好理解了。
假设,一个算法的复杂度是O(n²)+O(n)
,那么能够知道,当n愈来愈大,也就是输入的数据量愈来愈大时,n^2的变化率要比n大的多,
因此,这时候咱们只取变化率更大的n^2来表示复杂度便可,也就是O(n²)+O(n)
等同于O(n²)
。for循环
仍是借助上面的反转问题,这里再使用第二种解法。效率
def demo_2(): a = [1, 2, 3, 4, 5] tmp = 0 n= len(a) for i in range(n//2): # // 表示整数除法,返回不大于结果的一个最大整数 tmp = a[i] a[i] = a[n -i -1] a[n -i -1] = tmp print(a) if __name__ == "__main__": demo_2() ==============运行结果============== D:\Programs\Python\Python36\python.exe D:/练习/leecode/fuzadu.py [5, 4, 3, 2, 1] Process finished with exit code 0
跟第一个解法相比,第二个解法少了一个for循环,并且循环次数只是到了列表的一半,那么时间复杂度就是O(n/2)
,
因为复杂度与具体的常系数无关的性质,这段代码的时间复杂度仍是 O(n)
。
可是在空间复杂度上,第二个解法开辟了一个新的变量tmp
,它与数组长度无关。
输入是 5 个元素的数组,须要一个tmp
变量输入是 50 个元素的数组,一样只须要一个tmp
变量。
所以,空间复杂度与输入数组长度无关,这就是 O(1)
。
这里就直接上一些经验性的结论,能够直接拿过来用的:
O(1)
。O(n)
。O(n)+O(n)=O(2n)
,其实也是 O(n)
。O(n²)
。O(logn)
。趁热打铁,分析一下下面代码的复杂度:
for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { for (k = 0; k < n; k++) { } for (m = 0; m < n; m++) { } } }
能够先从最里面看,最内层是2个顺序结构的for循环,复杂度是O(n)
。
中间这层的又嵌套了一个for循环,因此这时候复杂度就变成了O(n^2)
。
最后,最外层又嵌套了一个for循环,因此最终的复杂度就是O(n^3)
。
虽然测试工程师的代码对于复杂度要求不高甚至说很是低,可是我以为理解复杂度,而且会作一些简单的分析 仍是颇有必要的。