目录
一、时间复杂度
2. 代码中时间复杂度的计算
二、空间复杂度
执行算法所需的计算工作量。一般来说,计算机算法是问题规模n的函数f(n),算法的时间复杂度也因此记做T(n)=O(f(n))
计算时间复杂度的规则:
举个栗子
int sum=0; -执行一次 sum+=1; -执行一次 System.out.println(sum); -执行一次 这个算法的运行次数函数是f(n)=3。根据规则: 第一步把常数项3改为1 -> 在保留最高阶项时发现,它没有最高阶。所以最终时间复杂度为O(1),也就是常数阶① 规则的原理
观察下图,当f(n)中的n不断增大时,常数项以及其他次要项对f(n)的影响几乎可以忽略不计
② 常见的时间复杂度
③ 常见的时间复杂度的耗时曲线
运行次数函数(举例) 时间复杂度 6n+5 O(n) - 线性阶 6n^2+6n+6 O(n^2) - 平方阶 5lg N+20
O(lgN) - 对数阶 2n+3n*lgN+6 O(n*lgN) - nlgN阶 6n^3+2n^2+n+6 O(n^3) - 立方阶 2^n O(2^n) - 指数阶 ![]()
![]()
2 计算fun2的时间复杂度
分析:整个代码执行次数为1+2*N+1+10,函数表达式f(n)=2*N+13。根据规则:略去尾数13 和系数2后,没有最高阶。最终得到时间复杂度为O(N),关键的代码其实就是for循环
答案:O(N)
2 计算func3的时间复杂度
分析:第一个循环进行O(M)次,第二个循环进行O(N)次,两次循环共同完成该函数操作。 代码执行次数为1+M+N+1次,函数表达式f(n)=M+N+2。根据规则略去常数项...
答案:O(M+N)
:计算时间复杂度最关键的就是循环部分,接下来我们关注循环吧~
分析:内循环第一题已经分析出是O(n),而外循环就是将内循环这个时间复杂度为O(n)的语 句再循环n次。总共循环次数也就是n个n相加求和,n+n+n+...+n=[(n+n)*n]/2=n^2
所以这段代码的时间复杂度就是O(n^2)
答案:O(n^2)
2 将上一题外循环的循环次数改成m,计算该段循环嵌套代码的时间复杂度
分析:内循环是O(n),外循环就是将内循环这个时间复杂度为O(n)的语句再循环m次。所以 同理计算出这段代码的时间复杂度就是O(M*N)
答案:O(m*n)
:经过总结得出,循环的时间复杂度等于该循环体的时间复杂度乘以该循环运行的次数
2 那下面这个循环嵌套体的时间复杂度是多少
分析:内循环j=i,受外部循环控制
当i=0时,内循环执行了n-1次,
当i=1时,内循环执行了n-2次,
... ...
当i=n-1时,内循环执行了1次
总的循环次数为:(n-1)+(n-2)+...+1=n*(n+1)/2=(n^2)/2+n/2
根据规则,得出最后的时间复杂度为O(n^2)
答案:O(n^2)
:经过观察发现,即使内循环受外循环控制,执行次数不断变化且没有n次,但是这些变动的数值比较小,仍然可以忽略不计
2 计算下列代码的时间复杂度
分析:由于每次count乘以2之后,就距离n更近了一分。也就是说,有多少个2相乘后大于 n,就会退出循环。由2^x=n得到x=lgN,所以这个循环的时间复杂度就是O(lgN)
答案:O(lgN)
计算下列代码的时间复杂度
分析:function函数的时间复杂度是O(1)。for循环调用时间复杂度为O(1)的函数,其实相当 于for循环调用一个时间复杂度为O(1)的循环。分析方式和循环嵌套是一致的
答案:O(1)
假设function是这样的,计算下列代码的时间复杂度
分析:function函数的时间复杂度是O(n)。for循环调用时间复杂度为O(n)的函数,其实相当 于for循环调用一个时间复杂度为O(n)的循环。时间复杂度为O(n^2)
答案:O(n^2)
:经过观察发现,分析函数的调用的时间复杂度和循环嵌套是一致的
分析:根据上面的总结,结合冒泡排序的思想。可以直接得出时间复杂度为O(n^2)
该段冒泡代码中总共有n个数字参与循环排序,所以第一个for循环表示有n轮循环;
每一轮当中每个数字参与比较,有n个数字,所以第二个for循环表示的是n个数字;
经过计算得出的时间复杂度就是O(n^2)
答案:O(n^2)
计算二分查找(binarySearch)的时间复杂度
分析:最坏情况是,找到最后只剩一个数字即begin==end的时候。二分查找,每找一次,循 环次数n减一半成n/2。所以问题就转化为:每查找一次循环次数减少1/2,运行多少 次后,n最终降为1。列出数学表达式:(1/2)*x=1 -> x=logN(通常忽略下角标2)
答案:O(logN)
分析:递归数列时间复杂度公式:递归的次数a*每次递归后代码段执行的次数b。这里的b始 终是1,只需求出a即递归了几次就行。n -> n-1 -> n-1-1 -> ... ->1,从1~n递归n次
答案:O(N)
计算斐波那契递归fibonacci的时间复杂度
分析:画图,斐波那契数列的递归次数如下图
递归的次数a=n
每次递归后代码段执行的次数b=2^n-1(等比数列求和)
总的执行次数=a*b,时间复杂度=总的执行次数函数->规则=O(2^n)
答案:O(2^n)
:对代码使用规则,比计算出整段代码的执行次数函数后,对执行次数函数使用规则更快
计算代码的时间复杂度时,对于复杂的代码要先学会从中看到简单的组成部分:
无非就是 单句代码段+各种循环+循环的并列出现+嵌套出现+函数调用 组成
再从单个组成部分看到本质:
冒泡排序 -> 两个for循环的循环嵌套;二分查找 -> for循环的对数阶 ...
看看下面这段复杂的代码,如何快速的计算出时间复杂度
图中标注了每段代码的执行次数,实际上我们不这么做。我们直接关注for循环和函数调用:
函数调用的时间复杂度是O(n);
第一个for循环->外循环时间复杂度为O(n),内循环时间复杂度是O(n).最终复杂度为O(n^2);
第二个for循环->忽略细微改变,就是两个for循环嵌套,时间复杂度都是O(n),最终为O(n^2)
n与n^2,随着n不断增大,n对n^2产生的影响微乎极微,所以最终时间复杂度为O(n^2)
空间复杂度比较简单,下面通过两个实例看一看空间复杂度是什么?怎么计算?
代码1:for循环的循环次数与num没有关系。num只占int空间的大小,不管num加了多少i,都只在 这个int字节的空间内存上,其数值+1。此时,空间复杂度就是O(1)
代码2:for循环的循环次数与array有关。最开始array为null,随着循环次数增加,数组中存放的元 素增加。此时,空间复杂度就是O(N)
注意:
其实就是看变量,变量空间是否随着循环次数的增加而增加
注意递归情况。虽然代码中没有明显的变量空间的增加,但是递归时,每一次的循环都需 要记住上一次的执行结果。所以,实际上,递归算法内部会有递归栈存储上一次的数值。 递归算法的算法时间复杂度也是O(N)
-- 特此说明:本文借用大话数据结构中的少数代码,如侵则删
-- 如果觉得有用,拜托一键三连以资鼓励嗷!