美文网首页数据结构与算法
数据结构第一季 Day01 算法复杂度

数据结构第一季 Day01 算法复杂度

作者: 望穿秋水小作坊 | 来源:发表于2021-02-24 07:53 被阅读0次

一、 初识算法

1. 算法用来做什么?解决同一个问题,不同的算法效率可能差距会很大吗?
  • 算法是用来解决特定问题的一系列的执行步骤
  • 不同的算法效率,差距是巨大的
    // 计算 a 跟 b 的和
    public static int plus(int a, int b) {
        return a + b;
    }
    // 计算 1+2+3+4+...+n 的和
    public static int sum(int n) {
        int result = 0;
        for (int i = 1; i < n; i++) {
            result += i;
        }
        return result;
    }
2. 斐波那契数列的计算,递归思路的代码如下,会有什么问题?
    /*
     *  0 1 1 2 3 5 8 13 ...
     */
    public static int fib1(int n) {
        if (n <= 1) {
            return n;
        }
        return fib1(n - 1) + fib1(n - 2);
    }
  • 这应该是最容易想到的算法了,但是,我们看到当计算 64 的时候,程序已经住了,算不出来结果了。
3. 斐波那契数列的计算,我们换一种求和算法的代码如下,依然会有问题吗?
    /*
     *  0 1 1 2 3 5 8 13 ...
     */
    
    public static int fib2(int n) {
        if (n <= 1) return n;
        
        int first = 0;
        int second = 1;
        int sum = 0;
        for (int i = 1; i < n; i++) {
            sum = first + second;
            first = second;
            second = sum;
        }
        return sum;
    }
  • 这个计算速度就非常快了,即使 n = 64 也能瞬间计算出来
4. 思考,如果是你,你会怎么量化的对比两个算法的效率?
  • 写一个时间统计的工具类
package com.lsp;

public class Times {
    public interface Block {
        void runBlock();
    }
    
    public static void execute(String taskName,Block block) {
        long startTime = System.currentTimeMillis();
        block.runBlock();
        long endTime = System.currentTimeMillis();
        System.out.println("===========================");
        System.out.println("【" + taskName + "】");
        System.out.println("耗时:" + (endTime - startTime) / 1000.0 + "秒");
    }
}
  • 调用时间统计的工具类
    public static void main(String[] args) {
        int n = 43;
        Times.execute("fib1", new Block() {
            @Override
            public void runBlock() {
                fib1(n);
            }
        });
        Times.execute("fib2", new Block() {
            @Override
            public void runBlock() {
                fib2(n);
            }
        });
    }
  • 输出结果如下
===========================
【fib1】
耗时:1.703秒
===========================
【fib2】
耗时:0.0秒
  • 这种方案也叫做:事后统计法

二、 算法复杂度

1. 上一个方案评判算法复杂度有什么明显缺点?如何评判一个算法的好坏(从一个前提、两个维度来说)?
  • 上述方案的明显缺点:
  • ① 执行时间严重依赖硬件以及运行时各种不确定的因素
  • ② 必须编写响应的测算代码
  • ③ 测试数据的选择比较难保证公正性
  • 评判算法的好坏:
  • 一个前提:正确性、可读性、健壮性
  • 两个维度:
  • 时间复杂度: 估算程序指令的执行次数(执行时间)
  • 空间复杂度: 估算所需占用的存储空间
2. 用你的知识,计算一下下面代码的执行次数?我发现 log 这个函数,需要再去巩固下,忘记了~~
  • 问题如下
    public static void test1(int n) {
        if (n > 10) { 
            System.out.println("n > 10");
        } else if (n > 5) { // 2
            System.out.println("n > 5");
        } else {
            System.out.println("n <= 5"); 
        }
        
        for (int i = 0; i < 4; i++) {
            System.out.println("test");
        }
    }

    public static void test2(int n) {
        for (int i = 0; i < n; i++) {
            System.out.println("test");
        }
    }

    public static void test3(int n) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                System.out.println("test");
            }
        }
    }

    public static void test4(int n) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < 15; j++) {
                System.out.println("test");
            }
        }
    }

    public static void test5(int n) {
        while ((n = n / 2) > 0) {
            System.out.println("test");
        }
    }

    public static void test6(int n) {
        while ((n = n / 5) > 0) {
            System.out.println("test");
        }
    }

    public static void test7(int n) {
        for (int i = 1; i < n; i = i * 2) {
            // 1 + 3n
            for (int j = 0; j < n; j++) {
                System.out.println("test");
            }
        }
    }

    public static void test10(int n) {
        int a = 10;
        int b = 20;
        int c = a + b;
        int[] array = new int[n];
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i] + c);
        }
    }
  • 答案如下
    public static void test1(int n) {
        // 汇编指令
        
        // O(1)
        if (n > 10) { 
            System.out.println("n > 10");
        } else if (n > 5) { // 2
            System.out.println("n > 5");
        } else {
            System.out.println("n <= 5"); 
        }
        
        // 1 + 4 + 4 + 4
        for (int i = 0; i < 4; i++) {
            System.out.println("test");
        }
        
        // 140000
        // O(1)
    }

    public static void test2(int n) {
        // O(n)
        // 1 + 3n
        for (int i = 0; i < n; i++) {
            System.out.println("test");
        }
    }

    public static void test3(int n) {
        // 1 + 2n + n * (1 + 3n)
        // 1 + 2n + n + 3n^2
        // 3n^2 + 3n + 1
        // O(n^2)
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                System.out.println("test");
            }
        }
    }

    public static void test4(int n) {
        // 1 + 2n + n * (1 + 45)
        // 1 + 2n + 46n
        // 48n + 1
        // O(n)
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < 15; j++) {
                System.out.println("test");
            }
        }
    }

    public static void test5(int n) {
        // 8 = 2^3
        // 16 = 2^4
        
        // 3 = log2(8)
        // 4 = log2(16)
        
        // 执行次数 = log2(n)
        // O(logn)
        while ((n = n / 2) > 0) {
            System.out.println("test");
        }
    }

    public static void test6(int n) {
        // log5(n)
        // O(logn)
        while ((n = n / 5) > 0) {
            System.out.println("test");
        }
    }

    public static void test7(int n) {
        // 1 + 2*log2(n) + log2(n) * (1 + 3n)
        
        // 1 + 3*log2(n) + 2 * nlog2(n)
        // O(nlogn)
        for (int i = 1; i < n; i = i * 2) {
            // 1 + 3n
            for (int j = 0; j < n; j++) {
                System.out.println("test");
            }
        }
    }

    public static void test10(int n) {
        // O(n)
        int a = 10;
        int b = 20;
        int c = a + b;
        int[] array = new int[n];
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i] + c);
        }
    }
3. 大 O 表示法是忽略了三个什么因素?对数要注意什么?
  • 一般用大 O 表示法描述复杂度,它表示的是数据规模 n 对应的复杂度
  • 忽略 常数、系数、低阶
  • 注意:大 O 表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的好坏。
image.png image.png
4. 常见的复杂度(至少列举三个)?
image.png
5. 实战:自行计算斐波那契数列两种算法的时间复杂度,你能算出来吗?
image.png
  • 有时候算法之间的差距,往往比硬件的差距还要大

相关文章

网友评论

    本文标题:数据结构第一季 Day01 算法复杂度

    本文链接:https://www.haomeiwen.com/subject/hfvdfltx.html