分析算法时间复杂度的基本方法
1.找出语句频度最大的那条语句作为基本语句;
2.计算基本语句的频度,得到问题规模n的某一个函数;
3.取其数量级用O表示
忽略所有低次幂项和最高次幂的系数,这样可以简化算法分析,也体现出了增长率的含义。
- 常数阶
实际上,如果算法的执行时间不随问题规模n的增加而增长,算法中语句频度就是某个常数。即使这个常数再大,算法的时间复杂度都是O(1)。
- 线性阶
给小灰一个长度为10cm的面包,小灰每三分钟吃掉1cm,那么他吃掉整个面包要多久?
答案自然是3*10=30min
如果面包的长度为n cm呢?
此时吃掉整个面包需要3*n即3n分钟。
如果用一个函数来表达吃掉整个面包所需要的时间可以记作T(n)=3n
n表示面包的长度即处理的数据的规模
- 对数阶
给小灰一个长度为16cm的面包,小灰小灰每5min吃掉面包剩余长度的一半,第1min吃掉8cm,第2min吃掉4cm,第三min吃掉2cm…那么小灰把面包吃得只剩下1cm,需要多少天呢?
这个问题翻译一下,就是数字16不断地除以2,除几次以后的结果等于1?这里要涉及到数学当中的对数,以2位底,16的对数,可以简写为log216。(下文对函数的底数全部省略)
因此,把面包吃得只剩下1寸,需要 5log16=54=20 min
如果面包的长度为n cm呢?
此时吃掉整个面包需要5logn分钟记作T(n)=5logn
也可以给n几个具体的值找规律
- 平方阶
由于当i=0时内循环执行n次,当i=1时内循环执行n-1次,…,当i=n-1时内循环执行1次总执行次数
n+(n-1)++(n-2)+…+1=n(n+1)/2
时间复杂度是O(n^2)
- 立方阶
不太知道怎么算可以复习下级数求和
- 最好、最坏和平均时间复杂度
有的情况算法的基本操作重复执行的次数还随问题输入的数据集不同而不同
最好的情况a0=e执行1次
最坏数组中没有e/an-1=e执行n次
而对于一个算法来说,需要考虑各种可能出现的情况,以及每一种情况出现的概率,一般情况下,可假设待查找的元素在数组中所有位置上出现的可能性均相同。类似于数学中求期望值。计算每一种情况执行次数与概率的乘积在求和。
最坏时间复杂度是指在最坏情况下算法的的复杂度;
最好时间复杂度是指在最好情况下算法的的复杂度;
平均时间复杂度是指算法在所有可能情况下,按照输入实例以等概率出现时,算法计算量的加权平均值。
通常考虑最坏和平均但有时平均比较难计算只考虑最坏时间复杂度,最坏情况运行时间是一种保证,那就是运行时间不会再坏了。
计算公式
如果时间复杂度是平方阶最好降低到对数阶实在不行平方阶也可以接受,立方阶也尚可。
- 算法的空间复杂度:算法要占据的空间
- 算法本身要占据的空间:输入/输出、指令、常数、变量等。
- 算法要使用的辅助空间。
若输入数据所占据的空间只取决于问题本身和算法无关,这样只需分析该算法在实现时所需的辅助单元即可,若算法执行时所需的辅助单元相对于输入数据量而言是个常数,则称此算法为原地施工,空间复杂度为O(1)
- 时间与空间的取舍
举个例子说,要判断某年是不是闰年,你可能会花一点心思来写一个算法,每给一个年份,就可以通过这个算法计算得到是否闰年的结果。
另外一种方法是,事先建立一个有2050个元素的数组,然后把所有的年份按下标的数字对应,如果是闰年,则此数组元素的值是1,如果不是元素的值则为0。这样,所谓的判断某一年是否为闰年就变成了查找这个数组某一个元素的值的问题。
第一种方法相比起第二种来说很明显非常节省空间,但每一次查询都需要经过一系列的计算才能知道是否为闰年。第二种方法虽然需要在内存里存储2050个元素的数组,但是每次查询只需要一次索引判断即可。
这就是通过一笔空间上的开销来换取计算时间开销的小技巧。到底哪一种方法好?其实还是要看你用在什么地方。
但在绝大多数情况下,时间复杂度更重要一些,我们宁愿多分配一些内存空间也要提升程序的执行速度。