0.2 数据结构与算法
0.2 数据结构与算法
---
数据结构是程序的骨架,而算法则是程序的灵魂。
《算法 + 数据结构 = 程序》 是 Pascal 语言之父 Niklaus Emil Wirth 所著的经典著作,其书名也成为计算机科学领域广为流传的名言。这句话深刻揭示了算法与数据结构在程序设计中的密切关系。
在正式学习之前,我们首先要搞清楚:什么是算法?什么是数据结构?为什么要学习它们?
简而言之,「算法」是解决问题的方法或步骤。如果把问题看作一个函数,算法就是将输入转化为输出的过程。「数据结构」则是数据在计算机中的组织方式及其相关操作。「程序」就是算法和数据结构的具体实现。
如果把「程序设计」比作烹饪,「数据结构」就像是食材和调料,「算法」则是不同的烹饪方法或菜谱。不同的食材、调料和烹饪方式可以组合出千变万化的菜肴。同样的原料,不同的人做出来,味道也会大不相同。
为什么要学习算法和数据结构呢?
还是以做菜为例。做菜讲究「色香味俱全」,而程序设计则追求为问题选择最合适的「数据结构」,并采用更高效、占用资源更少的「算法」。
学习算法和数据结构,能够帮助我们在编程时从时间复杂度和空间复杂度等角度优化解决方案,提升逻辑思维能力,写出高质量的代码,从而增强编程能力,获得更好的职业发展。
当然,正如掌握了食材和烹饪方法并不代表就能做出美味佳肴,学会了算法和数据结构也不意味着就能写出优秀的程序。这需要不断实践、思考和持续学习,才能真正成长为一名优秀的 厨师(程序员)。
1. 数据结构
数据结构(Data Structure):具有特定结构特征的数据元素的集合。
简而言之,「数据结构」 就是数据的组织结构,用来组织、存储数据。
进一步来说,数据结构关注的是数据的逻辑结构、物理结构及其相互关系,并针对这些结构定义相应的操作和算法,确保经过操作后数据依然保持原有的结构特性。
数据结构的核心作用在于提升计算机资源的利用效率。例如,操作系统想要查找硬盘中「Microsoft Word」的存储位置,如果采用全盘扫描,则效率极低;而通过「B+ 树」索引,可以迅速定位到 Microsoft Word
,进而快速获取其文件信息和磁盘位置。
学习数据结构,能够帮助我们理解和掌握数据在计算机中的组织与存储方式,从而为高效编程打下坚实基础。
通常,我们可以从**「逻辑结构」和「物理结构」**两个维度对数据结构进行分类。
1.1 数据的逻辑结构
逻辑结构(Logical Structure):指数据元素之间的相互关系。
根据数据元素之间的关系,数据的逻辑结构通常分为以下四类:
1.1.1 集合结构
集合结构:数据元素属于同一个集合,彼此之间没有其他关系。
集合结构中的数据元素是无序且互不相同的,每个元素在集合中只出现一次。这种结构与数学中的「集合」概念非常相似。

1.1.2 线性结构
线性结构:数据元素之间存在严格的「一对一」关系。
在线性结构中,除第一个元素和最后一个元素外,每个数据元素都仅与前后各一个元素相邻。常见的线性结构有:数组、链表,以及基于它们实现的栈、队列等。此外,哈希表在底层实现时也常依赖数组或链表等线性结构。

1.1.3 树形结构
树形结构:数据元素之间存在「一对多」的层次关系。
树形结构最典型的例子是二叉树,其基本形式包括根节点、左子树和右子树,而每个子树又可以递归地包含自己的子树。除了二叉树之外,树形结构还包括多叉树、字典树等多种类型,广泛用于表达具有层级关系的数据。

1.1.4 图形结构
图形结构:数据元素之间存在「多对多」的关系。
图形结构是一种比树形结构更为复杂的非线性结构,常用于描述对象之间任意的关联关系。在图结构中,数据元素被称为 「顶点」(或 「结点」),顶点之间通过 「边」(可以是直线或曲线)相连,形成复杂的网络。
与树形结构不同,图形结构允许任意两个顶点之间建立连接,顶点之间的邻接关系没有限制。常见的图结构类型包括:无向图、有向图、连通图等。

1.2 数据的物理结构
物理结构(Physical Structure):指数据的逻辑结构在计算机中的具体存储方式。
在计算机中,常见的物理存储结构主要有两种:顺序存储结构 和 链式存储结构,它们被广泛应用于各类数据结构的实现。
1.2.1 顺序存储结构
顺序存储结构(Sequential Storage Structure):指将所有数据元素依次存放在一块地址连续的存储空间中,元素之间的逻辑关系通过它们在物理存储上的相对位置来体现。

在顺序存储结构中,逻辑上相邻的数据元素在物理地址上也紧密相邻。
顺序存储结构的优点在于:结构简单、易于理解,并且能够高效利用存储空间。其缺点主要包括:必须预先分配一块连续的存储空间,灵活性较差;在插入、删除等需要移动大量元素的操作时,时间效率较低。
2. 链式存储结构
链式存储结构(Linked Storage Structure):指将数据元素存放在内存中的任意存储单元,这些单元可以是连续的,也可以是不连续的。

在链式存储结构中,逻辑上相邻的数据元素在物理地址上不要求相邻,实际存储位置是随机的。通常,每个数据元素及其相关信息被组合成一个「链结点」。每个链结点除了存放数据本身外,还包含一个「指针(或引用)」,用于指向下一个逻辑上相邻的链结点。也就是说,数据元素之间的逻辑关系是通过指针来连接和体现的。
链式存储结构的主要优点在于:无需预先分配一整块连续的存储空间,能够根据需要动态申请和释放内存,避免空间浪费;在插入、删除等操作时,通常只需修改指针,效率较高。其缺点是:每个链结点除了存储数据外,还需额外存储指针信息,因此整体空间开销相较于顺序存储结构更大。
2. 算法
算法(Algorithm):解决特定问题求解步骤的准确而完整的描述,在计算机中表现为一系列指令的集合,算法代表着用系统的方法描述解决问题的策略机制。
简而言之,「算法」 就是解决问题的具体方法和步骤。
进一步来说,算法是一系列有序的运算步骤,能够为某一类计算问题提供通用的解决方案。对于任意合法输入,算法都能按照既定步骤逐步执行,最终得到正确的输出。算法本身与具体的编程语言无关,可以用 自然语言、编程语言(如 Python、C、C++、Java 等),也可以用 伪代码或流程图 等多种方式进行描述。
下面通过几个例子来直观理解什么是算法。
- 示例 1:
问题描述:
- 如何从上海前往北京?
解决方法:
- 选择乘坐飞机,速度最快但费用最高。
- 选择长途汽车,费用最低但耗时最长。
- 选择高铁或火车,时间和费用都较为适中。
- 示例 2:
问题描述:
- 如何计算 的和?
解决方法:
- 依次用计算器从 加到 ,最终得到 。
- 利用高斯求和公式:和 = (首项 + 末项) × 项数 ÷ 2,直接计算得 。
- 示例 3:
问题描述:
- 如何将一个包含 个整数的数组按升序排列?
解决方法:
- 使用冒泡排序对数组进行升序排序。
- 也可以选择插入排序、归并排序、快速排序等其他排序算法实现升序排列。
上述三个示例中的解决方法都属于算法。从上海到北京的出行方案是一种算法,对 到 求和的方法是一种算法,对数组排序的方法同样是一种算法。可以看出,对于同一个问题,往往存在多种不同的算法可供选择。
2.1 算法的基本特性
算法本质上是一组有序的运算步骤,用于解决特定的问题。除此之外,算法 还必须具备以下五个基本特性:
- 输入:算法需要接收外部提供的信息作为处理对象,这些信息称为输入。一个算法可以有零个、一个或多个输入。例如,示例 的输入是出发地和目的地(如上海、北京),示例 的输入是由 个整数构成的数组,而示例 针对的是固定问题,可以视为没有输入。
- 输出:算法的执行结果必须有明确的输出,即至少有一个输出结果。比如,示例 的输出是最终选择的交通方式,示例 的输出是求和的结果,示例 的输出是排好序的数组。
- 有穷性:算法必须在有限的步骤内终止,并且能够在合理的时间内完成。如果算法无法在有限时间内结束,就不能称为有效的算法。例如,若五一假期从上海到北京旅游,三天都没决定交通方式,计划就无法实现,这样的「算法」显然不合理。
- 确定性:算法中的每一步操作都必须有明确、唯一的含义,不能存在歧义。也就是说,任何人在相同输入下执行算法,得到的中间过程和最终结果都应一致。
- 可行性:算法的每一步都必须是可执行的,即在现有条件下能够通过有限次数的操作实现,并且可以被计算机程序实现并运行,最终得到正确的结果。
2.2 算法追求的目标
研究算法的核心目的,是让我们以更高效的方式解决问题。对于同一个问题,往往存在多种算法可选,而不同算法的“代价”也各不相同。一般来说,优秀的算法应当重点追求以下两个目标:
- 更少的运行时间(更低的时间复杂度)
- 更小的内存占用(更低的空间复杂度)
举例来说,假设计算机执行一条指令需要 纳秒。若某算法需 纳秒,另一算法只需 纳秒,在不考虑内存消耗的前提下,显然后者更优。再比如,若某算法只需 字节内存,另一算法需 字节,在不考虑运行时间的情况下,前者更优。
实际应用中,算法设计往往需要在运行时间和空间占用之间权衡。理想情况下,算法既快又省空间,但现实中常常需要根据具体需求做出取舍。例如,当程序运行速度要求较高时,可以适当增加空间消耗以换取更快的执行速度;反之,如果设备内存有限且对速度要求不高,则可以选择更节省空间的算法,即使牺牲一些运行时间。
除了运行时间和空间占用,优秀的算法还应具备以下基本特性:
- 正确性:算法能准确满足问题需求,程序运行无语法错误,能通过典型测试,达到预期目标。
- 可读性:算法结构清晰,命名规范,注释恰当,便于理解、维护和后续修改。
- 健壮性:算法能合理应对非法输入或异常操作,具备良好的容错能力。
这三点是算法的基本要求,所有算法都必须满足。而我们评价一个算法是否优秀,通常最看重的还是其运行时间和空间占用两个方面。
3. 总结
3.1 数据结构
数据结构通常分为 逻辑结构 和 物理结构 两大类。
- 逻辑结构:描述数据元素之间的关系,主要包括:集合结构、线性结构、树形结构 和 图形结构。
- 物理结构:指数据在计算机中的实际存储方式,主要有:顺序存储结构 和 链式存储结构。
逻辑结构强调数据元素之间的相互关系,而物理结构则关注这些关系在计算机内的具体实现。例如,线性表中的「栈」在逻辑上属于线性结构,元素之间是一对一的关系(除首尾元素外,每个元素有唯一前驱和后继)。在物理实现上,栈可以采用顺序存储(即 顺序栈,元素在内存中连续存放),也可以采用链式存储(即 链式栈,元素在内存中不一定连续,通过指针连接)。
3.2 算法
算法 是解决特定问题的有序操作步骤的集合。
一个算法应具备以下五个基本特性:输入、输出、有穷性、确定性、可行性。
优秀的算法通常追求以下目标:正确性、可读性、健壮性、更低的时间复杂度(运行时间更短) 和 更低的空间复杂度(占用内存更小)。
参考资料
- 【文章】数据结构与算法 · 看云
- 【书籍】大话数据结构——程杰 著
- 【书籍】趣学算法——陈小玉 著
- 【书籍】计算机程序设计艺术(第一卷)基本算法(第三版)——苏运霖 译
- 【书籍】算法艺术与信息学竞赛——刘汝佳、黄亮 著