此篇文章仅基于自己对数据结构的理解,如果还有大佬有自己的想法真心希望您在下方留言
如果发现我有写的不好的地方恳请大家指出来
1.顺序表
1).静态顺序表
2.).动态顺序表
2.链表
1).单向链表
2).双向链表
3).顺序表和链表对比总结
3.栈和队列
4.二叉树
5.堆
1).堆排序
6.各数据结构使用场景
数据结构是计算机专业的核心必修课,也是计算机相关领域的基础理论知识。它和算法紧密联系,是理解程序运行逻辑、设计高效代码的关键,不管是软件开发、人工智能、大数据还是网络工程等方向,都离不开数据结构的支撑。
什么是数据结构?
数据结构是计算机中组织、存储和管理数据的方式,旨在高效地支持数据的访问、插入、删除等操作,是算法设计的基础。
我们常见的基础数据结构可分为两大类:
1.线性结构。比如:数组、链表、栈、队列
这里所谓的线性结构也就是逻辑上是连续的,它们在物理上的储存空间未必连续。
2.非线性结构。比如:树(二叉树、红黑树)、图、哈希表。这里我们只讲解二叉树。
它们在逻辑上是不连续的。
下面我们来认识认识它们
1.顺序表
顺序表是用一组地址连续的存储单元依次存储数据元素的线性结构,是数组在逻辑结构层面的抽象实现。
它的特点是元素物理存储位置与逻辑顺序一致,支持随机访问(通过下标直接定位元素),但插入、删除操作需要移动大量元素,时间复杂度较高。
顺序表有两种:静态顺序表、动态顺序表。
1).静态顺序表
静态顺序表是顺序表的一种,它使用固定长度的数组来存储数据元素,在创建时就确定了存储空间的大小,且运行过程中无法动态扩容。
因为存储空间固定,当元素个数达到数组长度上限时,就无法再插入新元素,容易出现“溢出”问题。
这里的size是顺序表里有效元素的个数
2).动态顺序表
动态顺序表是对静态顺序表的改进,它的存储空间大小可以在程序运行过程中动态扩容。
这里的size是顺序表里有效元素的个数,capacity是这个动态数组的容量
我们可以看到,对于静态顺序表来说,它提前就被分配好了空间大小,而动态顺序表却没有告诉分配的空间大小,意味着它可以动态扩容。下面我们重点讲解动态顺序表
1.初始化
让整型指针指向空,把size和capacity置为0
2.尾插
插入数据时首先要判断空间是否足够(这很重要,否则会出现越界访问的风险)
如果空间不够(size==capacity),那就申请空间。程序45、46行是对容量进行判断,如若开始的容量为0,那就先为它分配4个空间。然后更新容量capacity,将元素尾插到顺序表中,再次更新元素个数size。
3.在特定位置插入数据
它的逻辑和前面的尾插大部分一致,只是多了个移动元素的流程
4.特定位置删除
通过下标访问到要删除的位置的元素,将其后面的元素向前移动进行覆盖实现删除。
2.链表
1).单项链表
单向链表是一种线性数据结构(逻辑连续,空间不连续),由若干节点组成,每个节点包含数据域和指针域,指针域只指向链表中的下一个节点,尾节点的指针域指向空( null )。
1.尾插
定义时定义了节点指针,所以在尾插传参时传的是指针的地址,那么函数接受值就是二级指针(SLTNode*),最后就是将每个链表进行链接。
2).双向链表
双向链表是每个节点包含数据域、前驱指针和后继指针的线性数据结构,前驱指针指向当前节点的上一个节点,后继指针指向下一个节点,头节点前驱指针和尾节点后继指针通常指向 null 。
头结点的前驱指针指向链表的尾节点,尾结点的前驱指针指向
核心特点:
1. 支持双向遍历:既能从头节点向后遍历,也能从尾节点向前遍历,灵活性比单向链表更高。
2. 增删效率提升:已知目标节点时,删除或插入操作无需像单向链表那样从头遍历找前驱节点,时间复杂度仍为 O(1)。
3. 空间换时间:每个节点多存储一个指针,占用的内存空间略高于单向链表。
1.哨兵位
哨兵位(哨兵节点)的核心意义是统一链表操作逻辑,避免处理空指针和边界情况,让增删等操作的代码更简洁、不易出错。
它的前驱指针指向空,后继指针指向头结点。
具体作用体现在两点:
1. 消除边界判断:普通链表在头插、头删时,需要单独判断头节点是否为 null ;添加哨兵位(通常作为伪头节点)后,无论链表是否为空,头节点和其他节点的操作逻辑完全一致,不用额外区分。
2. 简化代码:无需在每次操作时检查指针是否越界,减少了 if 判断语句,降低代码的复杂度和出错概率。
举个例子,普通链表头删时要先判断头节点是否存在,而带哨兵位的链表直接删除哨兵后的第一个节点即可,逻辑完全统一。
这里我们加入了哨兵位,目的是为了增加增删效率。
2.尾插
首先要申请节点,将数据初始化,然后将节点进行链接,使之连续。
这里有一个小技巧:先连后继,再改前驱,最后处理边界
1. 定位目标位置:明确要插入的新节点 newNode 位于前驱节点 prev 和后继节点 next 之间。
2. 先绑定新节点的指针:先让 newNode.prev = prev 、 newNode.next = next ,这一步不会影响原有链表的结构。
3. 再修改原有节点的指针:接着更新 prev.next = newNode 、 next.prev = newNode ,顺序反过来容易导致 next 节点的指针丢失。
4. 哨兵/循环结构适配:如果是带哨兵位的双向链表,直接把哨兵当作普通节点处理即可,无需额外判断空链表;如果是循环结构,注意首尾节点和哨兵的互相指向。
3.尾删
尾删时只需找到尾结点的前一个节点和头结点,将其进行链接,释放尾结点即可。
4.查找
3).顺序表和链表对比总结
了解了顺序表和链表和它们的特性,那我们总结一下它们各自的优缺点
顺序表:
优点:
1.查找方便(支持随机访问)时间复杂度 O(1)
缺点:
1.空间不够时需要申请空间,尤其是当连续的空间不够的时候需要寻找一段合适的空间
将原有的数据拷贝过去,时间复杂度高O(N)
2.插入删除时需要挪动大量数据,时间复杂度高O(N)
3.申请的空间未必能全部利用到,会有空间浪费
链表:
优点:
1.增加元素时只需要申请节点就可以,删除时只需改变指针指向即可,灵活度高
时间复杂度低O(1)
2.需要时按需申请,不会有空间浪费
缺点:
1.不支持随机访问,需要遍历链表才能拿到想要的数据,时间复杂度高O(N)
总结:顺序表适用于频繁查找的对象,链表适用于频繁插入删除的对象。
3.栈和队列
栈和队列通常是用顺序表实现的,但是有链表实现的情况,主要看实际需求。
栈的特性:后进先出
对列的特性:先进先出
顺序表实现:适用于操作频繁、对效率要求高,且能预估数据规模的场景,比如编程语言内置的栈、队列结构大多基于此。
链表实现:适用于数据规模不确定、需要频繁扩容缩容的场景,比如处理动态变化的数据流。
这是栈和队列的一些常用到的接口。
4.二叉树
单一的二叉树并没有太大的利用价值,通常用到很多算法中。
二叉树是一种每个节点最多只有两个子节点的树形数据结构,这两个子节点分别被称为左子节点和右子节点。
二叉树有多种常见类型,比如满二叉树(除叶子节点外,每个节点都有两个子节点)、完全二叉树(按层序编号,节点编号与满二叉树一一对应)、二叉搜索树(左子树所有节点值小于根节点,右子树所有节点值大于根节点)等。
1.二叉树节点的申请
2.前序遍历
3.中序遍历
4.后序遍历
5.堆
大堆和小堆都属于完全二叉树结构,是堆排序和优先队列的核心数据结构,二者的核心区别在于父节点与子节点的数值大小关系。
大堆(大顶堆)
1. 定义:任意一个父节点的数值 大于或等于 其左右子节点的数值。
2. 特性:堆顶元素(根节点)是整个堆中的最大值。
小堆(小顶堆)
1. 定义:任意一个父节点的数值 小于或等于 其左右子节点的数值。
2. 特性:堆顶元素(根节点)是整个堆中的最小值。
1.堆排序
这个堆排序表面上是二叉树实现,但是实际上是基于数组实现的。因为我们不难发现,当我们把它们看成一个连续的集合,那么这些父子之间有一个规律,左孩子是父亲节点下标*2+1,右孩子是左孩子+1,既然它们有这样的规律,那我们就可以利用这个特性来实现堆排序。
1.插入数据
这是一个建大堆的算法,每次在尾部插入数据后,让它和父亲节点进行对比如果比父亲大,那就一直向上调整,直到满足大堆特性。
2.删除堆顶数据
当我们想要删除堆顶的数据时,如果直接删除堆顶的数据,那么堆的结构就被破坏了(根节点)没有数据,就不再是堆了。那我们不妨换个思想,将最后一个数据和堆顶的数据进行交换,删除最后面的数据。此时根节点还存在,只不过是不符合大堆的特性了,那就将根节点和左右孩子作比较,找出孩子中较大的数据和父亲进行交换,依次向下调整。
6.各数据结构使用场景
数据结构 日常接触的应用场景
顺序表(数组):
1. 手机相册的图片列表,按顺序存储且可快速滑动查看
2. 音乐播放器的歌曲列表,支持按索引快速定位曲目
3. Excel表格的单元格存储,通过行列下标直接访问
4. 编程语言中的数组、列表(如Python的list、Java的ArrayList)
链表 :
1. 浏览器的前进/后退功能,通过双向链表记录访问历史
2. 音乐播放器的随机播放列表,便于动态插入、删除歌曲
3. 操作系统的内存碎片管理,灵活分配不连续内存
4. 编程语言中的链表结构(如Java的LinkedList)
二叉树:
1. Windows、macOS的文件系统目录结构,文件夹和文件构成树形层级
2. 搜索引擎的关键词自动补全,基于二叉搜索树快速检索
3. 办公软件的多级菜单(如Word的菜单栏),层级分明
4. 数据库的索引结构(如B+树,基于二叉树优化)
堆(大顶堆/小顶堆):
1. 手机购物APP的商品推荐排序,按热度(大顶堆)或价格(小顶堆)优先展示
2. 任务管理器的进程优先级调度,高优先级进程优先执行(大顶堆)
3. 直播平台的弹幕热度排行,热度高的弹幕优先显示
4. 编程语言的优先队列(如Java的PriorityQueue),实现任务按优先级处理
栈:
1. 浏览器的页面跳转,新打开页面入栈,关闭页面出栈
2. 计算器的表达式求值(如逆波兰表达式计算)
3. 文本编辑器的撤销操作,每一步编辑动作入栈,撤销时出栈
4. 手机APP的页面导航(如从首页进入详情页,再返回首页)
队列 :
1. 外卖平台的订单排队,按下单顺序依次派单
2. 打印机的任务队列,按提交顺序依次打印文档
3. 医院的挂号排队叫号系统,先挂号先就诊
4. 操作系统的任务调度,按优先级或顺序执行进程