第一章—数据结构绪论

算法的时间复杂度和空间复杂度分析

算法时间复杂度

将算法中基本操作的执行次数作为算法时间复杂度的度量。这里所讨论的时间复杂度不是执行完一段程序的总时间,而是其中基本操作的总次数。因此,对一个算法进行时间复杂度分析的要点,无非是确定算法中哪些操作是基本操作,然后计算出基本操作重复执行的次数即可。在问题中总能找到一个 n n n,可以称为问题的规模,如果要处理的数组元素的个数为n,而基本操作所执行的次数是n的一个函数 f ( n ) f(n) f(n)。对于求其基本操作执行的次数,就是求函数 f ( n ) f(n) f(n)。求出以后就可以取出 f ( n ) f(n) f(n)中随n增大而增大最快的项,然后将其系数变为1,作为时间复杂度的度量。记为 T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n))增长最快的项/此项的系数)。例如: f ( n ) = 2 n 3 + 4 n 2 + 100 f(n)=2n^3+4n^2+100 f(n)=2n3+4n2+100,则其时间复杂度为 T ( n ) = O ( 2 3 / 2 ) = O ( n 3 ) T(n)=O(2^3/2)=O(n^3) T(n)=O(23/2)=O(n3)。实际上计算算法的时间复杂度就是给出相应的数量级,当 f ( n ) f(n) f(n) n n n无关时,时间复杂度 T ( n ) = O ( 1 ) T(n)=O(1) T(n)=O(1)

常用的时间复杂度比较关系: 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(1) \leq O(log_{2}n) \leq O(n) \leq O(nlog_2n) \leq O(n^2) \leq O(n^3) \leq ... \leq O(2^n) O(1)O(log2n)O(n)O(nlog2n)O(n2)O(n3)...O(2n)

通过以上分析,总结出计算一个算法时间复杂度的具体步骤如下:

  1. 确定算法中的基本操作以及问题的规模。
  2. 根据基本操作执行情况计算出规模 n n n的函数 f ( n ) f(n) f(n),并确定时间复杂度为 T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n))中增长最快的项。

有的算法中基本操作的执行次数不仅跟初始输入的数据规模有关,还和数据本身有关。一般依照使得基本操作执行次数最多的输入来计算时间复杂度,即将最坏的情况作为算法时间复杂度的度量。

例题解读

例1:求出以下算法的时间复杂度

void fun(int n){
	int i = 1,j = 100;
	while(i < n){
		++i;
		i += 2;
	}
}

分析:

第一步:找出基本操作,确定规模 n n n

  1. 找基本操作:基本操作即以求时间复杂度为目的的前提下,重复执行次数和算法的执行时间成正比的操作。通俗地说,这种操作组成了算法,当它们都执行完的时候算法也结束了。多数情况下取最深层循环内的语句所描述的操作作为基本操作。显然该题中++i;i+=2;这两行都可以作为基本操作。
  2. 确定规模:由循环条件 i < n i<n i<n可知,循环执行的次数和参数 n n n有关,因此参数 n n n就是规模 n n n

第二步:计算出 n n n的函数 f ( n ) f(n) f(n)

显然, n n n确定以后,循环结束与否和 i i i有关。 i i i的初值为1,每次自增2,假设 i i i自增 m m m次后循环结束,则 i i i最后的值为 1 + 2 m 1+2m 1+2m,因此有 1 + 2 m + K = n 1+2m+K=n 1+2m+K=n(其中,K是一个常数,因为在循环结束时 i i i的值稍大于 n n n),为了方便表述和进一步计算,用 K K K 1 + 2 m 1+2m 1+2m修正为 n n n,解得 m = ( n − 1 − K ) / 2 m=(n-1-K)/2 m=(n1K)/2,即 f ( n ) = ( n − 1 − K ) / 2 f(n)=(n-1-K)/2 f(n)=(n1K)/2,可以发现增长最快的是 n / 2 n/2 n/2,因此时间复杂度为 T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n)

例2:分析以下算法的时间复杂度

void fun(int n){
    int i,j,x = 0;
    for(i = 0;i < n;++i){
        for(j = i+1; j < n;++j){
            ++x;
        }
    }
}

分析:++x;处于最内层循环,因此取++x;作为基本操作。显然 n n n为规模,可以算出++x;的执行次数为 f ( n ) = n ( n − 1 ) / 2 f(n)=n(n-1)/2 f(n)=n(n1)/2,变化最快的项是 n 2 / 2 n^2/2 n2/2,因此时间复杂度为 T ( n ) = O ( n 2 ) T(n)=O(n^2) T(n)=O(n2)

例3:分析以下算法时间复杂度

void fun(int n){
    int i=0,s=0;
    while(s<n){
        ++i;
        s=s+i;
    }
}

分析: n n n为规模,基本操作为++i;s=s+i; i i i s s s都是从0开始的,假设循环执行m次结束,则有 s 1 = 1 s_1=1 s1=1 s 2 = 1 + 2 = 3 s_2=1+2=3 s2=1+2=3 s 3 = 1 + 2 + 3 = 6 s_3=1+2+3=6 s3=1+2+3=6,…, s m = m ( m + 1 ) / 2 s_m=m(m+1)/2 sm=m(m+1)/2(其中, s m s_m sm为执行到第 m m m次时 s s s的值),则有 m ( m + 1 ) / 2 + K = n m(m+1)/2+K=n m(m+1)/2+K=n,由求根公式得
m = − 1 + 8 n + 1 − 8 K 2 m = \frac{-1 + \sqrt{8n+1-8K}}{2} m=21+8n+18K

f ( n ) = − 1 + 8 n + 1 − 8 K 2 f(n)=\frac{-1+\sqrt{8n+1-8K}}{2} f(n)=21+8n+18K
由此可以得到时间复杂度为 T ( n ) = O ( n ) T(n)=O(\sqrt{n}) T(n)=O(n )

算法的空间复杂度

算法的空间复杂度是指算法在运行期间所需存储空间的度量。只要考虑在算法运行过程中临时占用的存储空间的大小(和时间复杂度一样,也用数量级的形式给出)

说明:这一部分在理解了各种数据的存储结构及其操作之后更容易理解,因此对于这一部分,将在后面结合进行学习。

数据结构和算法的基本概念

数据结构的基本概念

  1. 数据:是对客观事物的符号表示,在计算机科学中是指所有能输入到计算机中并且被计算机程序处理的符号的总称。例如,整数,实数和字符串都是数据。
  2. 数据元素:是数据的基本单位,是数据结构课程中讨论的最小单位,在计算机程序中通常将其作为一个整体进行考虑和处理。有时,一个数据元素可由若干数据项组成。例如,一本书的书目信息为一个数据元素,而书目信息的每一项(如书名、作者名等)为一个数据项。
  3. 数据对象:是性质相同的数据元素的集合,是数据的一个子集。例如,大写字母就是一个数据对象,大写字母数据对象是集合{‘A’,‘B’,‘C’}。
  4. 数据结构:是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构包括3方面内容:逻辑结构、存储结构和对数据的运算。
  5. 数据的逻辑结构:是对数据之间的关系的描述,它与数据的存储结构无关,同一种逻辑结构可以有多种存储结构。归纳起来数据的逻辑结构主要有以下两大类。
    1. 线性结构:是一个数据元素的有序集合。它有以下四个基本特征:
      1. 集合中必存在唯一的一个“第一个元素”。
      2. 集合中必存在唯一的一个”最后一个元素“
      3. 除最后一个元素之外,其他数据元素均有唯一的“后继”
      4. 除第一个元素之外,其他数据元素均有唯一的“前驱
    2. 非线性结构:与线性结构不同,非线性结构中的节点存在着一对多的关系,它又可以细分为树形结构和图形结构。
  6. 数据的物理结构:又称为存储结构,是数据的逻辑结构在计算机中的表示(又称为映像)。它包括数据元素的表示和关系的表示。当数据元素是由若干数据项构成的时候,数据项的表示称为数据域。例如:一个链表结点,结点包含值域和指针域,这里结点可以看做一个数据元素,其中的值域和指针域都是这个数据元素的数据域。
  7. 数据元素之间的关系在计算机中有两种不同的表示方法:顺序映像和非顺序映像。对应的两种不同的存储结构分别是顺序存储结构和链式存储结构。顺序映像是借助数据元素在存储器中的相对位置来表示数据元素之间的逻辑关系;非顺序映像是借助指针表示数据元素之间的逻辑关系,实际上,在数据结构中有4种常用的存储结构。
    1. 顺序存储方法:是把逻辑上相邻的结点存储在物理位置上相邻的存储单元,结点之间的逻辑关系由存储单元的邻接关系来体现。通常顺序结构是借助数组来描述的。
    2. 链式存储方法:该方法不要求逻辑上相邻的结点在物理位置上也相邻,结点间的逻辑关系是由附加的指针字段表示的。通常借助指针类型实现。
    3. 索引存储方法:在存储结点信息时除建立存储结点信息外,还建立附加的索引表来标识结点的地址。索引项的一般形式为<关键字,地址>。关键字标识唯一一个节点,地址作为指向结点的指针。
    4. 散列存储方法:基本思想是根据结点的关键字通过散列函数直接计算出该结点的存储地址。这个方法实际上是顺序存储方法的扩展。

算法的基本概念

  1. 算法:由基本运算及规定的运算顺序所构成的完整的解题步骤,或者看成按照要求设计好的有限的确切的计算序列。
  2. 算法的特性
    1. 有穷性:一个算法必须保证执行有限步之后结束。
    2. 确定性:算法的每一步骤必须有确定的定义。
    3. 输入:一个算法有0个或多个输入,以刻画运算对象的初始情况。所谓0个输入是指算法本身确定了初始条件。
    4. 输出:一个算法有一个或多个输出。以反映对输入数据加工后的结果。
    5. 可行性:算法中的所有操作都必须通过已经实现的基本操作进行运算,并在有限次内实现。
  3. 算法设计目标
    1. 正确性:要求算法能够正确地执行预先规定的功能和性能要求。这是最重要的标准。
    2. 可读性:要求算法易于人的理解。
    3. 健壮性:要求算法有很好的容错性,能够不合理的数据进行检查。
    4. 高效率与低存储量需求:算法的效率主要是指算法的执行时间,对于同一个如果有多种算法可以求解,执行时间短的效率高。算法的存储量是指算法执行过程中所需要的最大的存储空间。高效率和低存储量都与问题规模有关。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值