算法复杂度分析-集大成篇

目录

1.  算法复杂度分析总结

1.1   复杂度分析法则

1.2   时间复杂度 ( Time Complexity )

1.2.1   常数阶 O(1)

1.2.2   对数阶 O (log N)

1.2.3   线性阶 O ( N )

1.2.4   线性对数阶 O ( N logN )

1.2.5   平方阶 O ( N^2 )

1.2.6   立方阶 O ( N^3 ) 

1.2.7   O ( m + n) 和 O( m * n )

1.3   空间复杂度 ( Space Complexity )

2.  浅析最好、最坏、平均和均摊时间复杂度

2.1   最好和最坏情况时间复杂度

2.2   平均情况时间复杂度

2.3   均摊时间复杂度

2.3.1   伪代码

2.3.2   代码 

3.  经典面试题

4.  总结

参考书籍:极客时间的 《数据结构与算法之美》


1.  算法复杂度分析总结

        复杂度分析,它几乎占了数据结构与算法这门课的半壁江山,是数据结构和算法的精髓。时间复杂度和空间复杂度的分析有助于我们产出高质量的代码,能区分一流工程师和三流的工程师。

        我们可以粗略地把复杂度分析分为两类多项式量级非多项式量级。其中非多项式量级只有两个:O(2^{N})和 O( N ! )。

       我们把时间复杂度为非多项式量级的算法问题叫做 NP(Non-Deterministic Polynomial, 非确定多项式)问题。当数据规模 N 越来越大时,非多项式量级算法执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是非常低效的。

1.1   复杂度分析法则

加法法则:如果 T1 ( N ) = O ( f( N )) 且 T2 ( N ) = O ( g(n) ),

                  那么,T1 ( N ) T2 ( N ) = max ( O ( f( N ) ,O ( g(n) ) )。

                  即,总复杂度等于量级最大的那段代码的复杂度。

乘法法则:如果 T1 ( N ) = O ( f( N )) 且 T2 ( N ) = O ( g(n) ),

                  那么,T1 ( N ) * T2 ( N ) = O ( f( N ) O ( g(n)  )。

                   即,嵌套代码的复杂度等于嵌套内外代码复杂度的乘积。

优先法则:只关注循环执行次数最多的一行代码。 

1.2   时间复杂度 ( Time Complexity )

1.2.1   常数阶 O(1)

        O (1) 只是常量级时间复杂度的一种表示方法,并不是指只执行了一行的代码。比如这段代码,即便只有 3 行,它的时间复杂度是也是 O ( 1 ),而不是 O ( 3 )。

int i = 3;
int j = 3;
int sum = i + j;

        只要代码的执行时间不随 N 的增大而增长,这样代码的时间复杂度我们都记作 O ( 1 )。或者说,一般情况下,只要算法中不存在循环语句和递归语句,即使有成千上万行的代码,其时间复杂度也是 O ( 1 )。 

1.2.2   对数阶 O (log N)

        对数阶时间复杂度非常常见,同时也是最难分析的一种时间复杂度。我通过一个例子来说明一下。

i = 1;
while( i <= n )
{
    i = i * 2;
}

        根据我们前面讲的复杂度分析算法,第三行代码是循环次数最多的。所以,我们只要能计算出这行代码被执行了多少次,就能知道整段代码的时间复杂度。

        从代码中可以看出,变量 i 的值从 1 开始取,每循环一次就乘以 2。当 i > n 时,循环结束。还记记得高中学过的等比数列吗?实际上,变量 i 的取值就是一个等比数列。如果我把他一个一个列出来,就应该是这个样子的:

        所以,我们只要知道 X 的值,就知道这行代码执行的次数,从而算出整段代码的时间复杂度。故 2^{X}=N 求解 X ,解出 X=\log_{2}N ,即这段代码的时间复杂度就是 O( \log_{2}N )

        现在,我把代码稍微改一下,你在看看,这段代码的时间复杂度是多少?

i = 1;
while( i <= n)
{
    i = i * 3;
}

        根据刚刚讲的思路,很简单就能看出来,这段代码的时间复杂度为 O(\log_{3}N)

        由对数换底公式可得:

        所以 O(\log_{3}N)=O(C*\log_{2}N) , 其中 C=\log_{3}2 是一个常量。在采用大 O 标记复杂度的时候,可以忽略系数,即 O(Cf(n))=O(f(n)) ,故 O(\log_{2}N)=O(\log_{3}N)  。因此,在对数阶的时间复杂度表示方法里,我们忽略对数的 “底” ,统一表示为 O(\log_{}N) 。

        所以,不管是以 2 为底、以 3 为底还是以 10 为底,我们可以把所有对数阶的时间复杂度都记为 O(\log_{}N) 。

1.2.3   线性阶 O ( N )

        代码执行 N 次,其算法时间复杂度为线性阶,其最经典的例子就是数组的遍历。

for( int i = 0; i < N ; i++)
{
    printf("%d",arr[ i ]);
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aperion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值