时间复杂度分析

复杂度分析

    • 简介
    • 为什么需要复杂度分析
    • 大O复杂度表示法
    • 时间复杂度分析
      • 只关注循环执行次数最多的一段代码。
      • 加法法则:总复杂度等于量级最大的那段代码的复杂度
      • 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂的的乘积
    • 几种常见的时间复杂度实例分析
    • O(1)
    • O(logn)
    • O(m+n),O(m*n)

简介

数据结构和算法本身解决的是快和省的问题
衡量执行效率的就需要时间空间复杂度分析

复杂度分析是整个算法学习的精髓,只有掌握了他,数据结构和算法的内容就基本掌握了一半。

为什么需要复杂度分析

1)测试结果依赖测试环境
2)测试结果受数据规模的影响很大

所以结合上述两种情况,就需要一个不用具体的测试数据来测试,就可以粗略的估算算法的执行效率的方法。也就是时间空间复杂度。

大O复杂度表示法

从CPU的角度来看,每一行代码的操作都是读数据-运算-写数据
即,可以把一行代码的时间看作一个 unit_time时间单位
如下
例子1

public class HelloWorld {
    public static void main(String[] args) {
        
    }

    public static int cal(int n){
        int sum = 0;          // unit_time 一个时间单位
        int i = 1;            // unit_time  一个时间单位
        for (; i <= n; ++i) { // unit_time * n  n个时间单位
            sum = sum + 1;    // unit_time * n  n个时间单位
        }
        return sum;
    }
}

上述时间单位综合为 1+1+n+n
即(2n+2)* unit_time

由此,所有代码的执行时间T(n)与每行代码的执行次数成正比

依照上述思路 看下个
例子2

    public static int cal(int n){
        int sum = 0;               // unit_time 一个时间单位
        int i = 1;                 // unit_time 一个时间单位
        int j = 1;                 // unit_time 一个时间单位
        for (; i <= n; ++i) {      // unit_time * n  n个时间单位
            j=1;                   // unit_time * n  n个时间单位
            for (; j <= n; ++j) {  // unit_time * n * n   n2个时间单
                sum = sum + i * j; //  unit_time * n * n  n2个时间单位
            }
        }
        return sum;
    }

所以以上的总时间就是1+1+1+n+n+n2+n2 = 3+2n+2n2
即总共的耗时是3+2n+2n2个时间单位。

以上可以得出 所有代码的执行时间T(n)与每行代码的执行次数成正比。
总结公式就是
T(n)= O(f(n))
T(n) :代码执行所需要的时间
f (n):表示每行代码执行的次数总和。
O表示代码执行时间和次数总和是成正比的。即需要时间越长,执行的次数越多。

例子1 2n+2
例子2 3+2n+2n2
公式化:T(n) = O(2n+2)
公式化:T(n) = O(3+2n+2n2)

以上就是大O表示法了,大O表示法表示的是 代码执行时间随数据规模增长的变化趋势

在公式中低阶,常量,系数三部分并不左右增长趋势。只需要记录一个最大量级就可以了。

公式化:T(n) = O(2n+2)
公式化:T(n) = O(3+2n+2n2)

以上公式化内容写成以下即可
T(n) = O(n);
T(n) = O(n2);

时间复杂度分析

只关注循环执行次数最多的一段代码。

大O表示法,表示的是趋势,同时会省略掉公式中的常量,低阶,系数,并且只需要记录最大量级。

分析一个算法,代码就是只关注循环次数最多的代码那一段代码就可以了。这一段最多的执行次数的量级就是这段代码的时间复杂度

例子1

    public static int cal(int n) {
        int sum = 0;
        int i = 1;
        for (; i <= n; ++i) {
            sum=sum+1;
        }
        return sum;
    }

以上例子执行次数最多的就是for循环,与n的大小无关。for循环执行了n次,所以这段代码的时间复杂度就是O(n);

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

例子1

    public static int cal(int n) {
        int sum_1 = 0;
        int p = 1;
        for (; p < 100; ++p) {
            sum_1 = sum_1 + p;
        }
        
        int sum_2 = 0;
        int q = 1;
        for (; q < n; ++q) {
            sum_2 = sum_2 + q;
        }

        int sum_3 = 0;
        int i = 1;
        int j = 1;
        for (; i <= n; ++i) {
            j = 1;
            for (; j <= n; ++j) {
                sum_3 = sum_3 + i * j;
            }
        }
        
        return sum_1 + sum_2 + sum_3;
    }

以上代码分为三个部分
sum_1 部分 sum_2部分 sum_3部分

sum_1 循环执行100次常量级 T(n) = O(n);
sum_2 循环执行n次常量级 T(n) = O(n);
sum_3 循环执行n2次指数级 T(n) = O(n2);

取出最大量级,所以例子1的时间复杂度就是O(n2);

抽象成公式:
T1(n) = O(f(n)) , T2(n) = O(g(n)) 两个代码执行时间 等于整个方法的执行时间

T(n) = T1(n) + T2(n)
但是取最大的时间作为时间复杂度
即T(n) = max(O(f(n)),O(g(n)))

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

若假设T1(n) = O(n),T2(n) = O(n2) 则 T1(n) * T2(n) = O(n3)
在代码中的体现就是嵌套循环
例子1

    public static int cal(int n) {
        int ret = 0;
        int i = 1;
        for (; i < n; ++i) {
            ret = ret + f(i);
        }
        return ret;
    }

    private static int f(int n) {
        int sum = 0;
        int i = 1;
        for (; i < n; ++i) {
            sum = sum + 1;
        }
        return sum;
    }

上述代码在循环中调用f方法 f方法的本身的时间复杂度是O(n)
cal如果不看f方法,时间复杂度也是O(n) 但是在cal中调用了f
所以整个cal方法的时间复杂度就是Tcal(n) *Tf(n) = O(n2)

几种常见的时间复杂度实例分析

复杂度量级 公式
常量级 O(1)
对数级 O(logn)
线性级 O(n)
线性对数级 O(nlogn)
平方阶 O(n2)
立方阶 O(n3)
K次方阶 O(nk)
指数阶 O(2n)
阶乘阶 O(n!)

以上只有指数阶,阶乘阶,是非多项式量级
其余的都是多项式量级
时间复杂度是非多项式量级叫做NP问题。
因为随着n的规模增加,执行时间会急剧增加,执行时间会无限增长。

O(1)

一般情况下,只要算法中不存在循环语句,递归语句,即便成千上万行代码,时间复杂度都是O(1)常量阶

O(logn)

例子1

    public static int cal(int n) {
        int i = 1;
        while (i <= n) {
            i = i * 2;
        }
        return i;
    }

以上代码循环次数最多的是while
i = 2 +2平方 + 2立方 + …2x
求x = log2n
即上述时间复杂度就是O(logn)

O(m+n),O(m*n)

这种时间复杂度与上述的不同,他的代码的复杂度,是由两个数据的规模来决定的
例子1

    public static int cal(int m, int n) {
        int sum_1 = 0;
        int i = 1;
        for (; i < m; ++i) {
            sum_1 = sum_1 + i;
        }

        int sum_2 = 0;
        int j = 1;
        for (; i < n; ++i) {
            sum_2 = sum_2 + i;
        }

        return sum_1 + sum_2;
    }

例子中传入m和n两个数据规模,没有办法评估谁大谁小。
所以上述代码的时间复杂度就是O(m+n)
针对上述情况,原来的加法法则不正确,公式改为O(f(m)+g(n)).

从 低 到 高
O(1),O(logn),O(n),O(nlogn),O(n2)。

你可能感兴趣的:(算法,算法)