数据结构 表基础与构造
本文最后更新于 2025年9月11日 凌晨
数据的组织
线性表
线性表指的是数据的组织是线性有序的,结构在一定的规则下“顺序”存储,中间不存在为空的元素。
随机访问与数组
通过一个确定的地址+地址偏移量完成对其他元素的访问。数组是这种思想的最直观例子,当我们定义了一个数组后,通过偏移量,完成对后续数组中的元素进行访问。
1 | |
通过确定的地址和偏移量信息,完成了对其他元素的查找。除此之外,C语言中的结构体定义,也是在内存中紧密排列的(绝大部分,某些定义中会进行字节对齐)。通过这种手段,可以使用确定的偏移量获取到其中的某一段数据,并通过强制转换的方式,将对应的这段数据转换为预先定义好的结构体,交给后续的函数处理。很多基于C的系统就是通过这样的偏移+强制转换的方式完成的数据处理
指针与链表
指针是一个占用4字节内存地址的结构,在该段内存中,存储了另一个内存地址。当我们定义了一个指针时,就通过一个指针存储了目标结构的地址信息
1 | |
我们可以看到,在指针定义时,p表示的是该指针在内存中的起始地址,当我们进行解引用时,通过该指针结构查询到实际结构的内存地址,并且根据类型从实际结构的内存地址中获取数据,最终得到a的值。通过这种方式,我们可以更加灵活的记录和访问数据。而链表就是依赖于指针技术实现的线性表技术。它通过定义一系列的函数,完成结构的添加/删除/遍历过程。达到以一种灵活有序的结构管理数据的目的。
单链表实现
一个最简单的单链表需要具备以下结构
对于链表来说: 仅需要存储指向头节点的指针
对于中间节点来说: 需要定义存储的值和指向下一个节点的指针
函数签名:
1 | |
实现:
1 | |
散列表
散列表指的是在存储上,结构的存储是无序的,结构与结构“之间”可能存在空的部分。
哈希计算与哈希集合
哈希本身是一种通过筛选进行的数据查询,通过一个哈希函数,我们可以得到某个结构的特征,最终可以获得该结构与存储位置的特征,存储/读取时的哈希函数不会发生变化,那么通过这个函数维护的映射关系就是固定的,于是我们就可以通过这种关系,来完成结构的存储和查找。
1 | |
使用hash的一大优点是通过hash就可以直接从数组中取出值,其时间复杂度为O(1)。但是 hash的缺点是数据的存储过程是无序的,因为它是依照数据的特征进行的存储,而非按照存储的顺序完成的存储。但同时因为不需要顺序插入,就不再需要像链表一样遍历整个链表来决定插入位置,也不需要像数组那样需要提前获取偏移量信息,能够更便捷的访问到存储的数据。
表结构数据组织的局限性
1. 数组(Array)
优势 :提供有序的逻辑存储,支持O(1)随机访问
局限 :存储布局与物理位置强耦合,插入/删除操作需要大量元素移动
- 插入操作最坏时间复杂度:O(n)
- 根本原因:连续内存布局要求保持元素间的相对位置
2. 链表(Linked List)
优势 :保持插入顺序,通过指针灵活管理元素关联
局限 :存储位置与逻辑顺序分离,只能通过节点间链接进行顺序访问
- 查询操作最坏时间复杂度:O(n)
- 根本原因:缺乏随机访问能力,必须从头节点开始遍历
3. 哈希表(Hash Table)
优势 :通过哈希函数映射实现O(1)平均查询效率
局限 :存储位置完全由哈希值决定,无法维护元素间的顺序关系
- 遍历操作:无法保证有序性
- 根本原因:哈希散列破坏了元素的原始顺序信息
局限性的本质
这些权衡的根本原因在于: 单一组织方式只能优化一个维度的性能 。每种结构的存储策略决定了其擅长的操作类型,同时也限制了其他方面的表现。
受限表结构
所谓受限表结构,就是对于表结构只提供了部分受限方法,来达成一些面向应用的处理过程
队列
队列结构就是一种受限表结构,其提供的方法只允许数据依照LILO的方式被存储/读取。
队列在编程中存在广泛应用,它提供了一种手段用于数据的临时有序存储,经典的生产者/消费者就是依赖于队列的数据结构,进行生产消费过程。作为一种消息中间件,解耦了生产者和消费者之间的强绑定关系
栈
栈结构也是一种受限表结构,其提供的方法只允许数据依照LIFO的方式被存储/读取。
栈结构主要应用于函数栈/运算/以及存在嵌套执行的调用关系上,系统栈以及各种编程语言的本地方法栈都是通过栈结构完成的函数执行。