算法入门准备:学习笔记 时间复杂度

一序

  不从数学角度来看公式推导,只看程序中。

 不考虑前面的系数。

二 常见的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,它是指数级的。


1570721349041.png

 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);
    }

2019-10-10-23-41-14.jpg

观察图,发现的规律:

  1. 每展开一层,运行的节点数就是上层的两倍,按指数级递增(从根节点开始算起)
  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

主定理是用来解决递归的时间复杂度的,常用的有下面的四种。

1570722438634.png

一维数组二分查找:每次排除一半,故为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)

如果你之前不了解过动态规划,可以看看网上的文章,动手写下代码。直接看这种结论是难理解的。


最重要就是多加练习,虽然我自己就做不到这一点

猜你喜欢

转载自blog.csdn.net/bohu83/article/details/107010322