目录
1.前言
2.究竟什么是大O?
3.数据规模的概念
4.常见的复杂度的分析:
5.时间复杂度的测试
6.递归算法的时间复杂度:(最主要的是要计算递归的深度)
7.均摊复杂度的分析:
如果对于一个算法来说n是他的数据规模,O(f(n))则表示运行算法所需要执行的指令数,和f(n)成正比。
时间复杂度O 衡量的是一个量级上的差距,这种量级上的差距表现在当n突破了一个点的时候,时间复杂度底的算法就一定比时间复杂度高的算法要快。而且n越大这个优势就越明显
所以当数据规模比较小的时候,前面的这个常数是有意义的。在学术界严格的来讲,O(f(n))表示的是算法执行的上界。在业界,我们就使用O来表示算法执行的最低上界。
例题:
这个题有两个维度的变量 一个是数组内字符串的长度,一个是数组有多少个元素,这两个变量毫无关联。
空间复杂度:
O(n)级别的算法有一个很明显的特征就是有一个循环,并且循环的次数是和n相关的。
所执行的指令数和n^2是成正比关系的
二分查找的时间复杂度就是O(logn)
分析:
例子:
由于增量的特殊性,第一重循环将循环logn次,sz+=sz 其实就是sz=sz*2,就是n经过几次除以2等于1 所以时间复杂度为O(n*logn)
有没有什么算法帮助我们来判断我们写的程序到底是一个什么级别的算法?数据规模每次都翻倍,观察消耗时间。
时间复杂度为O(n)的算法
// O(n) public static void test1(int n) { int i; int sum; for (i = 0; i < n; i++) { /*时间复杂度为O(1)的程序*/ sum = (1 + i) * i / 2; } }
测试代码:
int n = 12; for (int i = 1; i < n; i++) { int pow = (int) Math.pow(2, i); long before = System.nanoTime(); test1(pow); long after = System.nanoTime(); long time = after - before; System.out.println("数据规模为2的" + i + "次方,消耗的时间为" + time); }
结果:
从2的6次方开始,数据规模增大一倍,所消耗的时间增大一倍
其他复杂度的算法也可以按照这种方式测试
不是所有的递归算法的时间复杂度都是O(nlogn),对于递归算法时间复杂度要具体问题具体分析
比如二分查找法:在每一次递归中,只进行了一次的递归调用,他的递归调用的深度就是logn,因为每次都对查找的数据减少一半,还有就是在这个函数中处理问题的时间复杂度是O(1) 的,所以整个算法的时间复杂度就是O(logn)
继续分析:
每次递归减少一个元素,递归的深度为0,所以这是一个O(n)的算法
继续分析下面一个计算x的n次方的案例:这个函数的每次的数据规模都除以2,他的递归深度就是logn,函数中也没有其他复杂的操作,所以这个函数的时间复杂度为O(logn)
通常情况下,我们计算x的n次方是用n个x连乘,这个时间复杂度是O(n)的,我们做了这样的改进后就相当于把时间复杂度优化为了O(logn)
如果递归函数中一次调用存在两个递归那又该怎么计算时间复杂度呢?此时我们真正关心的是递归调用的次数。
这个时候我们可以画出递归树:
我们可以计算出这颗递归树有多少个节点:
所以这个递归的时间复杂度就是O(2^n)级别的
继续分析归并排序的时间复杂度:首先8个元素的时候他的递归树的层级是3而不是8,层级或者深度就是logn,对于每一层都是一样的多的元素,每层总体的元素都是8,每个节点都是O(n)级别的算法.所以总体来说每一层也都是O(n)级别的算法。最终整个算法的时间复杂度就是O(nlogn)