入门数据结构,看这一篇就够了!

1 数组

数组应该是最简单的数据结构了,所谓数组,便是可以在内存中连续存储多个元素的结构。需要注意,数组在内存中的分配是连续的,其中的元素通过数组下标进行访问且下标以0开始。

数组的优点如下:

  1. 结构简单
  2. 可在常数时间内访问数组元素

数组的缺点如下:

  1. 数组长度在定义数组时便已固定,不可修改,无法扩容
  2. 数组只能存储一种类型的数据
  3. 添加,删除操作耗时长
    在这里插入图片描述

2 链表

链表是最简单的动态数据结构。链表是物理存储单元上非连续的、非顺序的存储结构,每个元素包含两个结点,一个是存储元素的数据域,另一个是指针域。根据指针域中指针的指向,我们可以把链表细分为单向链表、双向链表以及循环链表。
在这里插入图片描述

链表的优点如下:

  1. 动态数据结构,不需考虑容量问题
  2. 添加,删除操作较快

链表的缺点如下:

  1. 与数组相比,不可随机访问,访问单个元素开销较大
  2. 链表中的额外指针引用需要浪费内存

2.1 单向链表

单向链表是最简单的链表形式,我们将链表中最基本的数据称为节点,那么每一个节点包含了数据块和指向下一个节点的指针。
在这里插入图片描述

2.2 双向链表

所谓双向,便是指有两个方向。与单向链表不同,双向链表的每一个节点不仅存储指向下一个节点的指针,而且存储指向前一个节点的指针。
在这里插入图片描述

2.3 循环链表

顾名思义,循环链表会达到“循环”的效果,即首尾相连。其具体实现方式为在链表的尾部增加一个指向头结点的指针,头结点也增加一个指向尾节点的指针。
在这里插入图片描述

3 栈

栈是限定仅在表尾(栈顶 top)进行插入和删除操作的后进先出的线性表。入栈指从栈顶放入元素,而出栈指从栈顶取出元素。
在这里插入图片描述

4 队列

队列是一种先进先出的线性表,简称FIFO(First In First Out)。队列是一种只允许在一端进行插入操作,而在另一端进行删除操作的线性表。允许插入的一端被称为队尾,允许删除的一端被称为队头。

在这里插入图片描述

5 树

树是由结点或顶点和边组成的(可能是非线性的)且不存在着任何环的一种数据结构。没有结点的树称为空树,一棵非空的树包括一个根结点,还可能有多个附加结点,所有结点构成一个多级分层结构。

在这里插入图片描述

以下内容参考:常用数据结构——树

5.1 二叉树

每个结点至多拥有两棵子树(二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。

其性质如下:

  1. 若二叉树的层次从 0 开始,则在二叉树的第 i 层至多有 2^i 个结点(i>=0)
  2. 高度为 k 的二叉树最多有 2^(k+1) - 1 个结点 (k>=-1) (空树的高度为 -1)
  3. 对任何一棵二叉树,如果其叶子结点(度为 0)数为 m, 度为 2 的结点数为 n, 则 m = n + 1

二叉树的遍历方法有以下三种:

  1. 中序遍历:即左-根-右遍历,对于给定的二叉树根,寻找其左子树;对于其左子树的根,再去寻找其左子树;递归遍历,直到寻找最左边的节点 i,其必然为叶子,然后遍历 i 的父节点,再遍历i的兄弟节点。随着递归的逐渐出栈,最终完成遍历
  2. 先序遍历:即根-左-右遍历
  3. 后序遍历:即左-右-根遍历

其中二叉树又分为以下三种:完美二叉树,完全二叉树,完满二叉树

5.1.1 完美二叉树(满二叉树)

除了叶子节点之外的每一个节点都有两个孩子,每一层(包括最后一层)都被完全填充。
在这里插入图片描述

5.1.2 完全二叉树

除了最后一层之外的其他每一层都被完全填充,并且所有节点都保持向左对齐。
在这里插入图片描述

5.1.3 完满二叉树

除了叶子节点之外的每一个节点都有两个孩子节点。
在这里插入图片描述

5.2 二叉查找树

二叉查找树也称为有序二叉查找树,其性质如下:

  1. 任意节点如果左子树不为空,则左子树的值均小于根节点的值
  2. 任意节点如果右子树不为空,则右子树的值均大于根节点的值
  3. 任意节点的左右子树也分别是二叉查找树
  4. 没有键值相等的节点

二叉查找树具有一定的局限性,在某些情况下,二叉查找树会退化成一个线性链。
在这里插入图片描述

5.3 AVL 树

AVL 树是带有平衡条件的二叉查找树,和红黑树相比,它是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过1)。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而旋转是非常耗时的。

AVL 树特点如下:

  1. AVL 树是一棵二叉搜索树
  2. AVL 树的左右子节点也是 AVL 树
  3. 每个节点的左右子节点的高度之差的绝对值最多为1,即平衡因子为范围为[-1,1]

AVL 树适合用于插入删除次数比较少,但查找多的情况。
在这里插入图片描述

5.4 红黑树

红黑树是一种自平衡二叉查找树,通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保从根到叶子节点的最长路径不会是最短路径的两倍,用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决。

红黑树的性质如下:

  1. 节点是红色或黑色
  2. 根节点是黑色
  3. 每个叶子节点都是黑色的空节点
  4. 每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点

至于红黑树与 AVL 树的比较,红黑树的查询性能略微逊色于 AVL 树,因为比 AVL 树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的 AVL 树最多多一次比较,但是,红黑树在插入和删除上大优于 AVL 树,AVL 树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于 AVL 树为了维持平衡的开销要小得多。

红黑树使用场景如下:

  1. 广泛用在 C++ 的 STL 中,map 和 set 都是用红黑树实现的
  2. 著名的 linux 进程调度 Completely Fair Scheduler 是用红黑树管理进程控制块的
  3. epoll 在内核中的实现,用红黑树管理事件块
  4. 在 nginx 中,用红黑树管理 timer
    在这里插入图片描述

5.5 B 树

B_TREE 是一种平衡多路查找树,是一种动态查找效率很高的树形结构。B_TREE 中所有结点的孩子结点的最大值称为 B_TREE 的阶,B_TREE 的阶通常用 m 表示,简称为 m 叉树。一般来说,应该是 m>=3。一颗 m 阶的 B_TREE 或是一颗空树,或者是满足下列条件的 m 叉树:

  1. 树中每个结点最多有 m 个孩子结点
  2. 若根结点不是叶子节点,则根结点至少有2个孩子结点
  3. 除根结点外,其它结点至少有( m/2 的上界)个孩子结点
  4. 结点的结构如下图所示,其中,n 为结点中关键字个数,(m/2 的上界)-1 <= n <= m-1;di(1<= i <= n)为该结点的 n 个关键字值的第 i 个,且di < d(i+1);ci(0<= i <=n)为该结点孩子结点的指针,且 ci 所指向的节点的关键字均大于或等于 di 且小于 d(i+1)
    在这里插入图片描述
  5. 所有的叶结点都在同一层上,并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)

B 树为大块数据的读写操作做了优化,同时它也可以用来描述外部存储。(支持对保存在磁盘或者网络上的符号表进行外部查找)

B_TREE 的查找类似二叉排序树的查找,所不同的是 B-树每个结点上是多关键码的有序表,在到达某个结点时,先在有序表中查找,若找到,则查找成功;否则,到按照对应的指针信息指向的子树中去查找,当到达叶子结点时,则说明树中没有对应的关键码。由于 B_TREE 的高检索效率,B-树主要应用在文件系统和数据库中,对于存储在硬盘上的大型数据库文件,可以极大程度减少访问硬盘次数,大幅度提高数据检索效率。

无论二叉搜索树还是 AVL 树,当数据量比较大时,都会由于树的深度过大而造成 I/O 读写过于频繁,进而导致查询效率低下,因此对于索引而言,多叉树结构成为不二选择。特别地,B-Tree 的各种操作能使 B 树保持较低的高度,从而保证高效的查找效率。

在这里插入图片描述

5.6 B+ 树

B+ 树是 B 树的变体,也是一种多路搜索树。B+ 树的非叶子结点相当于是叶子结点的索引,叶子结点相当于是存储(关键字)数据的数据层。B+Tree 是应文件系统所需而产生的一种 B_TREE 树的变形树。一棵 m 阶的 B+ 树和 m 阶的 B_TREE 的差异主要在于:B+ 树只有到达叶子结点才会命中,而 B 树可以在非叶子结点命中。

下图为一棵3阶的 B+ 树。通常在 B+ 树上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点。因此可以对 B+ 树进行两种查找运算:一种是从最小关键字起顺序查找,另一种是从根节点开始,进行随机查找。 在 B+ 树上进行随机查找、插入和删除的过程基本上与 B 树类似。只是在查找时,若非终端结点上的关键码等于给定值,并不终止,而是继续向下直到叶子结点。因此,对于 B+ 树,不管查找成功与否,每次查找都是走了一条从根到叶子结点的路径。
在这里插入图片描述
B/B+ 树通过对每个节点存储个数的扩展,使得对连续的数据能够进行较快的定位和访问,能够有效减少查找时间,提高存储的空间局部性从而减少 IO 操作,常用于文件系统和数据库系统。

6 散列表

散列表又称哈希表,是一个根据关键码值(key-value)而直接进行访问的数据结构,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

我们之前介绍过,数组寻址容易,插入和删除困难;链表寻址困难,插入和删除容易。哈希表使用了数组与链表两种数据结构,同时也综合了数组和链表两者的优点,寻址容易,插入删除也容易。

哈希表将 key 通过哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将 value 存储在以该数字为下标的数组空间里。当使用哈希表进行查询的时候,再次使用哈希函数将 key 转换为对应的数组下标,并定位到该空间获取 value。
在这里插入图片描述

7 堆

堆分为最大堆和最小堆两种。堆的性质如下:

  1. 堆树是一颗完全二叉树
  2. 堆树中某个节点的值总是不大于或不小于其孩子节点的值
  3. 堆树中每个节点的子树都是堆树

当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆,当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。

下图为最大堆:
在这里插入图片描述
下图为最小堆:
在这里插入图片描述

8 图

一个图就是一些顶点的集合,这些顶点通过一系列边连接。图有各种形状和大小,边可以有权重,也可以有方向。
在这里插入图片描述
图算法一般有深度优先搜索和广度优先搜索,如果一个问题可以通过顶点和边表示,那么可以运用相应的算法解决问题。

至于如何描述一个图,有两种主要的方法:

  1. 邻接列表:每一个顶点会存储一个从它这里开始的边的列表

在这里插入图片描述

  1. 邻接矩阵:行和列都表示顶点,由两个顶点所决定的矩阵对应元素表示这里两个顶点是否相连,如果相连的话,这个值表示的是相连边的权重
    在这里插入图片描述

参考:数据结构:链表(linked-list)
常用数据结构——树
哈希表(散列表)原理详解
数据结构:图(Graph)
数据结构:八大数据结构分类

发布了127 篇原创文章 · 获赞 237 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Geffin/article/details/104373834