常见的数据结构(理论)

常见的数据结构

本文只介绍常见数据结构的理论知识,适用个人学习。

线性结构:顺序表,链表,栈和队列;

非线性结构:散列表(哈希表),树,堆,图;

1. 顺序表(数组)Array

顺序表是用一组地址连续的存储单元依次存储线性表的数据元素

  • 优点:随机访问性强,因为可以用起始地址+元素序号访问

  • 缺点:插入删除操作效率低

  • 插入操作:在顺序表中间插入一个元素时,需要移动其后的所有元素;平均移动元素个数为n/2((最好情况+最坏情况)/2),时间复杂度为O(n)。

  • 删除操作:在顺序表中间删除一个元素时,需要移动其后的所有元素;平均移动元素个数为(n-1)/2,时间复杂度为O(n)。

无论是静态顺序表还是动态顺序表,如果要增容,通常是2的倍数增加,还需将原数据复制到新数组并释放内存,劣势明显,效率低浪费资源。

2. 链表(List)

链表是一系列节点组成,这些节点在逻辑上是线性排列的,但节点在物理内存中是非连续存储的。

  • 优点:动态性好,链表不需要预先分配固定大小的存储空间,可以根据需要动态地插入和删除节点,有效地利用内存空间;插入和删除操作效率高(在已知位置的情况下)。

  • 缺点:随机访问性效率低,额外的存储空间存储了指针。

  • 插入和删除操作的时间复杂度为O(1)(在已知位置的情况下),查找的时间复杂度为O(n)。

单链表:每个节点只有一个指针域,指向下一个节点。链表的头节点是整个链表的起始点,通过头节点的指针可以访问到链表中的其他节点。尾节点的指针域为 NULL,表示链表的结束。循环单链表就是最后一个节点指向第一个节点,整体构成一个链环。

在这里插入图片描述

双链表:每个节点有两个指针域,一个指向前一个节点,一个指向后一个节点。双链表有头节点和尾节点,头节点的前指针为 NULL,尾节点的后指针为 NULL。同理循环双向链表是最后一个节点指向第一个节点。

3. 栈(Stack)

栈是一种只能在一端进行插入和删除操作的线性表。特点:后进先出(Last In First Out,LIFO)的原则存储数据,即最后进入栈的元素最先被移除。

入栈(Push):新元素添加到栈顶的操作

出栈(Pop):栈顶元素从栈中移除的操作

应用场景:函数调用,中缀表达式,浏览器的后退功能

4. 队列(Queue)

队列是一种特殊的线性表,只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,遵循先进先出(First In First Out,FIFO)的原则。

入队(Enqueue):进队操作,是将新元素添加到队列的末尾

出队(Dequeue):队列的头部的元素移除,每次出队都是移除最早进入的元素

在这里插入图片描述

顺序队列和链队列优缺点:顺序队列可能存在假溢出问题,而链队列不存在假溢出,并且试应数据的动态变化,但会消耗更多的空间(由于每个节点要存储指针)。

应用场景:任务调度,消息队列,打印任务管理

5. 散列表(Hash Table)

散列表,也称哈希表。根据关键码(key)和( value)而直接进行访问的数据结构

key当作数据的标识符,value就是数据,注意key可以不唯一。例如取key为人名,value表示性别。

  • key通过哈希/散列函数来计算它应该存储的位置,这个哈希函数的计算方法有多种(直接定址法,除留余数法,平方取中法等)。
  • 散列函数的主要作用是将关键码映射为一个整数(哈希值),这个哈希值代表关键码在哈希表中的存储位置(通常是数组的索引),

哈希冲突:使用哈希函数进行数据存储或查找时,不同的输入key值经过哈希函数计算后得到了相同的哈希值(索引值/存储位置)。所以尽可能均匀地分布在哈希表的各个位置上,以减少冲突的发生。

在这里插入图片描述

冲突解决:

  • 开放寻址法:冲突时,依次检查下个空位置插进去(但是这样容易产生聚集现象);所以也可按照二次函数计算下个位置。

  • 链地址法:将哈希表的每个位置设置成一个链表,有哈希冲突,就相同的哈希值(存储位置),插入这个链表的末尾。适用于数据量大,冲突频繁的情况。(上图所示)

6. 树(Tree)

树是一种非线性结构,由n(n>=1)个有限节点组成一个具有层次关系的集合。

每个节点有零个或多个子节点;每一个非根节点有且只有一个父节点

应用场景:文件系统,表达式树

节点概念
根节点没有父节点的节点
节点的度该节点拥有子树的个数
叶子节点度为0的节点,即没有子树
兄弟节点具有相同父节点的节点
层次节点的层次从根开始定义
二叉树类别概念
二叉树每个节点最多有 2 个子节点
满二叉树每层节点都达到了最大数量
完全二叉树除了最后一层,每层节点都达到了最大数量,并且最大层次中的叶子结点都依次排列在该层最左边
平衡二叉树左右子树的高度差的绝对值不超过 1

在这里插入图片描述

二叉树的性质

  • 任意一棵树,若结点数量为n,则边的数量为n-1(除根节点外,其他每个节点都连接上方的边)

  • 任意一棵二叉树中,度为 0 的结点(即叶子结点)总是比度为 2 的结点多一个

  • 深度为 k 的二叉树最多有 2^k - 1 个结点(k≥1) (可用等比数列的和计算或者把这个理解为2进制,相当于满位+1就进位,所以减1就是未进位的和)

  • 二叉树第 i 层上的结点数最多为 2^(i - 1)(i≥1) (理解成二进制第几位等价于多少十进制)

  • 具有n个节点的完全二叉树的深度为⨽log₂ⁿ⨼(下取整数)+1

7. 堆 (Heap)

堆是一种特殊的完全二叉树,分为最大堆和最小堆

堆中某个结点的值总是不大于或不小于其父结点的值。

最大堆:每个节点的值都 ≥ 它的子节点的值

最小堆:每个节点的值都 ≤ 它的子节点的值

在这里插入图片描述

存储方式:通常使用数组来存储堆中的元素。对于完全二叉树中的节点编号(从根节点开始,根节点编号为 1),节点 i 的左子节点编号为 2i,右子节点编号为 2i + 1,父节点编号为 i/2(向下取整)。

应用场景:优先队列,堆排序,内存管理

8. 图(Graph)

图是由顶点的有穷非空集合和顶点之间边的集合组成。可用G(V,E),V是顶点结合,E是边集合。

  • 顶点 V(Vertex):顶点也称为节点
  • 边 E(Edge):连接两个顶点的线段或弧线称为边;边可分为有向边和无向边。
  • 度(Degree):在无向图中,顶点的度是指与该顶点相关联的边的数目;在有向图中,顶点的度分为入度(该顶点为终点的有向边的数目)和出度(该顶点为起点的有向边的数目)。顶点的度等于入度和出度之和

存储结构:

  • 邻接矩阵(Adjacency Matrix):用一个二维数组来表示图。数组的行和列分别对应图中的顶点。如果顶点和顶点之间有边相连,那么数组中第行第列的元素值为 1
  • 优缺点:直观、易于实现和理解,可快速顶点之间是否有边相连,时间复杂度为O(1),但对于稀疏图(边的数量相对较少的图),会浪费大量的存储空间,因为大部分元素都是0。
  • 邻接表(Adjacency List):图的每个顶点建立一个单链表,链表中的每个节点表示与该顶点相邻的一个顶点。对于有向图,每个节点还可以包含一个表示边的方向的信息
  • 优缺点:对于稀疏图,节省存储空间,并且方便地遍历一个顶点的所有相邻顶点;对应两个顶点之间是否有边相连,时间复杂度可能为O(V),(V是顶点的个数)

图的遍历:

深度优先搜索(Depth-First Search,DFS)

  • 深度优先搜索是从图中的某个顶点出发,沿着一条路径尽可能深地探索,直到无法继续前进时,再回溯到上一个顶点,继续探索另一条路径。
  • 实现方法通常使用递归或栈来实现。
  • 步骤:
    • 从一个未被访问过的顶点出发,标记该顶点为已访问。
    • 对于该顶点的每个相邻顶点,如果该相邻顶点未被访问过,则递归地对该相邻顶点进行深度优先搜索

广度优先搜索(Breadth-First Search,BFS)

  • 广度优先搜索是从图中的某个顶点出发,先访问该顶点的所有相邻顶点,然后再依次访问这些相邻顶点的相邻顶点,直到访问完所有顶点。
  • 实现方法通常使用队列来实现。
  • 步骤:
    • 从一个未被访问过的顶点出发,标记该顶点为已访问,并将该顶点加入队列。
    • 当队列不为空时,取出队首顶点,对于该顶点的每个相邻顶点,如果该相邻顶点未被访问过,则标记该相邻顶点为已访问,并将该相邻顶点加入队列。

在这里插入图片描述

应用场景:社交网络分析,交通网络规划

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值