数据结构与算法——概述与复杂度

344 阅读2分钟

1. 什么是数据结构

数据结构是计算机存储,组织数据的方式。

  • 线性结构:线性表,具有n个相同类型元素的有序列(n>=0)(数组,链表,栈,队列,哈希表)

  • 树形结构:二叉树,AVL树,红黑树,B树,堆,Trie,哈夫曼树,并查集

  • 图形结构:邻接矩阵,邻接表

在实际应用中,根据使用场景来选择最合适的数据结构

2. 什么是算法

算法是用来解决特定问题的一系列的执行步骤

    //计算a+b的和
    func plus(_ a:Int,_ b:Int) -> Int {
        a+b
    }
    //计算1+2+3+4+5+...+n的和
    func  sum(n:Int) -> Int {
        var result:Int = 0;
        for _ in 0...n {
            result += n
        }
        return result
    }
  • 使用不同的算法解决同一个问题,效率可能相差非常大
  • 比如求第n个斐波那契数

3. 如何评价算法的好坏?

  • 如果单从执行效率上进行评估,可能会想到这么一种方案
    • 比较不同算法对同一组输入的执行处理时间。也叫事后统计法
    • 执行时间会严重依赖于硬件以及运行时各种不确定的环境因素
    • 必须编写相应的测算代码
    • 测试数据的选择比较难保证公正性
  • 所以一般以以下维度来评估算法的优劣
    • 正确性,可读性,健壮性(对不合理输入的反应能力和处理能力)
    • 时间复杂度:估算程序指令的执行次数(执行时间)
    • 空间复杂度:估算所需占用的存储空间

3.1 大O表示法

一般用大O表示法来描述复杂度,它表示的是数据规模n对应的复杂度

  • 忽略常数,系数,低阶,对数阶一般忽略底数
    • 9 >> O(1)
    • 2n+3 >> O(n)
    • n^2+2n+6 >> O(n^2)
    • 4n^3+3n^2+22n+100 >> O(n^3)
    • log2n = log29 ∗ log9。所以 log2n 、log9n 统称为 logn。
  • 注意,大O表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率

3.2 常见的复杂度

执行次数复杂度非正式术语
12O(1)常数阶
2n+3O(n)线性阶
4n^2 +2n+6O(n^2)平方阶
4log2^n + 25O(log^n)对数阶
3n + 2nlog3^n + 15O(nlog^n)nlogn阶
4n^3 +3n2 +22n+100O(n^3)立方阶
2^nO(2^n)指数阶
  • O(1) < O(log^n) < O(n) < O(nlog^n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
  • 可以借助函数生成工具对比复杂度的大小 zh.numberempire.com/graphingcal…

数据规模较小时

数据规模较大时

3.2 斐波那契数列fib1函数的时间复杂度分析

  • 递归法
    func fib1(_ index:Int64) -> Int64 {
        if index <= 1 {
            return index;
        }
        return fib1(index-1) + fib1(index-2)
    }//时间复杂度O(2^n)

  • 迭代法
    func fib2(_ n:Int64) -> Int64 {
        if n <= 1 {
            return n
        }
        var first:Int64 = 0
        var second:Int64 = 1
        for _ in 0...n-2 {
            let sum:Int64 = first + second
            first = second
            second = sum
        }
        return second
    }//时间复杂度O(n)
  • 代数公式法(并不是所有的算法都公式可以直接解决),时间复杂度O(1)
  • 递归法和迭代法的差别有多大(n为64)
    • 如果有一台1GHz的普通计算机,运算速度为10^9次每秒
    • O(n)大约耗时6.4*10^-8秒
    • O(2^n)大约耗时584.84年
    • 有时候算法之间的差距,往往比硬件方面的差距还要大

4. 多个数据规模的情况下

    func test(_ n:Int,_ k:Int){
        for _ in 0...n {
            print("test")
        }
        
        for _ in 0...k {
            print("test")
        }
    }

复杂度O(n+k)

5. 算法的优化方向

  • 用尽量少的存储空间
  • 用尽量少的执行步骤(时间)
  • 根据情况,可以:时间换空间,空间换时间

更多复杂度的知识

  • 最好,最坏复杂度。
  • 均摊复杂度。
  • 复杂度震荡
  • 平均复杂度 *......