数据结构

在计算机科学中

  • 数据:是对客观事物的符号表示,是能输入到计算机并被计算机程序处理的符号的总称。
  • 数据元素:是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理。
  • 数据项:一个数据元素由若干个数据项组成,数据项是构成数据的最小单位
  • 数据对象:是具有相同性质的数据元素的集合(强调的是数据元素性质的相同)。

在这里插入图片描述

  • 数据结构:是相互之间存在一种或多种特定关系的数据元素的集合(强调的是数据元素之间的关系)。数据结构的三要素是逻辑结构、存储结构和数据运算:

在这里插入图片描述

上图中存储结构是依据数据在物理存储器上存储的位置进行描述的,如果在高级语言(如C语言)层面上进行描述,那么可以使用数组类型来描述顺序存储结构、用指针来描述链式存储结构。

  • 数据类型:数据类型是一个值的集合和定义在此集合上的一组操作的总称。
    • 原子类型:其值不可再分的数据类型
    • 结构类型:其值可以分解为若干成分的数据类型。
  • 抽象数据类型:是抽象数据组织及与之相关的操作。

算法

算法是为求解一个问题需要遵循的、被清楚指定的有限指令集合。一个算法具有以下五个特性:

  • 有穷性:一个算法必须在执行有穷步之后结束,且每一步都在有穷时间内完成。
  • 确定性:算法中的每一条指令都必须有明确的含义,没有二义性,对相同的输入只能得到相同的输出。
  • 可行性:算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现。
  • 输入:一个算法有零个或多个输入。
  • 输出:一个算法有一个或多个输出。

一个算法的设计取决于所选定的逻辑结构,而算法的实现依赖于所采用的存储结构。

算法分析

一旦给定一个算法并且确认其是正确的,那么重要的一步就是对这个算法进行算法分析,算法分析主要考虑算法的效率,算法效率包含以下两方面:

  • 时间效率:指算法所消耗的时间,用时间复杂度来度量。
  • 空间效率:指算法执行过程中所消耗的存储空间。用空间复杂度来度量。

时间复杂度

一个算法运行的时间大致可以等于计算机执行算法指令的时间( t t t)与指令执行次数( s s s)的乘积。
T ( n ) = ∑ i = 1 n t i × s i T(n)=\sum_{i=1}^{n} t_i\times s_i T(n)=i=1nti×si

由于每个指令执行的时间由计算机的硬件和软件条件决定,和算法本身没有关系,因此可以将每个指令执行的时间可以看作一个单位时间,那么讨论算法执行时间的问题就变成了讨论算法执行频度的问题:

T ( n ) = ∑ i = 1 n s i T(n)=\sum_{i=1}^{n} s_i T(n)=i=1nsi

下面分析一下选择排序算法的执行频度:

public static void selectionSort(int []data){
    for (int i = 0; i < data.length-1; i++) { //n次
        int minIndex=i; //n-1次
        for (int j = i+1; j < data.length; j++) { //n(n-1)/2次
            if (data[j]<data[minIndex]){ //n(n-2)/2次
                minIndex=j; //n(n-2)/2次
            }
        }
        int temp=data[i]; //n-1次
        data[i]=data[minIndex]; //n-1次
        data[minIndex]=temp; //n-1次
    }
}

所以选择排序的执行频度为:
T ( n ) = 3 n 2 ÷ 2 + 5 n ÷ 2 − 4 T(n)=3n^2\div2+5n\div2-4 T(n)=3n2÷2+5n÷24

但是这一过程也相当的繁琐并且结果难以比较,由公式
lim ⁡ x → ∞ a n x n + a n − 1 x n − 1 + ⋯ + a 1 x + a 0 b m x m + b m − 1 x m − 1 + ⋯ + b 1 x + b 0 = { a n b m , n = m 0 , n < m ∞ , n > m \lim\limits_{x\to\infty}\frac{a_nx^n+a_{n-1}x^{n-1}+\cdots+a_1x+a_0}{b_mx^m+b_{m-1}x^{m-1}+\cdots+b_1x+b_0}=\begin{cases}\frac{a_n}{b_m},n=m\\0,n<m\\\infty,n>m\end{cases} xlimbmxm+bm1xm1++b1x+b0anxn+an1xn1++a1x+a0= bman,n=m0,n<m,n>m
可知,如果存在一个函数 f ( n ) f(n) f(n),使 lim ⁡ n → ∞ T ( n ) f ( n ) = C ( C ≠ 0 ) \lim\limits_{n \to \infty}\frac{T(n)}{f(n)}=C(C\ne0) nlimf(n)T(n)=C(C=0)那么 f ( n ) f(n) f(n) T ( n ) T(n) T(n)就是同阶的,记作 T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n)) O ( f ( n ) ) O(f(n)) O(f(n))就称为算法的时间复杂度。那么上述选择排序的时间复杂度就为 O ( n 2 ) O(n²) O(n2)。也就是说在寻找算法频度时不需要找出所有语句执行的频度,只需要找出对算法频度贡献最大的语句即可。此外,输入数据的性质会影响算法的执行频度,在这种情况下将算法的时间复杂度分为三种:

  • 最坏时间复杂度:输入数据的性质使算法的执行频度很高。
  • 平均时间复杂度:输入数据的性质使算法的执行频度很平均。
  • 最好时间复杂度:输入数据的性质使算法的执行频度很低。

但一般只考虑最坏时间复杂度,以保证算法的运行时间不会比它更长。在计算程序的时间复杂度时,有以下两条规则:

  • 加法原则: T ( n ) = T 1 ( n ) + T 2 ( n ) = O ( f ( n ) ) + O ( g ( n ) ) = O ( m a x ( f ( n ) , g ( n ) ) ) T(n)=T_1(n)+T_2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n))) T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n)))
  • 乘法原则: T ( n ) = T 1 ( n ) × T 2 ( n ) = O ( f ( n ) ) × O ( g ( n ) ) = O ( f ( n ) × O g ( n ) ) T(n)=T_1(n)\times T_2(n)=O(f(n))\times O(g(n))=O(f(n)\times Og(n)) T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))=O(f(n)×Og(n))

常见时间复杂度的关系为:
O ( 1 ) < O ( l o g 2 n ) < O ( n ) < O ( n l o g 2 n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) O(1)<O(log_2n)<O(n)<O(nlog_2n)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n) O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)

空间复杂度

与时间复杂度类似,算法的空间复杂度记作:
S ( n ) = O ( f ( n ) ) S(n)=O(f(n)) S(n)=O(f(n))

它包括算法本身要占据的空间以及使用的辅助空间。在计算空间复杂度时只需要计算辅助空间即可,比如上述的选择排序算,它仅需要一个临时变量作为辅助空间,所以它的空间复杂度为 O ( 1 ) O(1) O(1),当算法的空间复杂度为 O ( 1 ) O(1) O(1)时称它在原地工作。注意,当函数递归调用时,要考虑每一次递归使用的辅助空间。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐