02. 算法复杂度(上)

1. 概念

  1. 数据结构和算法本身解决的是“快”和省得问题,即如何让代码运行得更快,运行时更省内存空间。
  2. 如何衡量代码的执行效率?时间和空间复杂度。

2.事后统计法

事后统计法:运行代码,通过统计、监控就能得到算法执行的时间和占用的内存大小

缺点:

  1. 测试结果非常依赖环境

  2. 测试结果搜数据规模影响很大

    我们需要一个不用具体的跑代码,就可以粗略地估算算法的执行效率的方法,这就是时间、空间复杂度。

3.大O复杂度表示法

3.1 概述

算法的执行效率,粗略地将,就是算法代码执行的时间

大O时间复杂度实际上并不具体表示具体代码真正的执行时间,而是表示代码执行时间随着数据规模增长的变化趋势,所以也叫做渐进时间复杂度(asymptotic time complexity,简称时间复杂度

3.2 时间复杂度分析方法

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

private static int cal(int n) {
        // 总复杂度 2n + 2 -> O(n)
        int sum = 0; // 1
        int i = 1; // 1
        for(; i <= n; ++i) { // n
            sum += i; // n
        }

        return sum;
}

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

private static int cal3(int n) {
        int sum1 = 0;
        int p = 1;

        // 总  2n² + 4n + 1 -> O(n²)

        // 1 O(1)
        for(; p <= 100; ++p) { // 100
            sum1 += p; // 100
        }

        // 2n O(n)
        int sum2 = 0;
        int q = 1;
        for (; q <= n; ++q) { // n
            sum2 += q; // n
        }

        // 2n² + 2n  -> O(n²)
        int sum3 = 0;
        int i = 1;
        int j = 1;

        for(; i <=n; ++i) { // n
            j = 1; // n
            for(; j <=n; ++j) { // n²
                sum3 = sum3 + i * j; // n²
            }
        }

        return sum1 + sum2 + sum3;
    }

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

private static int cal4(int n) {
        int ret = 0;
        int i = 1;
        // 总: O(n)*O(n) = O(n²)
        for(; i < n; ++i) { // n -> O(n)
            ret += f(i); // n
        }

        return ret;
    }

    /**
     * O(n)
     * @param n
     * @return
     */
    private static int f(int n) { // 2n -> O(n)
        int sum = 0;
        int i = 1;
        for(; i < n; ++i) { // n
            sum += i; // n
        }

        return sum;
    }

3.3 几种常见复杂度实例分析

3.3.0 常用复杂度

  • 常量阶 O(1)
  • 对数阶 O(logn)
  • 线性阶 O(n)
  • 线性对数阶 O(nlogn)
  • 平方阶 O(n²), 立方阶 O(n³) …
  • 指数阶 O(2^n)
  • 阶乘阶 O(n!)
  • 以上复杂度量级,可以分为 多项式量级非多项式量级,其中 O(2^n) 和 O(n!)为非多项式量级。当数据规模n越来越大师,非多项式量级算法的执行时间会急剧增加,求解问的的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是翡翠低效的算法。下图为例,选取了
    y=50^x, y=2^x , y=x , y=x!的曲线图:
    在这里插入图片描述

3.3.1 O(1)

/**
 * 一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是O(1).
 * O(1)
 * @return
 */
private static int o1() {
    int sum = 0;
    int i = 1;
    for(; i < 1000000; ++i) { // n
        sum += i; // n
    }

    return sum;
}

一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是O(1).

3.3.2 O(n)

/**
     * O(n)
     * @param n
     * @return
     */
    private static int f(int n) { // 2n -> O(n)
        int sum = 0;
        int i = 1;
        for(; i < n; ++i) { // n
            sum += i; // n
        }

        return sum;
    }

3.3.3 O(logn)

/**
     * O(logN)
     * 等比数列
     * 3¹,3²,3³,...
     * 即 3^x = n, x即循环次数, 所以 x = log(3)N,
     * 由换底公式 log(a)b = log(c)b / log(c) a;
     * 所以 log(3)N = log(2)N / log(3)2 = log(100)N / log(3)100 = log(x)N / log(3) x. <br/>
     * 其中 log(3)x为 为常量,忽略, -> N -> ∞ ,log(3)N = log(x)N, 统一记为 O(logN).
     * @param n
     * @return
     */
    private static int cal5(int n) {
        int i = 1, cnt = 0;
        while (i <= n) {
            i = i*3;
            cnt ++;
        }

        System.out.println("i/n=" + cnt + "/" + n);
        return i;
    }

3.3.4 O(nlogn)

/**
     * O(nlogn)
     * @param n
     */
    private static void cal6(int n) {
        // 总 O(n)*O(logN) -> O(nlogn)
        for(int i = 1; i <= n; i++) { // n -> O(n)
            int j = 1; // n
            while (j <= i) { // log(2)N -> O(logN)
                j = j * 2; // log(2)N
            }
        

3.3.5 O(m+n)

/**
     * O(m+n)
     * @param m
     * @param n
     * @return
     */
    private static int cal7(int m, int n) {
        int sum1 = 0;
        int i = 1;
        // O(m)
        for(; i < m; ++i) { // m
            sum1 = sum1 + i; //m
        }

        int sum2 = 0;
        int j = 1;
        // O(n)
        for(; j < n; ++j) { // n
            sum2 = sum2 + j; // n
        }

        return sum1 + sum2;
    }

3.3.6 O(m*n)

/**
     * O(m*n)
     * @param m
     * @param n
     * @return
     */
    private static void cal7mn(int m, int n) {
        int total = 0;
        // 2m*n + m -> O(m*n)
        for(int i= 0; i < m; ++i) { // m
            for(int j=0; j<n;j++) { // m*n
                total = i*j; // m*n
            }
        }
    }

3.4 空间复杂度

3.4.1 概念

与时间复杂度一样,也表示执行时间或空间与数据规模之间的增长关系,定位为:
空间复杂度:全称为渐进空间复杂度(aysmptotic space complexity), 表示算法的存储空间与数据规模之间的增长关系。

3.4.2 几种常见的复杂度

3.4.2.1 O(1)
/**
     * O(1)
     * 空间复杂度
     */
    private static void space1() {
        int i = 0;
        int[] a = new int[10000]; // 100 -> O(1)
        for(; i<10000; ++i) {
            a[i] = i * i;
        }

        for(int j=99; j >=0; j--) {
            System.out.println(a[j]);
        }
    }
3.4.2.2 O(n)
/**
     * O(n)
     * 空间复杂度
     * @param n
     */
    private static void space2(int n) {
        int i = 0;
        int[] a = new int[n]; // n -> O(n)
        for(; i<n; ++i) {
            a[i] = i * i;
        }

        for(int j=n-1; j >=0; j--) {
            System.out.println(a[j]);
        }
    }
3.4.2.3 O(n²)
/**
     * O(n²)
     * 空间复杂度
     * @param n
     */
    private static void space3(int n) {
        List<Integer> list = new ArrayList<>();

        // 总 n² -> O(n²)
        for(int i=0; i<n; i++) { // n
            for(int j=0; j<i; j++) { // n²
                list.add(new Random().nextInt()); // n²
            }
        }
    }
3.4.2.4 O(logn)
/**
     * O(logN)
     * 空间复杂度
     * @param n
     */
    private static void space4(int n) {
        List<Integer> list = new ArrayList<>();

        // 总 logN -> O(logN)
        int i = 1;
        while (i <= n) { // logN
            i = i * 2;  // logN
            list.add(new Random().nextInt());  // logN
        }
    }
3.4.2.5 O(nlogn)
/**
     * O(nlogN)
     * 空间复杂度
     * @param n
     */
    private static void space5(int n) {
        List<Integer> list = new ArrayList<>();

        // 总 nlogN -> O(nlogN)
        for(int i=0; i<n; i++) {// n
            int j = 1; // n
            while (j <= i) { // logN
                j = j * 2;  // logN
                list.add(new Random().nextInt());  // logN
            }
        }
    }

3.4.3 空间复杂度小结

​ 空间复杂度较时间复杂度简单很多,常见空间复杂度为 O(1), O(n), O(n²), 像O(logn), O(nlogn)这样的对数阶复杂度基本上用不到。

4. 各种函数复杂度曲线

对数

注:本文课程来自极客时间

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值