数据结构c语言版第二版(严蔚敏)第一章笔记

目录

引入样例

基本概念以及术语

逻辑结构

存储结构

数据类型和抽象数据类型 

数据类型

抽象数据类型

抽象数据类型的表示与实现

算法和算法分析

评价算法优劣的基本条件

扫描二维码关注公众号,回复: 14383255 查看本文章

时间复杂度

空间复杂度

小结

习题答案


引入样例

早期的计算机主要用于数值计算,目前计算机主要用于非数值计算,包括处理字符、表格和图像等具有一定结构的数据。

早期计算机主要用于数值计算时,一般要经历一下步骤:

1.从具体问题抽象出数学模型(分析问题,从中提取操作对象,并找出这些操作对象之间的关系,然后用数学语言加以描述,即建立相应的数学方程)

2.设计解决此数学模型的算法

3.编写程序,不断测试、调试,直至解决问题

学生学籍管理系统

相似的线性关系表还有图书馆的书目管理系统、库存管理系统等。 在这类问题当中,计算机处理的对象就是各种表,元素之间存在简单的一一对应的线性关系,因此此类数学模型被称为“线性”的数据结构

人机博弈问题

计算机能够与人博弈不是在计算机中有像人类一样电子脑的存在,而是计算机将所有可能存在的情况以及应对的策略已经处理好,在发生相应情况的时候进行处理。例如以上的井字博弈,就像一棵树,随着走法的不同对相应的情况也一直在发生改变,此类结构称为树结构,元素之间是一对多的关系,与博弈论算法相关。施加于对象上的操作有添加、插入、删除等。

最短路径问题

此种问题的数学模型就是图结构,求取两点之间的最短路径。元素之间的关系是多对多的网状关系,施加于对象上的操作有添加、插入、删除等。

从以上三种问题我们可以发现总结出:对于线性表和树结构以及图结构我们可以发现其有明显的不同之处,线性表中的元素是一一对应的状态,树结构中的元素是一对多的状态,图结构中的元素是多对多的状态 

基本概念以及术语

对于基本概念以及术语进行了充分的理解之后,我们可以对后面的内容进行过更好的记忆于理解

数据:是客观事物的符号表示,是所有能输入到计算机中并被计算机程序处理的符号的总称

数据元素:是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理,在有些情况下,数据元素也成为元素,记录等

数据项:是组成数据元素的、有独立含义的、不可分割的最小单位

数据对象:是性质相同的数据元素的集合,是数据的一个子集

数据结构

数据结构是相互之间存在的一种或多种特定关系的数据元素的集合,换句话说,数据结构是带“结构”的数据元素的集合,“结构”就是指数据元素之间存在的关系

逻辑结构

数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的

数据的逻辑结构有两个要素:一是数据元素,二是关系(数据元素间的逻辑关系)

1.集合结构:数据元素之间除了“属于同一集合”的关系外,别无其他关系

2.线性结构:数据元素之间存在一对一的关系

3.树结构:数据元素之间存在一对多的关系

4.图结构:数据元素之间存在多对多的关系

其中集合结构、树结构、图结构皆属于非线性结构

由此引申出线性结构与非线性结构的概念:

非线性结构:树、二叉树、有向图、无向图

线性结构:线性表、栈和队列、数组、广义表

存储结构

数据对象在计算机中的存储表示成为数据的存储结构,也称为物理结构。把数据对象存储到计算机是,通常要求既要存储各数据元素的数据,又要存储数据元素之间的逻辑关系,数据元素在计算机内用一个节点表示。数据元素在计算机中有两种基本的存储结构,分别是顺序存储结构和链式存储结构

顺序存储结构

顺序存储结构是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系,通常借助程序设计语言的数组类型来描述

链式存储结构

顺序存储结构要求所有的元素依次存放在一片连续的存储空间当中,而链式存储结构勿需占用一整块存储空间。但为了表示节点之间的关系需要给每个节点附加指针字段,用于存放后继元素的存储地址,所以链式存储结构通常借助于程序设计语言的指针类型来描述

那么经过对于数据的逻辑结构和存储结构的学习之后,我们便可以列出总的思维图了

数据类型和抽象数据类型 

数据类型

在程序设计语言中、每一个数据都属于某种数据类型。类型明显或者隐含地规定了数据的取值范围、存储方式以及允许进行的运算,数据类型是一个值的集合和定义在这个值集上的一组操作的总称

抽象数据类型

抽象就是抽取出实际问题的本质。在高级语言中出现了数据类型,如:整型、实型、字符型等,可以进一步利用这些类型构造出线性表、栈、队列、树、图等复杂的抽象数据类型

抽象数据类型一般指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称,具体包括三部分:树蕨对象、数据对象上关系的集合、以及对数据对象基本操作的集合

抽象数据类型的定义格式

ADT 抽象数据类型名 {
    数据对象:<数据对象的定义>
    数据关系:<数据关系的定义>
    基本操作:<基本操作的定义>
}ADT 抽象数据类型名

其中数据对象和数据关系的定义采用数学符号和自然语言描述

基本操作名(参数表)
    初始条件:(初始条件描述)
    操作结果:(操作结果描述)

基本操作有两种参数:赋值参数职位操作提供输入值;引用参数以&开头,除了以提供输入值外,还将返回操作结果。“初始条件”描述了操作执行之前的数据结构和参数应该满足的条件,若初始条件为空,则省略。“操作结果”说明了操作正常完成之后,数据结构的变化状况和应返回的结果

抽象数据类型的表示与实现

抽象数据类型的概念与面向对象方法的思想是一直的。抽象数据类型独立于具体实现,将数据和操作封装在一起,使得用户程序只能通过抽象数据类型定义的某些操作来访问其数据,从而实现了信息隐藏。在C++中,我们可以用类的声明表示抽象数据类型,用类的实现来实现抽象数据类型。因此,C++中实现的类相当于数据的存储结构及其在存储结构上实现的对数据的操作。

抽象数据类型和类的概念实际上反应了程序或软件设计的两层抽象:抽象数据类型相当于在概念层(或称为抽象层)上描述问题,而类相当于在实现层上描述问题。此外,C++中的类只是一个由用户定义的普通类型,可用它来定义变量(成为对象或类的实例)。因此在C++中,最终是通过操作对象来解决实际问题的。所以我们可将该层次看作是应用层。由此可以看出,最终表示和实现抽象数据类型还是使用C++面向对象的方法比较方便

1.预定义常量及类型

#define OK 1
#define ERROR -1
#define OVERFLOW -2
typedef int Statue;

2.数据结构的表示(存储结构)用类型定义(typedef)描述,数据元素类型约定为ElemType,由用户在使用该数据类型时自行定义

3.基本操作的算法都用如下格式来描述

函数类型 函数名 (函数形参表)
{
    语句序列
}

当函数返回为状态时,就可以用Statue来定义函数,其中可以借用&来使用引用参数。这样的话,形参的变化也会导致实参的变化,传递引用给函数与传递指针的效果都是一样的,但是引用使用起来比指针更加方便和搞笑

4.内存的动态分配与释放

使用C++的new和delete

分配空间 指针变量 = new 数据类型;
释放空间 delete 指针变量;

5.赋值语句

简单赋值 变量名 = 表达式;
串联复制 变量名1 = 变量名2 = 变量名n = 表达式;
成组赋值 (变量名1,变量名2...变量名n) = (表达式1,表达式2...表达式n);
结构赋值 结构名1 = 结构名2
         结构名 = (值1,值2...值n)
条件赋值 变量名 = 条件表达式 ? 表达式1 : 表达式2 ;
交换赋值 变量名1 <--> 变量名2   

6.选择语句

条件语句1 if (表达式) 语句;
条件语句1 if (表达式) 语句;
          else 语句;
开关语句 switch (表达式)
        {
            case 值1:语句序列1;break;
            case 值1:语句序列2;break;
            case 值1:语句序列3;break;
            default;语句序列n+1;
        }

7.循环语句

for 语句 for (表达式1;条件;表达式2) 语句;
while 语句 while (条件) 语句;
do - while 语句 do {
                        语句序列;
                   } while (条件) ;

8.结束语句

函数结束语句 return 表达式;
            return;
            case或循环语句break;
            异常结束语句exit (异常代码);

9.输入输出语句使用C++流式输入输出的形式

输入语句 cin >> 变量1 >> ... >> 变量n;
输出语句 cout << 变量1 << ... << 变量n;

10.基本函数

求最大值 Max(表达式1,表达式2...表达式n);
求最小值 Min(表达式1,表达式2...表达式n);

算法和算法分析

算法是为了解决某类问题而规定的一个有限长的操作序列

算法必须满足的五个特性:

1.有穷性

一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成

2.确定性

对于每种情况下所应执行的操作,在算法之中都有确切的规定,不会产生二义性,使算法的执行者或阅读者都能明确其含义及如何执行

3.可行性

算法中的所有操作都可以通过已经实现的基本操作运算执行有限次来实现

4.输入

一个算法有零个输入或多个输入。当用函数描述算法时,输入往往是通过形参表示的,在它们被调用时,从主调函数获得输入值

5.输出

一个算法有一个或多个输出,它们是算法进行信息加工后得到的结果,无输出的算法没有任何意义。当用函数描述算法时,输出多用返回值或引用类型的形参表示

评价算法优劣的基本条件

1.正确性

在合理的数据输入下,能够在有限的运行时间内得到正确的结果

2.可读性

一个好的算法,首先应便于人们理解和相互交流,其次才是机器可执行性。可读性强的算法有助于人们对算法的理解,而难懂的算法易于隐藏错误,且难于调试和修改

3.健壮性

当输入的数据非法时,好的算法能适当的做出正确反映或进行相应相应处理,而不会产生一些莫名其妙的输出结果

4.高效性

高效性包括时间和空间两个方面,时间高效是指算法设计合理,执行效率高,可以用时间复杂度来衡量;空间搞笑是指算法占用存储容量合理,可以用空间复杂度来衡量。时间和空间复杂度时衡量算法的两个主要指标

时间复杂度

衡量算法效率的方法主要有两类:事后统计法和事前估计法。事后统计法需要先将算法实现,然后测算其时间和空间开销。这种方法的缺陷很明显,一是必须要把算法转换为可执行的程序,而是时空开销的测算结果依赖于计算机的软硬件等环境因素,这容易掩盖算法本身的优劣。所以普遍采用的方法是事前分析估算法,通过计算算法的渐进复杂度来衡量算法的效率

1.问题规模和语句频度

不考虑计算机的软硬件等环境因素,影响算法时间代价的最主要因素是问题规模。问题规模是算法求解问题输入量的多少,是问题大小的本质表示,一般用整数n表示。问题规模n对不同问题的含义不同。例如在排序运算中n为参加排序的记录数,在矩阵运算中n为矩阵的阶数,在多项式运算中n为多项式的项数,在集合运算中n为集合中元素的个数,在树的运算中n为树的结点个数,在图的有关运算中n为图的顶点数或者边数。显然n越大,算法执行的时间也就越长

一个算法的执行时间大致上等于其所有语句执行时间的综合,而语句的执行时间则为该条语句的重复执行次数和执行一次所需时间的乘积

一条语句重复执行次数称作语句频度

由于语句的执行要由源程序经编译程序翻译成目标代码,目标代码经装配再执行,因此语句执行一次所需的具体时间与机器的软硬件环境密切相关,所以所谓的算法分析并非精确统计算法实际执行所需时间,而是针对算法中语句的执行次数进行估计,从中得到算法执行时间的信息

设每条语句执行一次所需时间均为单位时间,则一个算法的执行时间可用该算法中所有语句频度之和来度量

2.算法的时间复杂度定义

针对于较为复杂的算法,计算其时间复杂度的话就要使用到“基本语句”。所谓“基本语句”指的是算法中重复执行次数和算法的执行时间成正比的语句,它对算法运行时间的贡献最大。通常,算法的执行时间是随着问题的规模增长而增长的,因此对算法的评价通常只需考虑其随问题增长的趋势。这种情况下,我们只需要考虑当问题规模充分大时,算法中基本语句的执行次数再渐进意义下的阶。

如上面的那个例子

当n充分大时,(f(n)) / n^3 是一个不等于0的常熟,即二者是同阶的,或者说二者数量级相同。在这里我们用O来表示数量级,记作T(n) = O(f(n)) = O(n^2)

因此我们可以获得公式

T(n) = O(f(n))

它表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度

数学符号O的严格定义为:

若T(n)和O(f(n))是定义在正整数集合上的两个函数,则T(n) = O(f(n))表示存在正的常数C和n0使得当n >= n0的时候都满足C(f(n)) >= T(n) >= 0,该定义说明了函数T(n)和f(n)具有相同的增长趋势,并且T(n)的增长至多趋向于函数f(n)的增长。符号O来表示增长的上限,它表示当问题规模n >n0时,算法的实行时间不会超过f(n),其状况如图所示:

3.算法的时间复杂度分析举例

(1)幂次阶 

 在计算算法时间复杂度时,可以忽略所有低次幂项和最高次幂的系数,这样可以简化算法分析,也体现出了增长率的含义

(2)常量阶 

{x ++ ;}

此语句频度为1,算法的执行时间是一个与问题规模n无关的常熟,所以算法的时间复杂度为T(n) = O(1),称为常量阶。实际上,如果算法的执行时间不随问题规模n的增加而增长,算法中语句频度就是某个常数,即使这个常数再大,算法的时间复杂度都是O(1),例如

for(int i = 0;i < 1000000;i ++ )s ++ ;

(3)线性阶

for(int i = 0;i < n;i ++ )s ++ ;

s ++ ;语句频度为f(n) = n,所以算法的时间复杂度为T(n) = O(n),称为线性阶

(4)平方阶

x ++ ;
for(int i = 0;i < n;i ++ )s ++ ;
for(int i = 0;i < n;i ++ )
{
    for(int j = 0;j < n;j ++ )
    {
        q ++ ;
    }
}

语句频度最大的是q ++ ,其频度为f(n) = n^2,因此该算法时间复杂度为T(n) = O(n^2),称为平方阶。多数情况下,当有若干个循环语句时,算法的时间复杂度是由最深层循环内的基本语句的频度f(n)决定的

(5)立方阶

for(int i = 0;i < n;i ++ )
{
	for(int j = 0;j < n;j ++ )
	{
		for(int k = 0;k < n;k ++ )
		{
			s ++ ;
		}
	}
}

 s ++ ;语句频度为f(n) = n^3,所以算法的时间复杂度为T(n) = O(n^3),称为立方阶

(5)对数阶

for(int i = 0;i < n;i += 2)s ++ ;

s ++ ;语句频度为f(n) = n,则有2^(f(n)) <= n,所以算法时间复杂度为T(n) = O(log2n),称为对数阶

4.最好、最坏时间复杂度

对于某些问题的算法,其基本语句的频度不仅仅与问题的规模相关,还依赖于其他的因素。

称算法在最好情况下的时间复杂度为最好时间复杂度,指的是算法计算量可能达到的最小值

称算法在最坏情况下的时间复杂度为最坏时间复杂度,指的是算法计算量可能达到的最大值

算法的平均时间复杂度是指算法在所有可能情况下,按照实例以等概率出现时,算法计算量的加权平均值

一般来说,我们口中说的时间复杂度即为最坏时间复杂度 

空间复杂度

关于算法的存储空间需求,类似于算法的时间复杂度,我们采用渐进空间复杂度作为算法所需储存空间的量度,简称空间复杂度,它也是问题规模n的函数,记作

S(n) = O(f(n))

一般情况下,一个程序在机器上执行时,除了需要寄存本身所用的指令、常数、变量和输入数据外, 还需要一些对数据进行操作的辅助存储空间,其中,对于输入数据所占的具体存储量取取决于问题本身外,与算法无关,这样只需分析该算法在实现时所需要的辅助空间就可以了。若算法执行时所需要的辅助空间相对于输入数据量而言是个常数,则称这个算法为原地工作,辅助空间为O(1)

举一个逆序数组的算法

for(int i = 0;i < n / 2;i ++ )
{
    int t = a[i];
    a[i] = a[n - i - 1];
    a[n - i - 1] = a[i];
}

此算法只借助了一个临时变量t,与问题规模n大小无关,所以空间复杂度是O(1)

for(int i = 0;i < n;i ++ )b[i] = a[n - i - 1];
for(int i = 0;i < n;i ++ )a[i] = b[i];

此算法借助了一个大小为n的b数组,所以其空间复杂度为O(n)

对于一个算法,其时间复杂度和空间复杂度往往是相互影响的,当追求一个较好的时间复杂度时,可能会导致占用较多的存储空间,即可能会使空间复杂度的性能变差,反之亦然。不过在通常情况下,鉴于运算空间较为充足,人们都以算法的时间复杂度作为算法优劣的衡量指标

小结

1.数据结构是一门研究非数值计算程序设计中操作对象,以及这些对象之间的关系和操作的学科

2.数据结构包括两方面的内容:数据的逻辑结构和存储结构。同一逻辑结构采用不同的存储方法,可以得到不同的存储结构

(1)逻辑结构是从具体问题抽象出来的数学模型,从逻辑关系上描述数据,它与数据的存储无关。根据数据元素之间关系的不同特性,通常有四类基本逻辑结构:集合结构、线性结构、树结构、图结构

(2)存储结构是逻辑结构在计算机中的存储表示,有两类存储结构:顺序存储结构和链式存储结构

3.抽象数据类型是指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称,具体包括三部分:数据对象、数据对象上关系的集合、以及对数据对象的基本操作的集合

4.算法是为了解决某类问题而规定的一个有限长的操作序列。算法具有五个特性:有穷性、确定性、可行性、输入和输出。一个算法的优劣应该从以下四个方面来评价:正确性、可读性、健壮性、高效性

5.算法分析的两个主要方面是分析算法的时间复杂度和空间复杂度,以考察算法的时间和空间效率。一般情况下,鉴于运算空间较为充足,故将算法的时间复杂度作为分析的中带你。算法执行时间的数量级称为算法的渐进时间复杂度,T(n) = O(f(n)),它表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,简称时间复杂度

习题答案

猜你喜欢

转载自blog.csdn.net/couchpotatoshy/article/details/125847542