数据结构-树(二叉树、红黑、B、B+等)

​树的基本定义​

树的定义

树(Tree)​​ 是一种 ​​非线性数据结构​​,由 ​​节点(Node)​​ 和 ​​边(Edge)​​ 组成,满足以下条件:

​​有且仅有一个根节点(Root)​​:根节点没有父节点,是树的起点。

​​除根节点外,每个节点有且仅有一个父节点​​。

​​节点之间通过边连接​​,形成层次关系。

​​没有环路(Cycle)​​:从根到任意节点的路径唯一。

树的逻辑结构​

​​树形结构​​:

从根节点向下展开,形成多级分支,类似自然界中的树(倒置结构)。

节点间的连接具有 ​​方向性​​(父→子)和 ​​层次性​​。

​​递归定义​​:

一棵树由根节点和若干子树构成,每棵子树本身也是树。

核心术语​

树的分类​

根据节点子节点的限制,树可分为多种类型:

​​1.普通树​​:

每个节点可以有任意多个子节点。

2.​​二叉树(Binary Tree)​​:

每个节点最多有两个子节点(左子节点和右子节点)。

3.​​多叉树(N-ary Tree)​​:

每个节点最多有 N 个子节点(如 B 树、Trie 树)。

树的性质​

1.节点数​​:

一棵树有 n 个节点,则它有 n−1 条边(每条边对应一个父子关系)。

2.​​高度与节点数的关系​​:

对于高度为 h 的树,最少有 h+1 个节点(线性链状结构),最多有 2 h 2^h 2h−1 个节点(满二叉树)。

树的应用场景​

1.文件系统​​:

目录结构是树形层级(根目录→子目录→文件)。

2.​​数据库索引​​:

B+ 树用于加速数据查询。

3.​​组织架构​​:

公司部门层级关系(CEO→部门→团队→员工)。

​​4.算法与数据结构​​:

二叉搜索树、堆、哈夫曼树等用于高效操作。

树与其他数据结构的区别​

常见树结构

二叉树

​​定义​​:每个节点最多有两个子节点(左子节点和右子节点)。

​​特点​​:

结构简单,基础树形结构。

可用于构建其他复杂树结构(如二叉搜索树、堆)。

根节点(Root)​​:树的顶层节点,没有父节点。

​​叶子节点(Leaf)​​:没有子节点的节点。

​​内部节点(Internal Node)​​:至少有一个子节点的非叶子节点。

普通二叉树​

定义​​:每个节点最多有两个子节点,无其他约束。

​​示例​​:

bash 复制代码
      A
     / \
    B   C
   / \   \
  D   E   F

满二叉树(Full Binary Tree)​

​定义​​:所有非叶子节点都有两个子节点,且所有叶子节点在同一层。

特点​​:

深度为 h 的满二叉树有 2 h 2^h 2h−1 个节点。

​​示例​​:

bash 复制代码
      A
     / \
    B   C
   / \ / \
  D  E F  G

定义​​:

左子树所有节点的值 < 根节点的值。

右子树所有节点的值 > 根节点的值。

​​特点​​:

中序遍历结果为有序序列。

如果平衡,查询效率高(O(logn))。

​​缺点​​:

可能退化为链表(插入顺序不佳时),查询效率降至 O(n)。

​​应用场景​​:

需要快速查找的有序数据集。

平衡二叉树(AVL 树、红黑树)

定义​​:

二叉搜索树的变种,通过旋转操作保持平衡。

任意节点的左右子树高度差不超过 1。

​​特点​​:

严格平衡,查询效率稳定(O(logn))。

​​缺点​​:

插入和删除时需要频繁旋转,维护成本高。

​​应用场景​​:

适合读多写少的场景(如字典)。

完全二叉树

​​定义​​:

除最后一层外,其他层节点数达到最大值。

最后一层的节点必须从左到右连续填充,中间不能有空缺。

​​示例​​:

bash 复制代码
      A
     / \
    B   C
   / \  /
  D  E F

二叉树的遍历​

bash 复制代码
        A
       / \
      B   C
     / \   \
    D   E   F
深度优先遍历(DFS)​

前序遍历(Preorder)​​:根 → 左 → 右

bash 复制代码
      ①A
     /   \
  ②B    ⑤C
  / \      \
③D ④E    ⑥F
结果​​:A → B → D → E → C → F

​​中序遍历(Inorder)​​:左 → 根 → 右

bash 复制代码
      ④A
     /   \
  ②B    ⑥C
  / \      \
①D ③E    ⑤F
​​结果​​:D → B → E → A → C → F

​​后序遍历(Postorder)​​:左 → 右 → 根

bash 复制代码
      ⑥A
     /   \
  ③B    ⑤C
  / \      \
①D ②E    ④F
​结果​​:D → E → B → F → C → A
广度优先遍历(BFS / 层次遍历)​

按层遍历​​:从根节点开始,逐层从左到右访问。

bash 复制代码
      ①A
     /   \
  ②B    ③C
  / \      \
④D ⑤E    ⑥F
结果​​:A → B → C → D → E → F

二叉树的应用​

1.​​二叉搜索树(BST)​​:快速查找、插入、删除有序数据。

​​2.堆(完全二叉树)​​:优先队列、堆排序。

​​3.哈夫曼树(Huffman Tree)​​:数据压缩(如 ZIP 文件)。

​​4.表达式树​​:解析数学表达式(如 (a + b) * c)。

​​5.决策树​​:机器学习分类模型。

红黑树(Red-Black Tree)

红黑树概述​​

红黑树(Red-Black Tree)​​ 是一种 ​​自平衡二叉搜索树​​,通过颜色标记和旋转操作维持树的平衡,确保最坏情况下的查找、插入和删除操作时间复杂度为 O(logn)。其核心设计目标是在插入和删除时减少平衡调整的次数,适合高频修改的数据场景。

红黑树的五大性质​

​​节点颜色​​:每个节点是红色或黑色。

​​根节点​​:根节点必须是黑色。

​​叶子节点​​:所有叶子节点(NIL节点,空节点)为黑色。

​​红色节点限制​​:红色节点的子节点必须为黑色(即不存在连续红色节点)。

​​黑高一致​​:从任一节点到其所有后代叶子节点的路径上,黑色节点的数量相同(称为 ​​黑高​​)。

红黑树的操作

插入操作​:

​​按BST规则插入新节点​​,并将其标记为红色。

​​修复红黑树性质​​:

​​Case 1​​:父节点为黑色 → 无需调整。

​​Case 2​​:父节点为红色,叔叔节点为红色 → 父节点和叔叔变黑,祖父变红,再递归处理祖父节点。

​​Case 3​​:父节点为红色,叔叔节点为黑色 → 通过旋转和重新着色调整(分左旋和右旋情况)。

示例​​(插入节点后修复):

bash 复制代码
插入前:
      B (祖父)
     / \
  R (父) B (叔叔)
   /
R (新节点)

修复后(Case 2):
      R (祖父)
     / \
    B   B
   /
  R
删除操作​:

步骤​​:

​​按BST规则删除节点​​。

​​若删除节点为红色​​:直接删除,无需调整。

​​若删除节点为黑色​​:需通过旋转和颜色调整恢复黑高。

​​Case 1​​:兄弟节点为红色 → 旋转父节点,使兄弟变黑。

​​Case 2​​:兄弟节点为黑色,且兄弟的子节点均为黑 → 兄弟变红,递归处理父节点。

​​Case 3​​:兄弟节点为黑色,且兄弟的远侄子为红 → 旋转父节点并重新着色。

​​示例​​(删除黑色节点后的调整):

bash 复制代码
删除前:
      B (父)
     / \
  B (被删节点) R (兄弟)
           \
            B (远侄子)

修复后(Case 3):
      B (父)
     / \
    B   B
         \
          R
旋转操作​​:

左旋(Left Rotate)​​:将右子节点提升为父节点,原父节点变为左子节点。

bash 复制代码
   P               R
    \             /
     R    →     P
    /             \
   L               L

右旋(Right Rotate)​​:将左子节点提升为父节点,原父节点变为右子节点。

bash 复制代码
     P           L
    /             \
   L       →       P
    \             /
     R           R

红黑树的应用​

​Java集合框架​​:TreeMap、TreeSet 基于红黑树实现。

​​C++ STL​​:map、multimap、set、multiset。

​​Linux内核​​:进程调度、内存管理等模块使用红黑树管理数据。

​​数据库系统​​:部分数据库的索引结构采用红黑树。

红黑树的性能分析​

时间复杂度​​:插入、删除、查找均为 O(logn)。

​​空间复杂度​​:O(n),每个节点存储颜色标记(1 bit)。

​​实际优化​​:通过减少旋转次数,红黑树在动态数据场景中表现优异。

B 树(B-Tree)​

B树(B-Tree)是一种​​多路平衡搜索树​​,广泛用于处理​​大规模数据存储​​(如数据库、文件系统)的场景。其核心设计目标是​​减少磁盘I/O次数​​,通过保持树的高度平衡,优化数据的存储与访问效率。

B树通过多路平衡的设计,在​​高并发、大规模数据存储​​场景中表现优异。理解其核心操作(分裂、合并)及平衡逻辑,是掌握数据库索引和文件系统优化的关键基础。对于需要频繁范围查询的场景,B+树通常是更好的选择。

B树定义

阶数(Order)​​:B树的阶数由正整数 M 表示,决定了每个节点的​​最大子节点数​​和​​关键字数量​​。

节点特性:

每个节点最多有 M 个子节点。

每个非根节点至少有 ⌈M/2⌉ 个子节点(根节点至少有2个子树,除非它是叶子节点)。

每个非根非叶子节点至少有 ⌈M/2⌉−1 个关键字,最多有 M−1 个关键字。

​​所有叶子节点位于同一层​​,保证树的高度平衡。

B树的核心性质​

​​1.平衡性​​:所有叶子节点在同一层,保证查找效率稳定。

​​2.多路分支​​:每个节点有多个子节点,减少树高。

3.​​自调整机制​​:插入操作可能触发​​节点分裂​​,删除操作可能触发​​节点合并​​或从兄弟节点借位,始终保持平衡。

B树的操作​

查找​​

类似二叉搜索树,但在每个节点内部通过二分法确定分支路径:

从根节点开始,按关键字有序遍历。

若找到匹配值,返回结果;否则进入对应区间子节点。

直到叶子节点结束。

插入​​

​​定位插入位置​​:找到对应的叶子节点。

​​插入关键字​​:

若叶子节点未满(关键字数 < M−1),直接插入。

若节点已满,进行​​分裂​​:

将中间关键字(中位数)提升到父节点。

分裂原节点为两个新节点,各包含一半关键字。

若父节点满,继续向上递归分裂。

​​分裂可能一直向上传播到根节点​​,导致树高增加。

​​示例​​:3阶B树插入关键字(每个节点最多2个关键字):

插入到满节点 [10,20] → 分裂为 [10] 和 [20],中间提升 10 到父节点。

删除

分两种情况处理:

​​删除叶子节点关键字​​:

直接删除,若关键字数不足下限,需从兄弟节点借位或合并节点。

​​删除内部节点关键字​​:

用前驱(左子树最大值)或后继(右子树最小值)替代被删节点,转为叶子节点删除。

​​合并操作​​:若节点关键字过少且兄弟节点无法借位,与父节点的分隔键合并。

B树的应用场景​​

​​数据库索引​​:B树支持高效的点查和范围查询,降低磁盘I/O。

​​文件系统​​:存储文件的元数据(如NTFS、Ext4)。

​​键值存储​​:如LevelDB的SSTable索引。

B树时间复杂度​​

查找、插入、删除的时间复杂度均为 ​​O( log ⁡ m n \log_{m}n logmn)​​,其中 M 为阶数,N 为数据总量。

由于 M 通常较大,树高远低于二叉树(如 log ⁡ 100 n \log_{100}n log100n vs log ⁡ 2 n \log_{2}n log2n),显著减少磁盘访问次数。

B+ 树(B+ Tree)​

B+树(B-Plus Tree)是B树的变种,专为​​大规模数据存储​​和​​高效范围查询​​设计。它在数据库索引、文件系统等领域应用广泛,核心特点是​​数据仅存储在叶子节点​​,并通过叶子节点的​​链表连接​​优化顺序访问。

B+树的定义

​​阶数(Order)​​:由正整数 M 表示,决定每个节点的​​最大子节点数​​和​​关键字数量​​。

​​节点特性​​:

​​内部节点​​(非叶子节点):

存储关键字作为​​索引​​(分隔键),不存储实际数据。

最多有 M 个子节点,至少有 ⌈M/2⌉ 个子节点(根节点例外)。

​​叶子节点​​:

存储​​所有关键字和对应的数据指针​​(或数据本身)。

叶子节点通过​​双向链表​​连接,支持高效顺序遍历。

每个叶子节点至少有 ⌈M/2⌉ 个关键字,最多有 M−1 个关键字。

​​所有叶子节点位于同一层​​,保证树高平衡。

B+树的核心性质​

数据分离​​:内部节点仅存索引,数据全在叶子节点。

​​顺序访问优化​​:叶子节点通过链表连接,支持高效范围查询。

​​更高的分支因子​​:因内部节点不存数据,可容纳更多关键字,进一步降低树高。

B+树的操作​​

查找​​

​​点查​​:

从根节点开始,按关键字比较向下遍历。

最终到达叶子节点,若存在匹配关键字,返回数据;否则返回空。

​​范围查询​​:

找到起始关键字所在的叶子节点。

沿链表顺序遍历,直到终止关键字。

​​插入​​

​​定位插入位置​​:找到对应的叶子节点。

​​插入关键字​​:

若叶子节点未满(关键字数 < M−1),直接插入。

若叶子节点已满,进行​​分裂​​:

将中间关键字(中位数)​​复制到父节点​​作为索引。

分裂原节点为两个新节点,各包含一半关键字。

若父节点已满,递归向上分裂。

​​分裂规则​​:与B树不同,中间关键字在父节点中被复制而非移动。

​​示例​​:3阶B+树插入关键字(每个节点最多2个关键字):

插入到满叶子节点 [10,20] → 分裂为 [10] 和 [20],中间关键字 20 被复制到父节点作为索引。

删除​​

​​定位删除位置​​:找到目标关键字所在的叶子节点。

​​删除关键字​​:

若叶子节点关键字数仍满足下限(≥ ⌈M/2⌉),直接删除。

若关键字数不足,尝试从兄弟节点借位:

若兄弟节点有富余关键字,调整父节点索引。

若无法借位,与兄弟节点合并,并删除父节点中的冗余索引。

​​合并传播​​:合并可能导致父节点关键字不足,递归向上调整。

B+树的应用场景​​

​​数据库索引​​(如MySQL InnoDB):

范围查询(BETWEEN、ORDER BY)高效。

全表扫描只需遍历叶子链表。

​​文件系统​​(如ReiserFS、XFS):

快速定位文件块。

​​键值存储引擎​​(如RocksDB、MongoDB默认存储引擎WiredTiger)。

B+树的时间复杂度​

查找、插入、删除的时间复杂度均为 ​​O( log ⁡ m n \log_{m}n logmn​​)

由于内部节点不存储数据,分支因子 M 更大,树高更低,I/O次数更少。

B+树 vs B树​

Trie 树(字典树)​

Trie 树(​​字典树​​或​​前缀树​​)是一种专门用于​​字符串检索​​和​​前缀匹配​​的树形数据结构。其核心思想是通过字符串的公共前缀来共享存储空间,优化查询效率,广泛应用于​​搜索引擎​​、​​输入法提示​​、​​路由表​​等场景。

Trie 树的定义​

节点结构​​:每个节点代表一个字符,从根节点到某一节点的路径构成一个字符串。

​​根节点​​:不存储字符,表示空字符串。

​​子节点​​:每个节点的子节点对应不同字符(如26个小写字母、ASCII字符等)。

​​结束标记​​:节点可附加标记(如 is_end)表示是否为一个完整字符串的结尾。

​​示例​​:插入字符串 ["apple", "app", "banana"] 的 Trie 树结构:

root

/

a b

/

p a

/

p (is_end) n

/

p a

/ \

l (is_end) n

\

e (is_end) a (is_end)

Trie 树的核心性质​

​​前缀共享​​:具有相同前缀的字符串共享路径,减少冗余存储。

​​快速检索​​:查找时间复杂度与字符串长度相关,​​与数据量无关​​。

​​自动排序​​:按字典序插入时,Trie 树天然支持有序遍历。

Trie 树的操作​

插入字符串​​

从根节点开始,逐字符遍历字符串。

若字符对应的子节点不存在,则创建新节点。

到达字符串末尾时,标记当前节点为结束节点(is_end = true)。

​​示例​​:插入 "apple":

root → a → p → p → l → e(标记 e 节点为结束)。

查找字符串​​

从根节点开始,逐字符向下匹配。

若路径中途子节点不存在,返回 false。

若到达字符串末尾且当前节点标记为结束,返回 true。

​​示例​​:查找 "app":

root → a → p → p(该节点标记为结束,存在)。

查找前缀​​

与查找字符串类似,但无需检查结束标记,只需确认前缀存在。

​​删除字符串​​

先查找字符串是否存在。

若存在,从叶子节点向上回溯删除节点,直到遇到有其他子节点或标记为结束的节点。

​​注意​​:需处理共享前缀的情况,避免误删其他字符串。

Trie 树的应用场景​

搜索引擎自动补全​​:根据输入前缀快速推荐候选词。

​​拼写检查​​:快速判断单词是否存在。

​​路由表匹配​​:IP地址前缀匹配(需结合压缩优化)。

​​词频统计​​:在节点中存储计数信息。

​​敏感词过滤​​:结合AC自动机(Aho-Corasick)实现多模式匹配。

Trie 树时间复杂度​

​​插入​​:O(L),其中 L 为字符串长度。

​​查找​​:O(L)。

​​前缀匹配​​:O(L)。

堆(Heap)​

堆(Heap)是一种基于​​完全二叉树​​的抽象数据结构,具有独特的​​堆序性​​,常用于高效获取数据集中的​​最大值​​或​​最小值​​。堆分为​​最大堆​​和​​最小堆​​,分别用于快速访问极值,是优先队列(Priority Queue)的底层实现核心。

堆的定义​

​​完全二叉树​​:除最后一层外,其他层节点全满,最后一层节点从左到右填充。

​​堆序性​​:

​​最大堆​​:父节点的值 ≥ 子节点的值(根节点为最大值)。

​​最小堆​​:父节点的值 ≤ 子节点的值(根节点为最小值)。

​​存储结构​​:通常用​​数组​​实现,利用完全二叉树的性质简化索引计算。

堆的核心性质​

1.​​结构特性​​:

完全二叉树,高度为 O(logn)。

数组表示中,节点索引关系:

父节点索引:parent(i)=⌊(i−1)/2⌋

左子节点索引:left(i)=2i+1

右子节点索引:right(i)=2i+2

2.​​堆序性​​:仅保证父节点与子节点的大小关系,不保证兄弟节点有序。

堆的操作​

插入元素(Insert)​​

​​添加到末尾​​:将新元素插入数组末尾。

​​上浮(Percolate Up)​​:从新元素开始,与父节点比较并交换,直到满足堆序性。

​​示例​​:在最大堆 [70, 60, 50, 20, 10] 中插入 65:

插入后数组 → [70, 60, 50, 20, 10, 65]

上浮过程:65 > 20 → 交换 → 65 > 60 → 交换 → 最终堆 → [70, 65, 50, 60, 10, 20]

删除堆顶元素(Extract-Max/Min)​​

​​交换堆顶与末尾​​:将堆顶元素与最后一个元素交换,删除末尾。

​​下沉(Percolate Down)​​:从堆顶开始,与较大(或较小)的子节点交换,直到恢复堆序性。

​​示例​​:从最大堆 [70, 65, 50, 60, 10, 20] 删除堆顶:

删除后数组 → [20, 65, 50, 60, 10]

下沉过程:20 < 65 和 50 → 与 65 交换 → 20 < 60 → 交换 → 最终堆 → [65, 60, 50, 20, 10]

​​构建堆(Heapify)​​

​​Floyd算法​​:从最后一个非叶子节点开始,逐个节点执行下沉操作。

​​时间复杂度​​:O(n),数学证明基于树高和节点数量关系。

堆的应用场景​

堆排序​​:通过构建堆,依次取出堆顶元素实现排序(时间复杂度 O(nlogn))。

​​优先队列​​:任务调度、事件驱动模拟(如Dijkstra算法)。

​​Top K 问题​​:快速找到数据流中最大的K个元素。

​​合并有序序列​​:K路归并时,用堆高效选择最小元素。

堆时间复杂度​

各种树对比

相关推荐
zhangxueyi3 小时前
Java实现基数排序算法
java·数据结构·算法
keep intensify5 小时前
【数据结构】- 栈
c语言·数据结构·算法·
小技与小术6 小时前
代码随想录算法训练营day12(二叉树)
数据结构·python·算法
Demons_kirit6 小时前
LeetCode LCP40 心算挑战题解
java·数据结构·算法·leetcode·职场和发展
菜还不练就废了6 小时前
25.4.30数据结构|并查集 路径压缩
数据结构
鑫—萍9 小时前
C++——入门基础(2)
java·开发语言·jvm·数据结构·c++·算法
一只鱼^_10 小时前
力扣第447场周赛
数据结构·算法·leetcode·职场和发展·贪心算法·动态规划·迭代加深
zfj32113 小时前
Lucene并不是只有倒排索引一种数据结构,支持多种数据结构
数据结构·lucene
legend_jz13 小时前
算法--模拟题目
数据结构·c++·算法