数据结构 第1篇--简单理解

第一章 绪论

首先介绍数据结构的基本概念及相关术语;然后讨论数据结构、数据类型和抽象数据类型之间的关系,介绍数据结构描述方法;最后阐述算法分析的方法。

1.1 数据结构起源

“数据结构”的概念起源于1968年美国计算机科学家唐纳德·克努特(Donald Ervin Knuth)教授所著的《计算机程序设计艺术》(The Art of Computer Programming),如图所示。在该书的第一卷《基本算法》中,他开创了数据结构的最初体系,较系统地阐述了数据的逻辑结构和存储结构及其操作。
在这里插入图片描述
在计算机科学中,研究数据结构对设计出高性能的算法和高性能软件至关重要。“数据结构”课程不仅是程序设计的基础,而且是设计和实现编译程序、操作系统、数据库系统及其他应用程序的重要基础。

1.2 基本概念和术语

1.2.1 基本概念

1.数据

数据是可被计算机识别并加工处理的对象。

数据不仅包括数据是可被计算机识别并加工处理的对象。数据不仅包括整型、实型等数值数据,还包括声音、图像、视频等非数值数据。例如,MP3格式的文件是常见的音频声音数据,BMP格式的文件是典型的图像数据,RMVB格式的文件是视频数据。

2.数据元素

数据元素是由数据组成的具有一定意义的基本单位,在计算机中通常作为一个整体来处理。有些情况下,数据元素也称为元素、记录。

3.数据项

数据项是组成数据元素的、不可分割的最小单位。

下表为学生信息表,其中每个学生的信息可看作一个数据元素,它由学号、姓名、性别、籍贯等数据项组成。
在这里插入图片描述

1.2.2 数据结构数据结构

(Data Structure)是由某一数据对象及该对象中所有数据元素之间的关系组成的。数据结构包括数据的逻辑中所有数据元素之间的关系组成的。数据结构包括数据的逻辑结构、存储结构及数据的运算三方面的内容。接下来从这三方面分别阐述。

1.数据的逻辑结构

数据的逻辑结构是从逻辑关系上描述数据。数据的逻辑结构仅考虑数据之间的内在关系,是面向应用问题的,它是独立于计算机的。根据数据结构中数据元素之间关系的不同特征,可划分为四种基本逻辑结构,如下图所示。

(1)线性结构

线性结构中数据元素之间存在一对一的关系。例如,学生信息表可看成一个线性结构,学生信息按照学号依次排列。

(2)树形结构

树形结构中数据元素之间存在一对多的关系。例如,一所学校有多个学院,一个学院有多个专业,构成树形结构。

(3)图结构

图结构中数据元素之间存在多对多的关系。例如,微信朋友圈中,“我”的朋友们彼此之间可能是相识关系,也可能是不相识关系,他们之间存在多对多的朋友关系,从而构成图结构。
在这里插入图片描述

(4)集合结构

数据元素之间除了“属于同一个集合”的联系之外没有其他关系。例如,学生存在于某个班级中,若不考虑学生之间的其他关系,则可视班级为一个集合结构。

以上四种基本的逻辑结构还可进一步分成两类:

线性结构
非线性结构

除了线性结构以外,树形、图和集合结构可统一归入非线性结构一类。

2.数据的存储结构

数据的存储结构是数据及数据之间的关系在计算机内的表示形式。它是面向计算机的,是数据的逻辑结构在计算机存储中的影像。数据的存储结构中,最常见的两种基本存储结构分别是顺序存储结构和链式存储结构。

(1)顺序存储结构

顺序存储结构是将逻辑上相关的数据元素依次存储在地址连续的存储空间中。
这种存储结构是借助数据元素在存储空间中的相对位置来表示它们之间的逻辑关系。
假定有一线性结构的数据(a0,a1,a2),每个元素占2个存储单元,连续存储空间的起始地址是100,则其顺序存储表示如图(a)所示。
在这里插入图片描述
顺序表示方法并不仅限于存储线性结构的数据。例如,树形结构的数据对象有时也可采用顺序存储的方法表示。这将在以后详细阐述。

(2)链式存储结构

链式存储结构中,数据元素可以存储在任意的存储空间中,可以是连续的存储空间,也可以是不连续的存储空间。在这种情况下,数据元素的存储位置并不能体现它们之间的逻辑关系,需要用指针域存储逻辑上相关的数据元素的地址。因此,为了存储一个元素,需要存放数据元素本身和与该元素逻辑上相关的其他元素的地址,这两部分信息组成一个结点。
在这里插入图片描述

假定有一线性结构的数据(a0,a1,a2),其链式存储表示如图(b)所示。其中,每个结点存储了数据元素和该元素逻辑上的后继结点的地址。注意:一个结点的存储地址通常是指存放该结点的存储块的起始存储单元地址。

3.数据的运算

如果说数据的逻辑结构描述了数据的静态特性,那么在数据的逻辑结构上定义的一组运算给出了数据被使用的方式,即数据的动态特性。使用数据结构上定义的运算,用户可对数据结构的实例或组成实例的数据元素实施相应的操作。运算的结果可使数据改变状态。

数据结构最常见的运算有:

1)搜索运算——在数据结构中搜索满足一定条件的元素;
(2)插入运算——在数据结构中插入新元素;
(3)删除运算——将数据结构中指定元素删除;
(4)更新运算——将数据结构中指定元素更新为新的元素。

1.3 抽象数据类型

1.数据类型

数据类型是指性质相同的值的集合以及定义在该值集上的运算集合。
C语言常用的基本数据类型有整型、字符型、指针类型等。
数据类型规定了数据的取值范围和允许执行的运算。

例如,若在C语言中声明int a,b,则可以给变量a和b赋值0,但不可以赋值2.5,这超出了int型变量的取值范围。a和b之间可以执行加法运算,但不可以执行求交集运算,这也超出了int型变量所允许的运算范围。

2.抽象数据类型

了解一个数据类型的对象(变量、常量)在计算机内的表示是有一定用处的,但如果每个数据使用者都要考虑基本数据类型的实现细节,这将给数据使用者增加一项繁杂的工作,并且使用者一旦随意改变数据存储表示,也会滋生不可预知的错误。目前普遍认为对数据类型进行抽象,对使用者隐藏一个数据类型的实现是一个好的设计策略,由此产生了抽象数据类型。

抽象数据类型(Abstract Data Type,ADT)是一个数学模型以及在其上定义的运算集合。其最主要的两个特征是数据封装和信息隐蔽

数据封装是指把数据和操纵数据的运算组合在一起的机制。

信息隐蔽是指数据的使用者只需知道这些运算的定义(也称规范)便可访问数据,而无须了解数据的存储以及运算算法的实现细节。

通过实行数据封装和信息隐蔽,可使数据的使用和实现相分离。
抽象数据类型的定义格式如下:
在这里插入图片描述

3.数据结构与抽象数据类型

本文将一种数据结构视为一个抽象数据类型,从规范和实现两方面来讨论数据结构。

规范是对数据结构中数据元素及其关系、运算给出定义,即逻辑结构和运算的定义组成了数据结构的规范。

规范指明了一个数据结构可以“做什么”。数据结构的使用者通过规范中的说明使用一个数据结构,不必了解具体的实现细节。数据的存储表示和运算算法的描述构成数据结构的实现,它解决了“怎样做”的问题。

1.4 算法和算法分析

1.4.1 算法

算法是计算机科学中的基本概念,是对特定问题的求解步骤。算法须具有下列五个特征。

1)输入:一个算法有0个或多个输入。
(2)输出:一个算法产生一个或多个输出,作为算法运算的结果。
(3)可行性:算法的每一个步骤都可以通过已经实现的基本运算来实现。
(4)确定性:算法的每一个步骤都必须有确切的含义,不会产生二义性。
(5)有穷性:算法必须能在执行有穷步之后终止。

算法的描述方式多种多样,可以用自然语言、流程图、程序设计语言或伪代码来描述。为了方便读者理解算法和上机验证算法,本文采用自然语言描述算法思想,并使用C语言描述算法的实现。
衡量一个算法的优劣,主要有以下几个基本标准。
(1)正确性
在合理的数据输入下,算法能够在有限的时间内产生满足预先规定的功能和性能要求。
(2)可读性
一个好的算法应当思路清晰、简单明了。可读性高的算法便于人们阅读、理解和交流;晦涩难懂的算法容易隐藏错误,不易被发现和调试。
(3)健壮性
一个好的算法应在输入不合法数据时,能做出适当处理,而不至于产生异常或是出现崩溃等严重后果。
(4)高效性
评价一个算法的效率主要包括时间和空间两方面。好的算法应具备执行效率高和占用存储空间少的特点。时间复杂度和空间复杂度是衡量算法效率的两个重要指标。

1.4.2 算法的时间复杂度

算法执行时间需通过依据该算法编制的程序在计算机上运行时所消耗的时间来度量。算法的时间复杂度一般是指程序运行从开始到结束所需的时间。而度量一个程序的执行时间通常有两种方法:事后统计法和事前估算法。事后统计法是将算法实现后计算其时间和空间开销,从而确定算法的效率。然而,时间和空间开销的计算与计算机软硬件环境相关,同一个算法在不同的机器上执行所花的时间也不一样,这种方法存在明显的缺陷,因此,不予采纳。本文采用事前估算法评估算法效率。

抛开与计算机软硬件相关的因素,影响算法时间效率最主要的因素是问题规模

问题规模通常是指算法的输入量,一般用整数n表示。

例如,采用相同的排序算法对10个元素进行排序与对100 000个元素进行排序所需的时间显然是不同的。
一个算法时间花销与算法中语句的执行次数成正比例。算法中语句执行次数多,它的时间花销就多。一个算法中的语句执行次数称为语句频度。
一般情况下,算法中基本运算执行次数用T(n)表示,若有问题规模n的某个函数f(n),使存在自然数n0,正常数c,当n大于等于n0时,T(n)≤cf(n),则称f(n)是T(n)的渐近上界。记为
T(n)=O(f(n))
大O记号表示算法的一种渐近时间复杂度。渐近时间复杂度也常简称为时间复杂度。大O记号用以表达一个算法运行时间的上界,估计算法的执行时间的数量级。
下面举例说明算法的渐近时间复杂度的求解。
程序1.1 简单求和程序
在这里插入图片描述

程序1.1语句执行次数为5,算法的渐近时间复杂度为O(1),属于常数级。
程序1.2 累加求和程序
在这里插入图片描述
程序1.2语句执行次数为2n+4,算法的渐近时间复杂度T(n)=O(n)。
程序1.3 矩阵求和程序
在这里插入图片描述
程序1.3语句执行次数为3+n+1+n(n+1)+n2=2n2+2n+4,算法的渐近时间复杂度T(n)=O(n2)。

一般情况下,可以通过考察一个程序中的关键操作的执行次数来计算算法的渐近时间复杂度。
所谓关键操作是对算法执行时间贡献最大的操作。例如,程序1.2中,语句sum=sum+1可被认为是关键操作,其执行次数为n,由此计算可得算法的渐近时间复杂度也是O(n)。
常见的渐近时间复杂度从小到大依次是O(1)<O(log2 n)<O(n)<O(nlog2 n)<O(n2)<O(n3)。

1.4.3 最坏、最好和平均情况时间复杂度

算法的时间复杂度不仅与问题规模相关,如果输入数据不同,算法所需的时间开销也会不同。例如,在包含n个元素的数组中找给定元素x,设算法从左向右搜索,如果待搜索的元素x正好是第一个元素,则所需的查找时间最短,这就是算法的最好情况。如果待搜索的元素x是最后一个元素或是不在数组中,则是算法的最坏情况。如果需要多次在数组中查找元素,并且假定以相等的概率查找每个数组元素,则是算法时间代价的平均情况

对于算法的时间复杂度分析,存在三种情况,对应三种时间复杂度,即最好情况、最坏情况和平均情况时间复杂度。相关示例将在后续给出。

1.4.4 算法的空间复杂度

算法的空间复杂度往往是指对应的程序从运行开始到结束所需的存储量。

程序运行所需的存储空间包括两部分。

(1)固定部分。这部分空间与问题规模无关,主要包括程序代码、常量、简单变量等所占的空间。
(2)可变部分。这部分空间大小与问题规模有关。例如,长度为1 000的两个数组相加,与长度为10的两个数组相加,所需的存储空间是不同的。这部分存储空间除了包括数据元素所占的空间外,还包括算法执行所需的额外空间,如递归栈所用的空间。

算法的空间复杂度的讨论类似于时间复杂度,但空间复杂度一般按最坏情况来分析。

算法的时间复杂度是本章的重点和难点。

猜你喜欢

转载自blog.csdn.net/weixin_44354035/article/details/107750599