前言
本篇内容:简要介绍各种时间复杂度,并呈现Python代码示例。
时间复杂度
根据定义,时间复杂度指输入数据大小为 N 时,算法运行所需花费的时间。
根据输入数据的特点,时间复杂度具有「最差」、「平均」、「最佳」三种情况,分别使用 O , Θ , Ω 三种符号表示。
使用最多的是O符号,即考虑最差情况下执行算法所需的计算操作次数。
从小到大排列,常见的算法复杂度有:
O(1) < O(logN) < O(N) < O(NlogN) < O(N^2) < O(2^N) < O(N!)
常数 O(1)
常数复杂度O(1)说明代码运行次数与数据大小N无关,性能最好。
def c1(N):
a = 1
b = 2
x = a * b + N
return 1
即使代码中包含循环语句,循环次数也与N无关
def c2(N):
c = 0
a = 10000
for i in range(a): # 循环次数只与常数a有关
c += 1
return c
线性 O(N)
线性复杂度O(N)说明代码运行次数与N大小呈线性变化关系。一般存在一层与N有线性关系的循环。
def l1(N):
l = 0
for i in range(N): # 线性关系均可 2*N 3*N ...
l += 1
return l
即使存在多层循环,也只有一层循环与N有关。
def l2(N):
l = 0
a = 10000
for i in range(N):
for j in range(a): # 相当于一层 a*N 循环,仍然为线性关系
l += 1
return l
平方 O(N^2)
平方复杂度O(N^2)一般表现为两层与N线性相关的循环,整体上与N呈平方关系。
例如经典的冒泡排序,算法时间复杂度为O(N^2)。
def bubble_sort(nums):
N = len(nums)
for i in range(N - 1): # 第一层 O(N)
for j in range(N - 1 - i): # 第二层平均 O(N/2)=O(N)
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j]
return nums
指数 O(2^N)
指数复杂度O(2^N)类似于生物上的二分裂,一般出现在递归当中,每次循环多出两个分支。
def d1(N):
if N <= 0: return 1
count1 = d1(N - 1)
count2 = d1(N - 1) # 最终次数 1+2+4+...+2^(N-1)=2^N-1
return count1 + count2
阶乘 O(N!)
阶乘复杂度O(N!)和全排列原理相同,一般出现在递归当中。
def d2(N):
if N <= 0: return 1
for _ in range(N): # 最终循环次数 N*(N-1)*...*1=N!
count += d2(N - 1)
return count
对数 O(logN)
区别于指数复杂度「每次多出两个分支」的情况,对数复杂度O(logN)一般出现在「每次循环除一半」的情况,常出现于 二分、分治等算法中。
def log2(N):
count = 0
i = N
while i > 1:
count += 1
i = i / 2 # 每次循环次数都会变为原来的一半
return count
事实上,无论底数是多少,时间复杂度都可以记为O(logN),它们的增长维度是相同的。
def loga(N):
count = 0
i = N
a = 3
while i > 1:
count += 1
i = i / a # 无论底数a是多少,时间复杂度都为O(logN)
return count
线性对数 O(NlogN)
线性对数复杂度O(NlogN)一般出现在两层独立的循环中,一层复杂度为O(N),一层复杂度为O(logN)。常出现于 快速排序、归并排序、堆排序等算法。
def nlogn(N):
count = 0
i = N
while i > 1: # 第一层循环 O(logN)
i = i / 2
for j in range(N): # 第二层循环 O(N)
count += 1
return count
总结
以上是今天的学习笔记,时间复杂度是用于衡量算法执行时间随输入数据规模增长而增长的趋势的量度。它是分析算法效率的重要工具之一,有助于我们理解和预测算法在不同输入规模下的表现。
下期预告:链表