Java 二叉树

2025年5月13日 | 阅读 33 分钟

二叉树 是一种树状非线性数据结构,主要用于排序和搜索,因为它以分层形式存储数据。在本节中,我们将学习 Java 中二叉树数据结构的实现。还将提供二叉树数据结构的简要说明。

二叉树

每个节点(父节点)最多有两个子节点(左节点和右节点)的树称为二叉树。最顶端的节点称为根节点。在二叉树中,一个节点包含数据以及指向左子节点和右子节点的指针(地址)。

二叉树的高度树的根与其最远(最深)叶节点之间边的数量。如果树为空,则高度为0。节点的高度表示为h

Binary Tree Java

上述二叉树的高度为 2。

我们可以使用以下公式计算叶节点和节点的数量。

  • 二叉树的最大叶节点数:2h
  • 二叉树的最大节点数:2h+1-1

其中,h 是二叉树的高度。

二叉树示例

Binary Tree Java

二叉树的类型

数据结构中有以下类型的二叉树

  1. 满/严格二叉树
  2. 完全二叉树
  3. 完美二叉树
  4. 平衡二叉树
  5. 有根二叉树
  6. 退化/病态二叉树
  7. 扩展二叉树
  8. 斜二叉树
    1. 左斜二叉树
    2. 右斜二叉树
  9. 线索二叉树
    1. 单线索二叉树
    2. 双线索二叉树
  10. AVL 树
  11. 红黑树
  12. B 树
  13. B+ 树
  14. 线段树

Java 中的二叉树实现

有许多方法可以实现二叉树。在本节中,我们将使用 LinkedList 数据结构来实现二叉树。此外,我们还将实现遍历顺序、搜索节点和在二叉树中插入节点。

使用 LinkedList 实现二叉树

算法

定义 Node 类,该类包含三个属性:data、left 和 right。这里,left 表示节点的左子节点,right 表示节点的右子节点。

  • 创建节点时,数据将传递到节点的 data 属性,left 和 right 都将设置为 null。
  • 定义另一个类,该类有一个 root 属性。
  • Root 表示树的根节点,并将其初始化为 null。
  1. insert() 将添加一个新节点到树中
    • 它检查 root 是否为 null,这意味着树是空的。它将新节点添加为 root。
    • 否则,它将 root 添加到队列中。
    • 变量 node 表示当前节点。
    • 首先,它检查节点是否有左子节点和右子节点。如果有,它将两个节点都添加到队列中。
    • 如果左子节点不存在,它将新节点添加为左子节点。
    • 如果左子节点存在,则它将新节点添加为右子节点。
  2. inorder() 将按中序顺序显示树的节点。
    • 它遍历整个树,然后打印左子节点,然后是根节点,然后是右子节点。

BinarySearchTree.java

输出

Binary tree after insertion
1 
Binary tree after insertion
2 1 3 
Binary tree after insertion
4 2 5 1 3 
Binary tree after insertion
4 2 5 1 6 3 7

二叉树操作

可以在二叉树上执行以下操作

  1. 插入:将新节点添加到树中。
  2. 删除:从树中删除一个节点。
  3. 遍历
    1. 中序遍历:访问左子树,然后是当前节点,然后是右子树。
    2. 前序遍历:访问当前节点,然后是左子树,然后是右子树。
    3. 后序遍历:访问左子树,然后是右子树,然后是当前节点。
    4. 层序遍历:从左到右逐层访问节点。
  4. 搜索:查找具有给定值的特定节点。

Java 程序在二叉树中插入节点

BinaryTreeInsert.java

输出

Building tree with root value 25
=================================
  Inserted 11 to left of Node 25
  Inserted 15 to right of Node 11
  Inserted 16 to right of Node 15
  Inserted 23 to right of Node 16
  Inserted 79 to right of Node 25

Java 程序在 Java 中删除节点

算法

  1. 从根节点开始,找到二叉树中最深、最右的节点以及要删除的节点。
  2. 将最深、最右节点的*数据*替换为要删除的节点。
  3. 然后删除最深、最右的节点。

考虑下图。

Binary Tree Java

DeleteNode.java

输出

Inorder traversal before deletion: 7 11 12 10 15 9 8 
Inorder traversal after deletion: 8 11 12 10 15 9 

Java 程序在二叉树中搜索节点

算法

  • 定义 Node 类,该类包含三个属性:data、left 和 right。这里,left 表示节点的左子节点,right 表示节点的右子节点。
  • 创建节点时,数据将传递到节点的 data 属性,left 和 right 都将设置为 null。
  • 定义另一个类,该类有两个属性 root 和 flag。
    1. Root 表示树的根节点,并将其初始化为 null。
    2. Flag 将用于检查给定节点是否在树中。最初,它将设置为 false。
  1. searchNode() 将在二叉树中搜索特定节点
    • 它检查 root 是否为 null,这意味着树是空的。
    • 如果树不为空,它将比较 temp 的数据与 value。如果它们相等,它将设置 flag 为 true 并返回。
    • 通过递归调用 searchNode() 来遍历左子树,并检查 value 是否存在于左子树中。
    • 通过递归调用 searchNode() 来遍历右子树,并检查 value 是否存在于右子树中。

SearchBinaryTree.java

输出

Element is present in the binary tree.

二叉树遍历

遍历顺序第一次访问第二次访问第三次访问
中序按中序遍历左子树访问根节点按中序遍历右子树
前序访问根节点按前序遍历左子树按前序遍历右子树
后序按后序遍历左子树按后序遍历右子树访问根节点

注意:除了上述三种遍历方式,还有一种遍历方式称为边界遍历。

Java 程序遍历中序、前序和后序遍历

BinaryTree.java

输出

Inorder traversal of binary tree
12 34 56 67 89 90 
Preorder traversal of binary tree
34 12 56 89 67 90 
Postorder traversal of binary tree
12 67 90 89 56 34

除了上述操作,我们还可以执行查找最大节点、最小节点和所有节点之和等操作。

Java 程序查找二叉树中的最大节点

LargestNode.java

输出

Largest element in the binary tree: 99

Java 程序查找二叉树中的最小节点

算法

  1. 定义 Node 类,该类包含三个属性:data、left 和 right。这里,left 表示节点的左子节点,right 表示节点的右子节点。
  2. 创建节点时,数据将传递到节点的 data 属性,left 和 right 都将设置为 null。
  3. 定义另一个类,该类有一个 root 属性。
    1. Root 表示树的根节点,并将其初始化为 null。
  1. smallestElement() 将查找二叉树中的最小节点
    1. 它检查 root 是否为 null,这意味着树是空的。
    2. 如果树不为空,则定义一个变量 min,该变量将存储 temp 的数据。
    3. 通过递归调用 smallestElement() 来查找左子树中的最小节点。将该值存储在 leftMin 中。将 min 和 leftMin 的值进行比较,并将两者中的较小者存储到 min 中。
    4. 通过递归调用 smallestElement() 来查找右子树中的最小节点。将该值存储在 rightMin 中。将 min 和 rightMin 的值进行比较,并将两者中的较小者存储到 min 中。
    5. 最后,min 将包含二叉树中的最小节点。

SmallestNode.java

输出

Smallest element in the binary tree: 1

Java 程序查找二叉树的最大宽度

算法

  1. 定义 Node 类,该类包含三个属性:data、left 和 right。这里,left 表示节点的左子节点,right 表示节点的右子节点。
  2. 创建节点时,数据将传递到节点的 data 属性,left 和 right 都将设置为 null。
  3. 定义另一个类,该类有一个 root 属性。
    1. Root 表示树的根节点,并将其初始化为 null。
  4. findMaximumWidth() 将查找给定二叉树的最大宽度
    1. 变量 maxWidth 将存储任何层上存在的最大节点数。
    2. 队列用于逐层遍历二叉树。
    3. 它检查 root 是否为 null,这意味着树是空的。
    4. 如果不是,则将根节点添加到队列。变量 nodesInLevel 用于跟踪每个层上的节点数。
    5. 如果 nodesInLevel > 0,则从队列的前面移除节点,并将其左子节点和右子节点添加到队列中。在第一次迭代中,将移除节点 1,并将其子节点 2 和 3 添加到队列中。在第二次迭代中,将移除节点 2,其子节点 4 和 5 将添加到队列中,依此类推。
    6. MaxWidth 将存储 max(maxWidth, nodesInLevel)。因此,在任何给定时间点,它将表示最大节点数。
    7. 这将一直持续到遍历完树的所有层。

BinaryTree.java

输出

Maximum width of the binary tree: 4

查找二叉树的最大元素

查找二叉树的最大元素涉及递归地比较树节点中的值以确定最高值。它对于各种算法至关重要,有助于高效的搜索操作和理解树的结构。

BinaryTreeMaxElement.java

输出

Maximum element in the binary tree is: 5

时间复杂度:O(n)

空间复杂度:O(n)

查找二叉树的高度

树的高度是一个基本指标,用于衡量从根到任何叶节点的*最长路径*的长度。它量化了树结构的深度,反映了其整体大小和复杂性。

BinaryTreeHeight.java

输出

Height of the binary tree is: 3

时间复杂度:O(n),其中 n 是树中的节点数。

辅助空间复杂度:O(h),其中 h 是树的高度。

查找二叉树的*最深*节点

查找树的最深节点涉及识别离根节点最远的节点。该节点位于树的最大深度处,并且没有子节点。

DeepestNodeInBinaryTree.java

输出

Deepest node in the binary tree is: 5

时间复杂度:O(n)

辅助空间复杂度:O(w)

树的*左视图*

二叉树的左视图是指从左侧看树时可见的节点。它包括从根到最左叶节点的*每一层的第一个节点*。

LeftViewOfBinaryTree.java

输出

Left view of the binary tree is: [1, 3, 4]

时间复杂度:O(n)(线性,其中 n 是二叉树中的节点数)

辅助空间复杂度:O(m),其中 m 是二叉树中任何层的*最大节点数*。

二叉树的*右视图*

二叉树的右视图是指从右侧看树时*可见的节点集合*。它包括*每一层的最顶端节点*,使我们能够看到*每一层*的*最右侧节点*。

RightViewOfBinaryTree.java

输出

Right view of the binary tree is: [1, 2, 5]

时间复杂度:O(n),其中 n 是二叉树中的节点数。

辅助空间复杂度:O(m),其中 m 是二叉树中*任何层*的*最大节点数*。

二叉树的*顶视图*

二叉树的顶视图是指从*上方*看树时*可见的节点*。它包括在*每个垂直层*上*最先遇到*的节点,从根开始。

TopViewOfBinaryTree.java

输出

Top view of the binary tree is: [4, 3, 1, 2]

时间复杂度:O(N)

辅助空间复杂度:O(N)

树的*底视图*

二叉树的底视图是指从*底部*向上看树时*可见的节点*。它显示*树的最低层节点*,同时考虑它们与根的*水平距离*。

BottomViewOfBinaryTree.java

输出

Bottom view of the binary tree is: [4, 3, 5, 2]

时间复杂度:O(N)

辅助空间复杂度:O(N)

树的*镜像*

树的镜像,也称为镜像树或反转树,是通过*交换原始树中每个节点的左右子节点*而形成的二叉树。这种变换会产生一个*结构沿垂直轴反转*的树,其中*每个节点的子节点都被镜像*。

MirrorImageOfBinaryTree.java

输出

Original tree:
4 3 5 1 2 
Mirror image of the tree:
2 1 5 3 4

时间复杂度:O(n)

辅助空间复杂度:O(h)

序列化二叉树

树的序列化涉及将二叉树*转换为可以存储或传输并稍后重建的格式*,同时*保留其结构*。此过程通常使用*遍历方法*(例如前序遍历),并*包含 null 节点的标记*。下面是一个实现此方法的 Java 程序。

SerializeDeserializeBinaryTree.java

输出

Serialized tree: 1 3 4 null null 5 null null 2 null null 
Inorder traversal of deserialized tree: 4 3 5 1 2

时间复杂度:O(n),其中 n 是二叉树中的节点数。

辅助空间:O(n),其中 n 是二叉树中的节点数。

二叉树的应用

  • 搜索算法:二叉搜索树(BST)支持高效的搜索操作。搜索特定元素可以在 O(log n) 的时间复杂度内完成,其中 n 是节点数,这比在未排序数据中进行*线性搜索*更快。
  • 排序算法:二叉树是高效排序算法(如二叉搜索树排序和堆排序)的基础。这些算法利用树的结构来组织数据并有效地执行排序操作。
  • 数据库系统:二叉树用于数据库系统中存储和管理数据。每个节点代表一个记录,允许高效的搜索操作和处理大型数据集。B 树及其变体通常用于数据库中的索引。
  • 文件系统:二叉树可用于实现文件系统,其中每个节点代表一个目录或文件。这种结构允许在文件系统中进行高效的导航、组织和搜索。
  • 压缩算法:霍夫曼编码是一种流行的压缩算法,它使用二叉树根据字符在输入数据中的频率为其分配可变长度的代码。这通过减小整体大小来实现有效的数据压缩。
  • 决策树:在机器学习中,决策树用于分类和回归分析。每个节点代表基于属性的决策,树的结构有助于通过从根到叶节点的遍历来做出预测。
  • 游戏 AI:二叉树在游戏 AI 中用于表示游戏中的可能走法。AI 算法搜索树来评估和选择最佳可能走法,从而提高像国际象棋或井字游戏这样的游戏的决策过程。

二叉树的优点

  1. 高效搜索:二叉树支持高效的搜索操作,因为每个节点最多有两个子节点,可以使用*二分搜索算法*。这使得搜索操作的时间复杂度为 O(log n)。
  2. 有序遍历:二叉树的结构支持各种遍历方法,例如中序、前序和后序遍历。这允许按特定顺序对节点执行操作,例如按*排序顺序*打印节点。
  3. 内存效率:与其他树结构相比,二叉树在内存效率方面相对较高,因为每个节点只需要两个指针。这使得它们适合在内存中存储大量数据,同时保持高效的搜索操作。
  4. 快速插入和删除:二叉树支持时间复杂度为 O(log n) 的插入和删除操作。这使得它们成为数据库系统等应用程序中使用的动态数据结构的理想选择。
  5. 易于实现:二叉树易于实现和理解,使其成为各种应用程序的流行选择。
  6. 有效的排序:二叉树可用于实现高效的排序算法,例如堆排序和二叉搜索树排序。

二叉树的缺点

  1. 结构受限:二叉树仅限于每个节点有两个子节点,这可能不适用于需要*超过两个子节点*的应用程序。在这种情况下,不同的树结构可能更合适。
  2. 不平衡树:不平衡二叉树,其中一个子树比另一个大得多,可能导致*低效的搜索操作*。如果树没有正确平衡或数据以非随机顺序插入,则可能发生这种情况。
  3. 空间效率低下:由于每个节点需要两个子指针,与*其他数据结构*相比,二叉树可能*空间效率低下*,对于大型树来说会导致显著的内存开销。
  4. 最坏情况下的性能缓慢:在最坏的情况下,二叉树可能变得*退化*,类似于*链表*,其中每个节点只有一个子节点。在这种情况下,搜索操作会*下降到 O(n) 的时间复杂度*。
  5. 复杂的平衡算法:维护平衡二叉树通常需要*复杂的算法*,例如 AVL 树和红黑树中使用的算法。这些平衡机制可能*难以实现*并且会增加*额外的开销*,使其不太适合某些应用程序。