8 时间复杂度
8 时间复杂度
常见的时间复杂度量级有:常数阶O(1),对数阶O(logn),线性阶O(n),线性对数阶O(nlogn),平方阶O(n2 ),立方阶O(n3 ),K次方阶O(n k ),指数阶O(2n )。他们的时间复杂度越来越大,执行的效率越来越低。
下面选取一些较为常用的来讲解一下。
常数阶O(1)
代码执行次数是一个常数,不随n的变化而变化,那这个代码的时间复杂度就都是O(1),如下的代码中虽然含有for循环,但循环次数是100次,不随问题规模变化而变化,因此是常数级O(1)的时间复杂度:
for(int i = 1; i <= 100; i++){
sum += i;
}
线性阶O(n)
这段代码,for循环里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度。
for(int i = 1; i <= n; i++){
sum += i;
}
对数阶O(logn)
同样是for循环,但这段代码的时间复杂度是O(logn),因此不能单纯认为for循环就一定是O(n)的。
for(int i = 1; i <= n; i *= 2){
cnt++;
}
在这个for循环里,i 开始是1,然后不是自增1,而是自乘2,因此i的值依次是1、2、4、8......2x ,也就是当2的x次方大于n时,跳出循环,因此x =logn, 程序执行了
logn次,时间复杂度为:O(logn)
线性对数阶O(nlogn)
线性对数阶其实非常容易理解,将时间复杂度为O(logn) 的代码循环n遍的话,那么它的时间复杂度就是n×O(logn)。
就拿上面的代码加一点修改来举例:
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j *= 2){
cnt++;
}
}
平方阶O(n2 )
如果把O(n)的代码再嵌套循环一遍,它的时间复杂度就是 O(n 2 ) 了。
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cnt++;
}
}
其实冒泡排序的时间复杂度也是O(n2),以下代码中,当i=1时,执行n-1次,i=2时,执行n-2次......因此总的执行次数也就是 (n-1)+(n-2)...+2+1就是n*(n-1)/2,显然n2的量级更大,因此常数项和n/2可以忽略,时间复杂度即为O(n2):
for(int i =1; i<=n; i++){
for(int j=1; j<=n-i; j++){
if(a[j] > a[j+1])
swap(a[j], a[j+1]);
}
}
阶乘O(n!)
比如用枚举的方法求1~n的全排列,时间复杂度是 O(n!),效率很低。
练习
8-1
给定一个含N 个不相同数字的数组,在最坏情况下,找出其中最大或最小的数,至少需要N−1 次比较操作。则最坏情况下,在该数组中同时找最大与 最小的数至少需要( )次比较操作。(⌈ ⌉表示向上取整,⌊ ⌋表示向下取整)
A. ⌈3N/2⌉−2
B.⌊3N/2⌋−2
C. 2N−2
D. 2N−4
8-2
基于比较的排序时间复杂度的下限是( ),其中n表示待排序的元素个数。
A. O(n)
B.O(nlogn)
C. O(logn)
D. O(n 2 )
8-3
冒泡排序算法的伪代码如下:
输入:数组L, n ≥ k。输出:按非递减顺序排序的 L。
算法 BubbleSort:
1. FLAG ← n //标记被交换的最后元素位置
2. while FLAG > 1 do
3・ k ← FLAG -1
4・ FLAG ← 1
5・ for j=1 to k do
6. if L(j) > L(j+1) then do
7・ L(j) ↔ L(j+1)
8・ FLAG ← j
对n 个数用以上冒泡排序算法进行排序,最少需要比较多少次?( )。
A. n2
B. n−2
C. n−1
D. n
8-4
以比较作为基本运算,在N个数中找出最大数,最坏情况下所需要的最少的比较次数为( )。
A.N 2
B.N
C.N−1
D.N+1
常见算法的时间复杂度
算法
二分查找(Binary Search):O(logn)
二分查找算法每次将搜索区间缩小一半,因此时间复杂度为O(log n)。
倍增法(Exponentiation by Squaring):O(log n) 倍增法用于快速计算幂,如 a^n。每次迭代将幂指数减半,因此时间复杂度为O(log n)。
深度优先搜索(Depth-First Search,DFS):O(V+E) 深度优先搜索遍历图的时间复杂度为O(V+E),其中V表示顶点数,E表示边数。
广度优先搜索(Breadth-First Search,BFS):O(V+E) 和深度优先搜索类似,广度优先搜索遍历图的时间复杂度为O(V+E),其中V表示顶点数,E表示边数。
快速排序(Quick Sort):平均情况下 O(n log n),最坏情况下 O(n^2) 快速排序算法的平均时间复杂度为O(n log n),但最坏情况下(极度不平衡的划分)可能达到O(n^2)。
归并排序(Merge Sort):O(n log n) 归并排序算法的时间复杂度为O(n log n),因为它将数组分成两半,并递归地排序它们,然后将它们合并。
迪杰斯特拉(Dijkstra):O((n+m)logn),是一种用于求解单源最短路径的贪心算法,常用于带有非负权重边的图。
动态规划(Dynamic Programming):时间复杂度因问题而异 动态规划算法的时间复杂度取决于具体问题和子问题的数量。通常,动态规划算法会填充一个表格,时间复杂度与填充该表格所需的操作次数成正比。
函数
sqrt(n):计算n 的平方根。时间复杂度为O(logn),采用二分法等快速算法实现。
sort(a, a+n):对数组 a 进行排序。时间复杂度为 O(nlogn),采用快速排序、归并排序等常用排序算法实现。
__gcd(a, b):计算 a 和b 的最大公约数。时间复杂度为O(log min(a,b)),采用辗转相除法实现。
pow(x, n):计算x 的n 次方。时间复杂度为 O(logn),采用快速幂算法。
abs(x):返回 x 的绝对值。时间复杂度为O(1)。
log(x)、log10(x)、log2(x):计算x 的自然对数、以 10 为底的对数、以 2 为底的对数。时间复杂度为 O(1)。
char数组
以下是常见的一些 char 数组函数及其时间复杂度:
strlen(a):返回字符串 a 的长度。时间复杂度为 O(n)。
strcpy(a, b):将字符串 b复制到 a中。时间复杂度为O(n),其中n 为字符串 b的长度。
strcat(a, b):将字符串 a连接到 b的末尾。时间复杂度为 O(n),其中n 为字符串 a的长度。
strcmp(a, b):比较字符串 a和 b的大小关系。时间复杂度为O(n),其中 n 为字符串 a和 b的长度的较小值。
string
string是一个常用的字符串类,以下是几个常用函数的时间复杂度:
length()/size()😮(1),即常数时间复杂度。这是因为字符串的长度是在构造函数中计算得到的,并且在字符串的操作过程中一直被维护着。
append(): O(n),其中 n 是要追加的字符串的长度。因为要将追加的字符串复制到原字符串的末尾,所以时间复杂度为 O(n)。
insert()😮(n),其中 n 是要插入的字符串的长度。因为要将插入位置后面的字符串往后移动,所以时间复杂度为 O(n)。
erase(): O(n),其中 n 是要删除的字符串的长度。因为要将删除位置后面的字符串往前移动,所以时间复杂度为 O(n)。
find(): O(n),其中 n 是字符串的长度。因为 find() 函数需要遍历整个字符串来查找子串,所以时间复杂度为 O(n)。
递归函数复杂度分析
在分析递归函数的时间复杂度时,我们需要考虑以下因素:
- 每次递归调用的工作量。
- 递归的深度(调用的次数)。
- 每一层递归中的分支数。
通常,我们使用递归树来分析递归算法的时间复杂度。具体的时间复杂度取决于递归算法的实现细节。
我们来看一个简单的例子:计算斐波那契数列的递归实现。斐波那契数列的第n项可以用以下公式表示:
F(n)=F(n−1)+F(n−2),其中F(0)=0,F(1)=1。
下面是一个计算斐波那契数列的递归函数:
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n - 1) + fib(n - 2);
}
为了分析这个递归函数的时间复杂度,我们可以画出它的递归树。每个节点表示一个递归调用,子节点表示从该调用产生的递归调用。这里是树的前几层(每个节点显示调用 fib(x)的x值):
fib(4)
/ \
fib(3) fib(2)
/ \ / \
fib(2) fib(1) fib(1) fib(0)
/ \
fib(1) fib(0)
观察递归树,我们可以看到每层递归的分支数大约是2,深度是n。因此,这个递归函数的时间复杂度大约是O(2n)。请注意,这是一个非常低效的实现,可以使用动态规划或矩阵乘法将时间复杂度降低到O(n)或O(logn)。
练习
8-5
某算法的计算时间表示为递推关系式T(n)=T(n−1)+n(n 为正整数)及
T(0)=1,则该算法的时间复杂度为( )。
A. O(logn)
B. O(nlogn)
C. O(n)
D. O(n2)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能