根据定义,时间复杂度指输入数据大小为 N 时,算法运行所需花费的时间。需要注意:
根据输入数据的特点,时间复杂度具有「最差」、「平均」、「最佳」三种情况,分别使用 O, Θ, Ω 三种符号表示。以下借助一个查找算法的示例题目帮助理解。
题目: 输入长度为 N 的整数数组 nums ,判断此数组中是否有数字7,若有则返回 true ,否则返回 false。
解题算法: 线性查找,即遍历整个数组,遇到 7 则返回 true 。
Python代码:
def find_seven(nums):
for num in nums:
if num == 7:
return True
return False
大 O 是最常使用的时间复杂度评价渐进符号,下文示例与本 LeetBook 题目解析皆使用 O 。
根据从小到大排列,常见的算法时间复杂度主要有:
O(1) < O(log N) < O(N) < O(N log N) < O(N 2) < O(2N) < O(N !)
对于以下所有示例,设输入数据大小为 N ,计算操作数量为 count 。图中每个「蓝色方块」代表一个单元计算操作。
运行次数与 N 大小呈常数关系,即不随输入数据大小 N 的变化而变化。
def algorithm(N):
a = 1
b = 2
x = a * b + N
return 1
对于以下代码,无论 a 取多大,都与输入数据大小 N 无关,因此时间复杂度仍为 O(1) 。
def algorithm(N):
count = 0
a = 10000
for i in range(a):
count += 1
return count
循环运行次数与N大小呈线性关系,时间复杂度为O(N)。
def algorithm(N):
count = 0
for i in range(N):
count += 1
return count
对于以下代码,虽然是两层循环,但第二层与N大小无关,因此整体仍与N呈线性关系。
def algorithm(N):
count = 0
a = 10000
for i in range(N):
for j in range(a):
count += 1
return count
两层循环相互独立,都与N呈线性关系,因此总体与N呈平方关系,时间复杂度为O(N 2)。
def algorithm(N):
count = 0
for i in range(N):
for j in range(N):
count += 1
return count
以冒泡排序为例,其包含两层独立循环:
def bubble_sort(nums):
N = len(nums)
for i in range(N - 1):
for j in range(N - 1 - i):
if nums[j] > nums[j + 1];
nums[j], nums[j + 1] = nums[j + 1], nums[j]
return nums
生物学科中的“细胞分裂”即是指数级增长。初始状态为1个细胞,分裂一轮后为2个,分裂两轮后为4个,……,分裂N轮后有2N个细胞。
算法中,指数阶常出现与递归,算法原理图与代码如下所示:
def algorithm(N)
if N <= 0: return 1
count_1 = algorithm(N - 1)
count_2 = algorithm(N - 1)
return count_1 + count_2
阶乘阶对应数学上常见的“全排列”。即给定N个互不重复的元素,求其所有可能的排列方案,则方案数量为:
N×(N−1)×(N−2)×⋯×2×1=N !
如下图与代码所示,阶乘常使用递归实现,算法原理:第一层分裂出N个,第二层分裂出N-1个,……,直至到第N层时终止并回溯。
def algorithm(N):
if N <= 0: return 1
count = 0
for _ in range(N):
count += algorithm(N - 1)
return count
对数阶与指数阶相反,指数阶为“每轮分裂出两倍的情况”,而对数阶是“每轮排除一半的情况”。对数阶常出现于二分法、分治等算法中, 体现着“一分为二”或“一分为多”的算法思想。
设循环次数为m,则输入数据大小N与2m呈线性关系,两边同时取log2对数,则得到循环次数m与log2N呈线性关系,即时间复杂度为O(logN)。
def algorithm(N):
count = 0
i = N
while i > 1:
i = i / 2
count += 1
return count
如以下代码所示,对于不同 a 的取值,循环次数 m 与 loga N 呈线性关系 ,时间复杂度为 O(loga N)。而无论底数 a 取值,时间复杂度都可记作 O(logN) ,根据对数换底公式的推导如下:
O(loga N) = O(log2 N)/O(log2 a) = O(log N)
def algorithm(N):
count = 0
i = N
a = 3
while i > 1:
i = i / a
count += 1
return count
如下图所示,为二分查找的时间复杂度示意图,每次二分将搜索区间缩小一半。
两层循环相互独立,第一层和第二层时间复杂度分别为 O(log N) 和 O(N) ,则总体时间复杂度为 O(N logN) ;
def algorithm(N):
count = 0
i = N
while i > 1:
i = i / 2
for j in range(N):
count += 1
return count
线性对数阶常出现于排序算法,例如「快速排序」、「归并排序」、「堆排序」等,其时间复杂度原理如下图所示。
本文转自:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/r81qpe/