一序
不从数学角度来看公式推导,只看程序中。
不考虑前面的系数。
二 常见的7中时间复杂度
Big O notation
- O(1):Constant Complexity 常数复杂度
- O(log n):Logarithmic Complexity 对数复杂度
- O(n):Linear Complexity 线性时间复杂度
- O(n^2):N Square Complexity 平方
- O(n^3):N Square Complexity 立方
- O(2^n):Exponential Growth 指数
- O(n!):Factorial 阶乘
举个例子:
int n= 1000;
System.out.println(n);
这个是O(1):
那么:
int n= 1000;
System.out.println(n);
System.out.println("bohu83:"+n);
System.out.println("test:"+n);
也是O(1),虽然执行3次,但是不关心系数。
int n= 1000;
for(int i=1;i<n;i++){
System.out.println("at:"+i);
}
这种就是线性时间复杂度。
int n= 1000;
for(int i=1;i<n;i++){
for(int j=1;j<n;j++) {
System.out.println("at:" + i);
}
}
O(n^2)
注意这种并列的情况:
int n= 100;
for(int i=1;i<=n;i=i*2){
System.out.println("at:" + i);
}
O(log n)
public static int fib(int n){
if(n<2) {
return n;
}
return fib(n-1)+fib(n-2);
}
O(k^n) k是常数,可以理解为2,它是指数级的。
n<5时差别不太大,随着N的增长,增长差异明显不同。
三 demo
3.1计算:1+2+…+n:
方法1:暴力循环O(N)
public static int sum(int n){
int res =0;
for(int i=1;i<=n;i++){
res=res+i;
}
return res;
}
方法2:
public static int sum1(int n){
return n*(n+1)/2;
}
这个时间复杂度就是O(1),不要被n*n迷惑。是执行了一次。
常见的阶梯四步骤:
1 。确认题目要求
2. 列出所有肯能的解法。
3. 比较:删选出最优:时间最短。
4.写程序,验证测试case。
3.2 复杂的情况:
试着画出递归树(状态树)
Fib:0,1,1,2,3,5,8,13,21,…
- F(n) = F(n-1) + F(n-2)
简单的写法:
int fib(int n){
if(n<2) {
return n;
}
return fib(n-1)+fib(n-2);
}
观察图,发现的规律:
- 每展开一层,运行的节点数就是上层的两倍,按指数级递增(从根节点开始算起)
- 存在重复计算的节点
常会上运行45:1134903170use:4667
优化下:1134903170use:0
代码如下:
public static void main(String[] args) {
int n = 45;
long start = System.currentTimeMillis();
int res = fibCache(n);
System.out.println(res + "use:" + (System.currentTimeMillis() - start));
}
static int[] cache = new int[100];
public static int fib(int n) {
if (n < 2) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
public static int fibCache(int n) {
if (cache[n] != 0) {
return cache[n];
}
if (n < 2) {
cache[n] = n;
return n;
} else {
cache[n] = fibCache(n-1)+fibCache(n-2);
return cache[n] ;
}
}
3.3Master Theorem
主定理是用来解决递归的时间复杂度的,常用的有下面的四种。
一维数组二分查找:每次排除一半,故为O(log n),一般是数组本身有序的情况下
二叉树的遍历:每次分成一半,每一半是对等的。上面第二条,可以理解成每个节点被访问且仅访问一次,故为O(n)
二维矩阵的二分查找:有序的矩阵进行二分查找,被降了1维,不是N^2,是O(n)
归并排序:O(n logn)
问题
1 二叉树遍历:前序、中序、后序的时间复杂度是多少?
O(n)
这里的N代表二叉树的节点总数。
2 图的遍历:时间复杂度是多少?
同理,所有节点遍历一次且进遍历一次也是O(N),N也是图的节点总数
3.搜索算法:DFS、BFS的时间复杂度是多少?
也是O(N),这里深度优先、广度优先都是跟上面一样的,N是搜索空间的节点总数。
4 二分查找:时间复杂度是多少?
O(log n)
四 空间复杂度
1. 数组的长度
2.递归的深度。
老师找了LeetCode的70
https://leetcode.com/problems/climbing-stairs/
举例子,其实就是前面数列 的问题。
如果 使用哪个默认的递归且无优化的情况,时间复杂度就是O(2^n),空间复杂度就是状态树的深度O(N).
如果使用了缓存优化:代码参见上面。
时间复杂度是: O(n)
空间复杂度:树的深度O(n)
如果使用了动态规划,dp用了一维数组来实现。
那么 时间复杂度是: O(n)
空间复杂度:数组的长度O(n)
还可以更加优化空间复杂度,就是使用两个变量替代了数组,这样能使空间复杂度优化到O(1)
如果你之前不了解过动态规划,可以看看网上的文章,动手写下代码。直接看这种结论是难理解的。
最重要就是多加练习,虽然我自己就做不到这一点