一、时间复杂度
一个特定算法的“运行工作量”的大小只依赖于问题的规模(通常用整数量n表示)。
一般情况下,算法的基本操作重复执行的次数是问题规模n的某个函数f(n),算法的时间度量记作:
T(n) = O(f(n))
简称时间复杂度,随问题规模n增大,算法执行时间的增长率和f(n)的增长率相同。
1.1 常见的时间复杂度
相对复杂一点的算法里,可能拆分出O(1),O(n)等的时间复杂度,但整个算法只看最大的时间复杂度。
1.1.1 O(1)
常数阶的时间复杂度。例如:
def calculation1(n: int) -> int:
if n > 3:
return n + 5
else:
return n - 1
#O(1)
calculation(4)
Out[1]: 9
输入n通过一次判断,能得到结果。判断这一步在计算机中需要时间a,return的计算需要时间b,a和b都是计算机决定的,不需要多次重复运算,因此是常数阶的时间复杂度O(1)。
1.1.2 O(log n)
对数阶的时间复杂度,常见于while,例如:
def calculation2(n: int) -> int:
i = 1
while i < n:
i *= 3
return i
#O(log n)
calculation2(27)
Out[2]: 27
calculation2(28)
Out[3]: 81
i初始值为1,进入while循环,做多少次循环呢?求最小的x使得,x就是执行第四行的次数。x等于取上,while里的命令执行了对数次,无论底数是多少,时间复杂度都能写作O(log n)。
1.1.3 O(n)
一个算法的代码段执行kn次(k是常数,n是问题的规模,是变的),一般一个循环咯。下面这种循环次数固定的是O(k)哦,因为i += 1执行4次,与n无关。
def calculation3(n):
count = 0
for i in range(4):
count += 1
return n + count
#O(k)
循环的条件与n有关,一般是:
def calculation4(n):
count = 0
for i in range(n):
count += 1
return n + count
#O(n)
def calculation5(nums: List[int]) -> int:
count = 3
while count < len(nums):
count += 1
return sum(nums) + count
#O(n)
calculation5函数循环里与输入nums数组的长度相关,nums的长度也是问题的规模n。不妨记len(nums)为n,循环的次数是n-3次,O(n-3),n可以是1千1万甚至更多,减的常数3在大体量下可以忽略不计,因此时间复杂度为O(n)。
1.1.4 O(nlog n)
n*logn能看出做n次时间复杂度为O(logn)的代码块,套娃了。1.1.3的循环里头套1.1.2的循环或者反套就是O(nlog n)啦。【可能还会有别的情况,有的话再补充。】
1.1.5 O(n^2)和O(n^3)
O(n)套娃!
1.1.6 O(2^n)
现在还没遇到,遇到再补充
1.1.7 O(n!)
排序挺常见的,例如冒泡排序最差的情况。
二、空间复杂度
2.1 赋值类的空间复杂度
2.1.1 O(1)
开辟了一个空间存放值,如下面就是开了个count放int数据,虽然在循环改变count存放的具体值,但仍然是占用的count。(int是28个字节,每2**30增加4个字节)是常数阶空间复杂度。
count = 0
for i in range(3):
count += 1
2.1.2 O(n)
常见是元组、数组、哈希表啥的能储存数据的,会动态增加内容。
def calculation6(n):
res = list()
for i in range(n):
res.append(i)
return res
2.2 递归暗含的空间复杂度
递归栈占了O(n)。