第一章 绪论
第一章 绪论
数据结构主要研究的问题: 如何合理地组织数据、高效地处理数据。
1.1 数据结构的研究内容
数据结构主要研究非数值计算问题,非数值计算问题无法用数学方程建立数学模型。
数值计算步骤:
- 首先从具体问题中抽象出数学模型
- 然后设计一个用于此数学模型的算法
- 最后编写程序,进行测试、调试,直到解决问题。
数据结构是一门研究非数值计算程序设计中的操作对象,以及这些对象之间的关系和操作的学科。
程序 = 数据结构 + 算法
程序设计的实质就是为所处理的问题选择一种好的数据结构,并在此结构基础上施加一种好的算法。
1.2 数据结构的基本概念和术语
1.2.1 数据、数据元素、数据项和数据对象
数据 > 数据对象 > 数据元素 > 数据项
数据:是客观事物的符号表示,是所有能输入到计算机中并被计算机程序处理的符号的总称。
- 如:整数、实数、字符串、图形、图像、声音、动画等通过特殊编码定义后的数据。
数据元素:是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。(也称为元素、记录等)
- 数据元素用于完整地描述一个对象。
数据项:是组成数据元素的、有独立含义的、不可分割的最小单位。(也称为字段、属性)
数据对象:是性质相同的数据元素的集合,是数据的一个子集。只要集合内元素的性质相同,都可称之为一个数据对象。
1.2.2 数据结构
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。数据结构是带 “结构” 的数据元素的集合,“结构” 就是指数据元素之间存在的关系。
数据结构包括逻辑结构和存储结构两个层次。
1. 逻辑结构
数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。数据的逻辑结构可以看做是从具体问题中抽象出来的数学模型。
逻辑结构的两个要素:一是数据元素;而是关系。关系是指数据元素间的逻辑关系。
根据数据元素之间关系的不同特性,数据的逻辑结构通常有四类基本逻辑结构。
四类基本逻辑结构:
-
集合结构
数据元素之间除了属于同一集合的关系外,别无其他关系。
-
线性结构
数据元素之间存在一对一的关系。
-
树结构
数据元素之间存在一对多的关系。
-
图结构或网状结构
数据元素之间存在多对多的关系。

2. 存储结构
数据对象在计算机中的存储表示称为数据的存储结构,也称为物理结构。
数据对象存储到计算机时,通常既要存储各数据元素的数据,又要存储数据元素之间的逻辑关系,数据元素在计算机内用一个节点来表示。
两种基本存储结构:顺序存储结构和链式存储结构。
(1)顺序存储结构
顺序存储结构是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。
顺序存储结构要求所有的元素依次存放在一片连续的存储空间中。
(2)链式存储结构
链式存储结构,每个节点占用两个连续的存储单元,一个存放节点的信息,另一个存放后继节点的首地址。
链式存储结构,无需占用一整块存储空间。
1.2.3 数据类型和抽象数据类型
1. 数据类型
数据类型是一个值的集合和定义在这个值集上的一组操作的总称。类型明显或隐含地规定了数据的取值返回、存储方式以及允许进行的运算。
数据类型是高级程序设计语言中的一个基本概念。程序设计语言允许用户直接使用的数据类型由具体语言决定,数据类型反映了程序设计语言的数据描述和处理能力。
2. 抽象数据类型
抽象就是抽取出实际问题的本质。
抽象数据类型一般指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称,具体包括 3 个部分:数据对象、数据对象上关系的集合以及对数据对象的基本操作的集合。(数据对象、数据关系、基本操作)
抽象数据类型的定义格式:

基本操作的定义格式:

基本操作两种参数:
赋值参数只为操作提供输入值;引用参数以 “&” 打头,除可提供输入值外,还将返回操作结果。
初始条件描述了操作执行之前数据结构和参数应满足的条件,若初试条件为空,则省略。
操作结果说明了操作正常完成之后,数据结构的变化状况和返回结果。
1.3 抽象数据类型的表示和实现
抽象数据类型独立于具体实现,将数据和操作封装在一起,使得用户程序只能通过抽象数据类型定义的某些操作来方位其中的数据,从而实现信息隐藏。
可以用类来实现抽象数据类型。
抽象数据类型和类的概念实际上反映了程序或软件设计的两层抽象:抽象数据类型相当于在概念层(或称为抽象层)上描述问题,而类相当于在实现层上描述问题。
最终是通过操作对象(实例)来解决实际问题。最终表示和实现抽象数据类型,最好用面向对象方法。
完整抽象数据类型的定义、表示和实现
(1)定义部分
(2)表示部分
(3)实现部分
Python实现复数类型:
import math
class ComplexNumber:
def __init__(self, real: float, imag: float):
"""初始化复数,实部和虚部"""
self.real = real
self.imag = imag
def __add__(self, other):
"""实现复数的加法"""
return ComplexNumber(self.real + other.real, self.imag + other.imag)
def __sub__(self, other):
"""实现复数的减法"""
return ComplexNumber(self.real - other.real, self.imag - other.imag)
def __mul__(self, other):
"""实现复数的乘法"""
real_part = self.real * other.real - self.imag * other.imag
imag_part = self.real * other.imag + self.imag * other.real
return ComplexNumber(real_part, imag_part)
def __truediv__(self, other):
"""实现复数的除法"""
denominator = other.real ** 2 + other.imag ** 2
if denominator == 0:
raise ZeroDivisionError("Cannot divide by zero")
real_part = (self.real * other.real + self.imag * other.imag) / denominator
imag_part = (self.imag * other.real - self.real * other.imag) / denominator
return ComplexNumber(real_part, imag_part)
def modulus(self):
"""计算复数的模"""
return math.sqrt(self.real ** 2 + self.imag ** 2)
def __str__(self):
"""复数的字符串表示形式"""
if self.imag >= 0:
return f"{self.real} + {self.imag}i"
else:
return f"{self.real} - {-self.imag}i"
# 示例用法
z1 = ComplexNumber(3, 4)
z2 = ComplexNumber(1, -2)
print("z1:", z1)
print("z2:", z2)
print("z1 + z2:", z1 + z2)
print("z1 - z2:", z1 - z2)
print("z1 * z2:", z1 * z2)
print("z1 / z2:", z1 / z2)
print("z1 的模:", z1.modulus())
1.4 算法和算法分析
1.4.1 算法的定义及特性
算法是为了解决某类问题而规定的一个有限长的操作序列。算法就是解决问题的步骤。
算法的 5 个特性:
-
有穷性
一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成。
-
确定性
对于每种情况下所应执行的操作,在算法中有确切的规定,不会产生二义性,算法的执行者或阅读者都能明确其含义及如何执行。
-
可行性
算法中的所有操作都可以通过将已经实现的基本操作运算执行有现成来实现。
-
输入
一个算法有0个或多个输入。当用函数描述算法时,输入往往就是通过形参表示的,在被调用时,从主调函数获得输入值。
-
输出
一个算法有一个或多个输出,它们是算法进行信息加工后得到的结果,无输出的算法没有任何意义。
1.4.2 评价算法优劣的基本标准
(1)正确性
在合理的数据输入下,好的算法能够在有限的运行时间内得到正确的结果。
(2)可读性
一个好的算法,首先应便于人们理解和相互交流,其次才是机器可执行性。可读性强的算法有助于人们对算法的理解,而难懂的算法容易隐藏错误,且难以调式和修改。
(3)健壮性
当输入的数据非法时,好的算法能适当地做出正确反应或进行相应处理,而不会产生一些莫名其妙的输出结果。
(4)高效性
高效性包括时间和空间连个方面。
时间高效是指算法设计合理,执行效率高,可以用时间复杂度来衡量。
空间高效是指算法占用存储容量合理,可以用空间复杂度来度量。
时间复杂度和空间复杂度是衡量算法的两个主要指标。
1.4.3 算法的时间复杂度
算法效率分析的目的:
是看算法实际是否可行,并在同一问题存在多个算法时,可进行时间和空间性能上的比较,以便从中挑选出较优算法。
衡量算法效率的方法:
事后统计法、事前分析估算法
通常采用事前分析估算法,通过计算算法的渐进复杂度来衡量算法的效率。
1. 问题规模和语句频度
问题规模是算法求解问题输入量的多少,是问题大小的本质表示,一般用整数 n 表示。问题规模对不同的问题含义不同。
显然,n 越大算法执行时间越长。
一个算法的执行时间大致上等于其所有语句执行时间的总和,而语句的执行时间则为该条语句的重复执行次数和执行一次所需的时间的乘积。
语句频度:一条语句的重复执行次数称作语句频度。
所谓的算法分析并非精确统计算法实际执行所需时间,而是针对算法中语句的执行次数做出的估计,从中得到算法执行时间的信息。
2. 算法的时间复杂度定义
基本语句指的是算法中重复执行次数和算法的执行时间成正比的语句,它对算法运行时间的贡献最大。
用“O”来表示数量级
算法时间复杂度的定义:
一般情况下,算法中基本语句重复执行的次数是问题规模n的某个函数f(n),算法的时间量度记作: T(n) = O(f(n)) 它表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间复杂度(Time Complexity)。
常见的时间复杂度按数量级递增排列依次为:常量阶O(1)、对数阶O(log2 n)、线性阶O(n)、 第 1 章 绪论 14 数据结构 (C语言版) (第2版) ——双色版 线性对数阶O(nlog2 n)、平方阶O(n2)、立方阶 O(n3)、……、k次方阶O(nk)、指数阶O(2n)等。
3. 最好、最坏和平均时间复杂度
称算法在最好情况下的时间复杂度为最好时间复杂度,是指算法计算量可能达到的最小值;
称算法在最坏情况下的时间复杂度为最坏时间复杂度,是指算法计算量可能达到的最大值;
算法的平均时间复杂度是指算法在所有可能情况下,按照输入实例以等概率出现时,算法 计算量的加权平均值。
1.4.4 算法的空间复杂度
采用渐近空间复杂度(Space Complexity)作为算法所需存储空间的量度,简称空间复杂度,它也是问题规模n的函数,记作: S(n) = O(f (n))
若算 法执行时所需要的辅助空间相对于输入数据量而言是个常数,则称这个算法在原地工作,辅助空间为O(1)
对于一个算法,其时间复杂度和空间复杂度往往是相互影响的,当追求一个较好的时间复杂 度时,可能会导致占用较多的存储空间,即可能会使空间复杂度的性能变差,反之亦然。
1.5 小结
(1)数据结构是一门研究非数值计算程序设计中的操作对象,以及这些对象之间的关系和 操作的学科。
(2)数据结构包括两个方面的内容:数据的逻辑结构和存储结构。同一逻辑结构采用不同的存储方法,可以得到不同的存储结构。
① 逻辑结构是从具体问题中抽象出来的数学模型,从逻辑关系上描述数据,它与数据的存 储无关。根据数据元素之间关系的不同特性,数据的逻辑结构通常有4类基本逻辑结构:集合结 构、线性结构、树结构和图结构。
② 存储结构是逻辑结构在计算机中的存储表示,有两类存储结构:顺序存储结构和链式存 储结构。
(3)抽象数据类型是指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上 的一组操作的总称,具体包括3个部分:数据对象、数据对象上关系的集合,以及对数据对象的 基本操作的集合。
(4)算法是为了解决某类问题而规定的一个有限长的操作序列。算法具有5个性质:有穷 性、确定性、可行性、输入和输出。一个算法的优劣应该从以下四方面来评价:正确性、可读性、健壮性和高效性。
(5)算法分析的两个主要方面是算法的时间复杂度和空间复杂度,以考察算法的时间和空 间效率。一般情况下,鉴于运算空间较为充足,故将算法的时间复杂度作为分析的重点。算法 执行时间的数量级称为算法的渐近时间复杂度,T(n) = O(f(n)),它表示随着问题规模 n 的增大, 算法执行时间的增长率和f (n)的增长率相同,简称时间复杂度。

浙公网安备 33010602011771号