01 数据结构基础:数据的逻辑结构(集合、线性、树形、网状)或(线性与非线性)、数据的存储结构(顺序、链式、索引、散列)、数据的运算
目录
1 为什么要学习数据结构
2 数据结构是什么
2.1 宏观视角
2.2 微观视角
2.3 综合理解
2.4 数据结构的目的
3 数据结构和算法的关系
3.1 数据结构是算法的基础
3.2 算法依赖于数据结构
3.3 相互促进,共同发展
3.4 实际应用中的关系
4 数据结构与编程语言的关系
5 内存的理解
6 数据结构的研究方向
6.1 数据的逻辑结构
6.1.1 分类角度 1
6.1.1.1 集合结构
6.1.1.2 线性结构
6.1.1.3 树形结构
6.1.1.4 图形结构(网状结构)
6.1.2 分类角度 2
6.1.2.1 线性结构
6.1.2.2 非线性结构
6.1.3 小结
6.2 数据的存储结构
6.2.1 顺序存储结构
6.2.2 链式存储结构
6.2.3 索引存储结构
6.2.4 散列存储结构
6.3 数据的运算
6.3.1 资源管理
6.3.2 插入和删除
6.3.3 获取和遍历
6.3.4 修改和排序
1 为什么要学习数据结构
我们举一个形象的例子来理解程序中数据结构的作用:
- 战场:程序运行所需的软件、硬件环境。
- 敌人:项目或模块的功能需求。
- 指挥官:编写程序的程序员。
- 士兵和装备:一行一行的代码。
- 战术和策略:数据结构。
上图:没有战术,打仗事倍功半。
上图:有战术,打仗事半功倍。
由此可见,数据结构和算法才是程序的核心。但现实情况却是很多工作了三五年的程序员并不重视数据结构和算法,他们所实现的程序往往运行效率低且难以维护。
2 数据结构是什么
2.1 宏观视角
数据结构是一种将数据以特定的方式组织起来的方法,旨在提高数据的访问、管理和处理效率。这种组织方式不仅考虑到了数据的存储形式,还包括了如何有效地对其进行搜索、排序、插入、删除等操作。简而言之,数据结构是为了解决实际问题而设计的一种数据的组织和存储方案。
2.2 微观视角
从更细致的角度来看,数据结构是由一组数据元素组成的集合,其中每个元素都可能与其他元素存在某种特定的关系。这里的 “关系” 可以是指元素之间的前后顺序、层级关系或是其他任何形式的关联。数据结构的本质在于它不仅包含数据本身,还包含了这些数据之间的逻辑联系,即数据之间的结构化信息。
2.3 综合理解
综合上述两个视角,我们可以得出一个更为全面的定义:数据结构是指按照一定规则组织在一起的数据元素的集合,以及这些元素之间存在的关系。 它关注于如何高效地存储、访问和操作这些数据元素,而不直接涉及数据的具体内容。数据结构的设计和选择直接影响到程序的性能,包括运行时间和空间消耗等方面。
2.4 数据结构的目的
- 提高效率:通过合理选择和设计数据结构,可以显著提高数据处理的速度和效率。
- 简化问题:使用恰当的数据结构可以使复杂的问题变得简单明了,便于理解和解决。
- 支持抽象:数据结构提供了一种高级抽象,使得开发者可以专注于解决问题的核心逻辑,而不是底层实现细节。
- 促进代码复用:优秀的数据结构设计通常具有高度的通用性和可扩展性,有利于代码的重用和维护。
3 数据结构和算法的关系
3.1 数据结构是算法的基础
提供数据组织方式:数据结构为算法提供了存储和组织数据的方式。不同的数据结构适用于不同的应用场景,选择合适的数据结构可以显著提高算法的效率。例如,数组适用于随机访问,而链表则更适合频繁的插入和删除操作。
定义操作接口:数据结构定义了一系列基本操作,如插入、删除、查找等。这些操作是算法执行的基本单元,算法通过调用这些操作来实现其功能。
影响算法设计:数据结构的选择直接影响算法的设计思路。例如,使用二叉搜索树可以设计高效的查找算法,而使用哈希表可以实现快速的键值对查找。
3.2 算法依赖于数据结构
实现算法功能:算法是解决问题的一系列步骤,而这些步骤通常需要对数据进行各种操作。数据结构为这些操作提供了具体的实现方法。没有合适的数据结构,再好的算法也无法高效地执行。
优化算法性能:通过选择合适的数据结构,可以优化算法的时间复杂度和空间复杂度。例如,使用平衡二叉搜索树可以在对数时间内完成查找、插入和删除操作,而使用普通数组则需要线性时间。
提高算法灵活性:数据结构的多样性和灵活性使得算法可以适应不同的需求和场景。例如,图数据结构可以用于解决最短路径、网络流等问题,而堆数据结构则适用于优先队列和堆排序。
3.3 相互促进,共同发展
算法推动数据结构的发展:新的算法需求常常促使人们设计出新的数据结构。例如,为了高效地处理大规模数据,分布式数据结构应运而生。
数据结构促进算法创新:新的数据结构也为算法设计提供了新的思路和工具。例如,布隆过滤器是一种空间高效的概率型数据结构,广泛应用于缓存过滤和大数据处理中。
3.4 实际应用中的关系
软件开发:在软件开发中,选择合适的数据结构和算法是提高系统性能的关键。例如,数据库管理系统中广泛使用 B 树和 B+ 树来高效地管理索引。
科学研究:在科学研究中,数据结构和算法的结合可以解决复杂的计算问题。例如,生物信息学中的序列比对算法依赖于高效的字符串匹配数据结构。
工程实践:在工程实践中,数据结构和算法的应用无处不在。例如,搜索引擎的倒排索引数据结构和 PageRank 算法共同实现了高效的网页排名和检索。
4 数据结构与编程语言的关系
数据结构本身与编程语言是无关的。
数据结构是一种抽象的概念,描述了数据的组织和存储方式,以及数据元素之间的关系。这一概念独立于具体的编程语言,类似于数学定理与描述它的自然语言之间的关系。例如,勾股定理无论用中文、英文、法文还是其他任何语言表达,其本质和内容都是相同的。
然而,要学习和应用数据结构,必须掌握至少一种编程语言。编程语言提供了实现数据结构的具体工具和方法,使得数据结构的概念能够转化为实际的代码,并在计算机上运行和测试。理解数据结构与编程语言之间的关系,对于学习和应用数据结构至关重要。
5 内存的理解
一般而言,数据结构主要针对的是内存中的数据。因此,在学习数据结构之前,需要对内存有一个基本的了解。
内存由许多存储单元组成,每个存储单元可以存储一个固定大小的数据块,通常以字节(Byte)为单位。
每个存储单元都有一个唯一的地址,操作系统通过这些地址来访问内存中的数据。
在数据结构中,数据元素保存在这些内存单元中。这些数据元素可以存储在连续的内存单元中,也可以存储在分散的内存单元中。连续存储的典型例子是数组,而分散存储的典型例子是链表。
6 数据结构的研究方向
6.1 数据的逻辑结构
数据的逻辑结构描述了数据元素之间的逻辑关系,通常表示为 ,其中包含两个要素:
- 数据元素:构成数据结构的基本单位。
- 关系:数据元素之间的逻辑关系。
根据数据元素间关系的不同特性,逻辑结构可以分为以下几种基本类型:
6.1.1 分类角度 1
逻辑结构有四种基本类型:
6.1.1.1 集合结构
定义:数据结构中的元素之间除了 “同属一个集合” 的相互关系外,没有其他关系。
特点:数据之间最弱的一种关系。
6.1.1.2 线性结构
定义:结构中的元素存在一对一的相互关系,所有数据元素按顺序排列,强调数据元素的前后顺序。
特点:结构中必须存在唯一的首元素和唯一的尾元素。
6.1.1.3 树形结构
定义:结构中的元素存在一对多的相互关系,所有数据元素按层次结构组织,强调元素之间的父子关系。
特点:根节点位于最顶层,每个节点可以有零个或多个子节点。
6.1.1.4 图形结构(网状结构)
定义:数据结构中的元素存在多对多的相互关系,由顶点和边组成,顶点对应数据元素,边连接两个顶点,表示两个数据元素之间的关系。
特点:顶点之间可以有任意数量的连接。
6.1.2 分类角度 2
由于线性结构较为突出,习惯上将逻辑结构分为:
6.1.2.1 线性结构
定义:数据元素保持在一个线性序列中。
示例:线性表(顺序表、链表)、栈、队列、字符串、数组、广义表。
6.1.2.2 非线性结构
定义:各个数据元素不再保持在一个线性序列中。
示例:集合结构、树结构、图结构。
6.1.3 小结
数据的逻辑结构反映了数据元素之间的逻辑关系,是独立于计算机的,与数据的存储无关。具体来说,逻辑结构与以下几点无关:
- 数据元素本身的形式和内容。
- 数据元素的相对位置。
- 所含结点的个数。
例如,栈属于线性结构,是一种逻辑结构,可以采用顺序存储(如数组实现)或链式存储(如链表实现)。
6.2 数据的存储结构
数据的存储结构,又称为物理结构,是数据的逻辑结构在计算机中的表示(又称映像)。它包括数据元素的表示和关系的表示(即逻辑关系)。
数据的存储结构是逻辑结构用计算机语言的实现,它依赖于具体的编程语言。
为了合理利用计算机的存储空间,研究出了两种基本的存储方式:顺序存储结构、链式存储结构。
由两类基本的存储方式,又引申出两类存储结构:索引存储结构、散列存储结构。
6.2.1 顺序存储结构
顺序存储结构将逻辑上相邻的数据元素存储在物理位置上也相邻的存储单元中,即所有的元素依次存放在一片连续的存储空间中。
在 C 语言中,使用一维数组表示顺序存储结构。
- 优点:
- 数据元素存放的地址是连续的,支持下标访问,可以随机存取表中元素。
- 缺点:
- 插入和删除操作需要移动大量元素,效率较低。
- 为了防止内存溢出,需要在创建数组时指定一个最大容量。如果实际使用的元素个数超过这个容量,需要重新分配更大的内存空间,并将原有数据复制过去,这会导致额外的开销。
- 如果预先分配的空间大于实际使用的空间,会造成内存浪费。
6.2.2 链式存储结构
链式存储结构将逻辑上相邻的数据元素存储在物理上可以不相邻的存储空间中。每个数据元素构造一个结点(用结构体类型表示),结点中除了存放数据本身以外,还存放指向下一个结点的指针(pointer)。
在 C 语言中,使用指针来表示数据元素之间的这种结构。
- 优点:
- 克服了顺序存储结构中预知元素个数的缺点。链式存储结构中的每个节点都是单独分配内存的,不需要预先知道总共有多少个节点。
- 插入和删除操作灵活,只需改变节点中的指针,无需移动节点。
- 缺点:
- 需要额外的空间来存储指针,因此相同内存空间内存储的数据量比顺序存储少。
6.2.3 索引存储结构
索引存储结构类似于书的目录,通过建立数据元素的索引来快速检索数据。索引存储在存储数据元素之外,同时建立数据元素的目录。
例如 MySQL 数据库的索引设计方案。
- 优点:
- 用节点的索引号来确定节点存储地址,检索速度快。
- 缺点:
- 增加了附加的索引表,会占用较多的存储空间。
- 在增加和删除数据时需要修改索引表,会花费较多的时间。
6.2.4 散列存储结构
散列存储结构根据数据元素的关键字直接计算出该元素的关键码,由关键码决定其存储地址,又称为 Hash 存储。在初中,我们就学过一种能够将一个 x 值通过一个函数获得对应的一个 y 值的操作,叫做映射。散列表的实现原理正是映射的原理。在映射的过程中,事先设定的函数称作散列函数或者哈希函数。
- 优点:
- 检索、增加或删除节点的操作都很快。
- 缺点:
- 不支持排序。
- 一般比用线性表存储需要更多的空间。
- 记录的关键字不能重复,否则会导致冲突。
6.3 数据的运算
施加在数据上的运算包括运算的定义和实现。运算的实现是针对存储结构的,具体操作步骤如下:
6.3.1 资源管理
- 分配资源:为数据结构分配必要的存储空间。
- 建立结构:初始化数据结构,设置初始状态。
- 释放资源:在数据结构不再使用时,释放分配的存储空间,避免内存泄漏。
6.3.2 插入和删除
- 插入:在指定位置添加新的数据元素。不同的存储结构有不同的插入方法,例如,顺序存储结构需要移动后续元素,而链式存储结构只需调整指针。
- 删除:移除指定位置的数据元素。同样,不同的存储结构有不同的删除方法,顺序存储结构需要移动后续元素,链式存储结构只需调整指针。
6.3.3 获取和遍历
- 获取:访问数据结构中的特定元素。例如,数组可以通过下标直接访问,链表则需要从头节点开始逐个查找。
- 遍历:依次访问数据结构中的所有元素。遍历方法因数据结构而异,例如,数组可以使用循环遍历,树结构可以使用前序、中序或后序遍历。
6.3.4 修改和排序
- 修改:更新数据结构中特定元素的值。修改操作通常涉及查找目标元素并更改其内容。
- 排序:对数据结构中的元素进行排序,使其按照某种顺序排列。常见的排序算法有冒泡排序、插入排序、快速排序等,不同的数据结构适用的排序算法也不同。