文章目录

  • 1.常用数据结构与算法
  • 2.复杂度分析
  • 大O复杂度表示法
  • 时间复杂度
  • 空间复杂度


1.常用数据结构与算法

数据结构是指一组数据的存储结构。算法就是操作数据的一组方法。
数据结构与算法相辅相成,数据结构为算法服务,算法要作用在特定的数据结构上。

  • 常用数据结构:
  • 数组、链表、栈、队列
  • 散列表
  • 二叉树、堆、 图
  • 跳表
  • Trie树
  • 常用算法:
  • 递归
  • 排序
  • 二分查找
  • 搜索
  • 哈希算法
  • 贪心算法
  • 分治算法
  • 回溯算法
  • 动态规划
  • 字符串匹配算法

对于以上常用的数据结构预算法,在这里仅作简单记录,后续学习过程中会对每一种数据结构及算法单独写文章记录。

2.复杂度分析

设计优异的数据结构能够节省更多的空间,而优异的算法能够节省更多的时间。因此,学习数据结构与算法的核心就是尽可能让代码运行的更快、更省存储空间。

大O复杂度表示法

  • 公式:T (n) = O (f(n)) ,其中T(n) 代表的是代码的执行总时间,f(n)表示的是每行代码执行的次数总和。而公式中的O 表示代码的执行时间T (n) 与 f(n)表达式成正比。ps:n代表的是每行代码的执行次数
  • 大O时间复杂度并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,也叫做渐进时间复杂度,简称时间复杂度。
  • 示例:
public static int calSum(int n) {
        int sum = 0;	//执行1次
        int i = 1;		//执行1次
        int j = 1;		//执行1次
        for (; i <= n; i++) {	//执行n次
            j = 1;		//执行n次
            for (; j <= n; j++) {	//执行n*n次
                sum += i * j;	//执行n*n次
            }
        }
        return sum;
    }

假设每行代码执行都需要1个unit_time时间,则以上代码执行所需时间T(n) = (2n2 + 2n + 3)*unit_time,即f(n) = 2n2 + 2n + 3

时间复杂度

如何分析一段代码的时间复杂度?

  1. 只关注循环执行次数最多的一段代码
public static void calSum2(int n) {
        int sum = 0;	//只执行1次
        for (int i = 0; i < n; i++) {	//执行n次
            sum += i;	//执行n次
        }
    }

如上代码中,int sum = 0; 只执行1次且与n无关,for循环中的代码执行了n次,所以总的时间复杂度就是 O(n).

  1. 加法法则:总复杂度等于量级最大的那段代码的复杂度
public static int calSum(int n) {
        int sum = 0;
        int i = 1;
        int j = 1;
        // 循环1
        for (int k = 0; k < 1000; k++) {
            sum += k;
        }
        //循环2
        for (int k = 0; k < n; k++) {
            sum += k;
        }
        //循环3
        for (; i <= n; i++) {
            j = 1;
            for (; j <= n; j++) {
                sum += i * j;
            }
        }
        return sum;
    }

上述代码中,循环1执行了1000次,是一个常量的执行时间,与n的规模没有关系。循环2的时间复杂度为O(n),循环3的时间复杂度伪O(n2)总复杂度等于量级最大的那段代码的复杂度,因此以上代码的时间复杂度为O(n2)。
即:T1(n) = O(f(n)),T2(n) = O(g(n));
则T(n)=T1(n)+T2(n)=max(O(fn),O(g(n)))=O(max(f(n),g(n)));
3.乘法法则: 嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

参照加法法则,如果T1(n)=O(f(n)),T2(n)=O(g(n));
则 T(n)=T1(n)*T2(n)=O(f(n))*O(g(n))=O(f(n)*g(n));

public static void calSum2(int n) {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += getSum(i);
        }
    }
    public static int getSum(int n){
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum+=i;
        }
        return sum;
    }

上述代码中calSum2() 方法时间复杂度为O1(n),方法getSum() 时间复杂度也是O2(n),由于方法getSum()被嵌套在calSum2()方法中调用,因此calSum2()方法的时间复杂度为O(n) = O1(n)*O2(n) = O(n2);
4. 常见时间复杂度

  • 时间复杂度表

常见时间复杂度

自上而下递增

常量阶

O(1)

对数阶

O(logn)

线性阶

O(n)

线性对数阶

O(nlogn)

平方阶

O(n2)

立方阶

O(n3)

k次方阶

O(nk)

指数阶

O(2n)

阶乘阶

O(n!)

  • 常见复杂度分析
  • 常量阶O(1)
    如下代码中,执行时与数量级n无关,则其时间复杂度为O(1);只要代码的执行时间不随n的增大而增大,则其时间复杂度都是O(1);一般情况下,只要代码中没有循环语句、递归语句,即是代码量再多,其时间复杂度也是O(1);
int m = 10;
int k = 6;
int sum = m * k;
  • 对数阶O(log(n))
public static int getSum(int n) {
        int sum = 0, i = 1;
        while (i < n) {
            sum += i;
            i = i * 3;
        }
        return sum;
    }

在上述代码中,当i>=n时推出循环,每次循环i都乘3,假设循环次数为k,则i=3k;达到循环退出的临界值时i >= n ,即3k >= n,等号两边同时取以3为底的对数,可得 k = 数据结构与算法 prim 数据结构与算法设计_时间复杂度,即以上代码的时间复杂度为数据结构与算法 prim 数据结构与算法设计_时间复杂度_02

ps:对数之间是可以互相转换的,数据结构与算法 prim 数据结构与算法设计_数据结构与算法 prim_03 = 数据结构与算法 prim 数据结构与算法设计_数据结构_04 * 数据结构与算法 prim 数据结构与算法设计_数据结构与算法 prim_05 = 数据结构与算法 prim 数据结构与算法设计_数据结构_04 * 数据结构与算法 prim 数据结构与算法设计_复杂度_07 ,而在计算时间复杂度的时候,系数是可以忽略不计的,所以数据结构与算法 prim 数据结构与算法设计_数据结构与算法 prim_03 可以看做是数据结构与算法 prim 数据结构与算法设计_时间复杂度_09;同理,在计算时间复杂度的时候,不管是以几为底的对数,都可以写作O(数据结构与算法 prim 数据结构与算法设计_时间复杂度_09)

  • 线性对数阶O(nlogn)
    如果一段代码的时间复杂度是数据结构与算法 prim 数据结构与算法设计_时间复杂度_09,而这段代码又执行了n遍,那么总的时间复杂度就是数据结构与算法 prim 数据结构与算法设计_数据结构与算法 prim_12.如下代码所示:
public static int getSum(int n) {
        int sum = 0, i = 1;
        for (int j = 0; j < n; j++) {
            while (i < n) {
                sum += i;
                i = i * 3;
            }
        }
        return sum;
    }
  • O(m+n)、O(m * n)
    这种形式的时间复杂度是由两个数据规模决定的。我们无法判断m和n哪一个数据量级大,因此在计算时间复杂度的时候无法使用加法法则省略掉其中一个,因此时间复杂度会出现O(m+n)的情况。但是乘法法则同样是适用的.

补充:

  • 最好情况时间复杂度:在最理想的情况下,执行这段代码的时间复杂度
  • 最坏情况时间复杂度:在最糟糕的情况下,执行这段代码的时间复杂度
  • 平均时间复杂度:最好、最坏情况出现的概率极低,为了将各种情况都考虑进去,引入平均时间复杂度。
  • 均摊时间复杂度

空间复杂度

  • 空间复杂度分析可以参照时间复杂度分析方法使用大O表示法表示;空间复杂度表示算法的存储空间与数据规模之间的增长关系。
  • 常见的空间复杂度有:O(1)、O(n)、O(n2),对数阶复杂度基本用不到

文章仅用作学习过程记录,如有不当,欢迎交流指正~