复杂度分析

1 为什么要对算法进行复杂度分析?

数据结构与算法就是为了解决如何更省的存储数据、如何更快的处理数据的问题,那么就必须有一个准则对“更快”、“更省”进行判断。

ok,可以,既然咱们要分析算法的执行时间和运行时占用的内存,那好办呀,咱们就在Visual Studio上把代码跑一遍就可以了。

                                                            

这种测试方法当然是对的,但是对于算法的复杂度分析来说,却有两个缺陷:一是这种测试方法对测试环境依赖很严重,例如同一段代码在i3和i9上运行,i9上跑的i3快;二是输入数据的不确定性,输入数据的规模和有序度都会影响算法的执行时间。

所以才有了空间、时间复杂度分析。

 

2 什么是时间复杂度、空间复杂度?

2.1 时间复杂度:测试运行时间就是计算对运行时间有消耗的基本操作的执行次数,那么我们可以假设一个前提,每一行代码执行的时间(baseTime)是一样的。例如,

 1 long add(int n)
 2 {
 3     int i = 1;
 4     int j = 1;
 5     long sum = 0;
 6     
 7     for ( , i < n, ++i)
 8     {
 9         for (, j < n, ++j)
10             sum += i*j;
11     }
12 
13     return sum;
14 }

外层循环每执行一次,内层循环执行n次,所以我们知道总共执行次数f(n) = 2n2+n+4,总共执行时间T(n) = f(n) * baseTime,也可以表示成T(n) = O(f(n)),‘O’表示总执行时间和执行语句数量f(n)成正比,叫做大O复杂度表示法。

大O复杂度并不具体表示代码真正的执行时间,而是代码执行时间随数据输入规模增长的变化趋势。记录时我们只需关注最大量级即可,上述例子就可表示成O(n2)。

2.3 时间复杂度分析技巧

1) 只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是O(1);

2) 只关注循环次数最多的一段代码;

3) 加法原则:总复杂度等于量级最大的那段代码的复杂度;

4) 乘法原则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积。

2.4 常见的时间复杂度

O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

2.5 空间复杂度

与时间复杂度类似,空间复杂度表示的是代码执行过程中所需存储空间随数据输入规模增长的变化趋势,同样可以用大O表示法表示。这里所需的存储空间是说除了输入数据存储空间外,算法运行还需额外的存储空间。 

 1 ListNode* removeNthFromEnd(ListNode* head, int n) {
 2     vector<ListNode*> nodeVector;    //创建一个节点容器
 3     ListNode* pWorkNode = head;
 4     int i = 0;    //链表长度
 5         
 6     while (pWorkNode)    //遍历链表
 7     {
 8         nodeVector.push_back(pWorkNode);    //将各个节点放入容器中
 9         pWorkNode = pWorkNode->next;
10         ++i;
11     }
12         
13     int j = i + 1 - n;    //待删除节点序号
14     if (1 == j)    //如果删除的是第一个节点
15     {
16         pWorkNode = head->next;
17         delete head;
18         head = pWorkNode;
19     }
20     else
21     {
22         nodeVector[j - 2]->next = nodeVector[j - 1]->next;
23         delete nodeVector[j - 1];
24     }
25         
26     return head;
27 }

该例子中在运行过程中创建了一个长度为n的容器,空间复杂度为O(n)。

 

3 最好情况时间复杂度、最坏情况时间复杂度和平均情况时间复杂度

1) 最好情况时间复杂度:最理想的情况下,执行这段代码的时间复杂度。

2) 最坏情况时间复杂度:最糟糕的情况下,执行这段代码的时间复杂度。

3) 平均情况时间复杂度:平均情况下的时间复杂度。

一般只有在同一块代码在不同的情况下,时间复杂度有量级的差距,才会使用这三种复杂度表示法来区分。

 

4 均摊时间复杂度

摊还分析法:将特殊耗时多的那次或几次操作均摊到剩下的n次耗时少的操作上。大多数情况下,均摊时间复杂度都等于最好情况复杂度。

适用场景:对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系,这个时候,我们就可以将这一组操作放在一起分析,看是否能将较高时间复杂度的那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上。

posted @ 2019-04-07 14:05  zpchya  阅读(272)  评论(0编辑  收藏  举报