我的个人主页
我的专栏:人工智能领域、java-数据结构、Javase、C语言,希望能帮助到大家!!!点赞👍收藏❤
前言
在计算机科学领域,数据结构是算法实现的基石,而二叉树则是其中一颗璀璨的明珠。它以独特的树形结构,在众多场景中发挥着关键作用。二叉树由节点组成,每个节点最多包含两个子节点,这种简洁而强大的设计,使得数据的存储、检索与处理变得高效且有序。无论是数据库索引、编译器的语法分析,还是人工智能中的决策树算法,都离不开二叉树的身影。在Java编程世界里,掌握二叉树的生成与操作是迈向高级编程的重要一步。通过使用Java语言来构建二叉树,不仅能够深入理解数据结构的底层原理,还能提升解决复杂问题的能力。接下来,我们将一步步深入探索如何在Java中实现二叉树,从节点的定义到树的构建,再到各种遍历与操作方法,揭开这一重要数据结构的神秘面纱。
一:什么是二叉树
二叉树是一种每个节点最多有两个子节点的树形数据结构,这两个子节点通常被称为左子节点和右子节点。这种简洁而强大的结构,在许多算法和数据处理场景中发挥着关键作用。
树是⼀种⾮线性的数据结构,它是由n(n>=0)个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的。它具有以下的特点:
- 有⼀个特殊的结点,称为根结点,根结点没有前驱结点
- 除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每⼀个集合Ti
(1 <= i <= m) ⼜是⼀棵与树类似的⼦树。每棵⼦树的根结点有且只有⼀个前驱,可以有0个或多个后
继 - 树是递归定义的。
注意:树形结构中,⼦树之间不能有交集,否则就不是树形结构
那么怎么区分什么是树,什么不是树呢?
请看下面图片:
一些二叉树重要的概念,以下面这棵树为例:
结点的度:⼀个结点含有⼦树的个数称为该结点的度; 如上图:A的度为6
树的度:⼀棵树中,所有结点度的最⼤值称为树的度; 如上图:树的度为6
叶⼦结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I…等节点为叶结点
双亲结点或⽗结点:若⼀个结点含有⼦结点,则这个结点称为其⼦结点的⽗结点; 如上图:A是B的⽗
结点孩⼦结点或⼦结点:⼀个结点含有的⼦树的根结点称为该结点的⼦结点; 如上图:B是A的孩⼦结点
根结点:⼀棵树中,没有双亲结点的结点;如上图:A
结点的层次:从根开始定义起,根为第1层,根的⼦结点为第2层,以此类推
树的⾼度或深度:树中结点的最⼤层次; 如上图:树的⾼度为4
树的以下概念只需了解,在看书时只要知道是什么意思即可:⾮终端结点或分⽀结点:度不为0的结点; 如上图:D、E、F、G…等节点为分⽀结点
兄弟结点:具有相同⽗结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
堂兄弟结点:双亲在同⼀层的结点互为堂兄弟;如上图:H、I互为兄弟结点
结点的祖先:从根到该结点所经分⽀上的所有结点;如上图:A是所有结点的祖先
⼦孙:以某结点为根的⼦树中任⼀结点都称为该结点的⼦孙。如上图:所有结点都是A的⼦孙
森林:由m(m>=0)棵互不相交的树组成的集合称为森林
二:Java中二叉树的实现
2.1定义二叉树节点类
首先,我们需要定义一个类来表示二叉树的节点。每个节点包含一个数据元素,以及指向左子节点和右子节点的引用。
class TreeNode {
int data;
TreeNode left;
TreeNode right;
TreeNode(int data) {
this.data = data;
this.left = null;
this.right = null;
}
}
2.2构建二叉树
接下来,我们可以编写代码来构建一棵简单的二叉树。
public class BinaryTreeMagic {
public static void main(String[] args) {
// 创建根节点
TreeNode root = new TreeNode(1);
// 创建左子节点
root.left = new TreeNode(2);
// 创建右子节点
root.right = new TreeNode(3);
// 为左子节点创建左子节点
root.left.left = new TreeNode(4);
// 为左子节点创建右子节点
root.left.right = new TreeNode(5);
// 至此,一棵简单的二叉树构建完成
// 1
// / \
// 2 3
// / \
// 4 5
}
}
2.3二叉树的遍历
遍历二叉树是对树中每个节点进行访问的过程。常见的遍历方式有三种:前序遍历、中序遍历和后序遍历。
学习⼆叉树结构,最简单的⽅式就是遍历。所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中
每个结点均做⼀次且仅做⼀次访问。访问结点所做的操作依赖于具体的应⽤问题(⽐如:打印节点内
容、节点内容加1)。 遍历是⼆叉树上最重要的操作之⼀,是⼆叉树上进⾏其它运算之基础。
在遍历⼆叉树时,如果没有进⾏某种约定,每个⼈都按照⾃⼰的⽅式遍历,得出的结果就⽐较混乱,
如果按照某种规则进⾏约定,则每个⼈对于同⼀棵树的遍历结果肯定是相同的。如果N代表根节点,L
代表根节点的左⼦树,R代表根节点的右⼦树,则根据遍历根节点的先后次序有以下遍历⽅式:
• NLR:前序遍历(Preorder Traversal 亦称先序遍历)⸺访问根结点—>根的左⼦树—>根的右⼦树。
• LNR:中序遍历(Inorder Traversal)⸺根的左⼦树—>根节点—>根的右⼦树。
• LRN:后序遍历(Postorder Traversal)⸺根的左⼦树—>根的右⼦树—>根节点
2.3.1前序遍历
前序遍历的顺序是先访问根节点,然后递归地访问左子树,最后递归地访问右子树。
void preOrderTraversal(TreeNode node) {
if (node!= null) {
System.out.print(node.data + " ");
preOrderTraversal(node.left);
preOrderTraversal(node.right);
}
}
2.3.2中序遍历
中序遍历的顺序是先递归地访问左子树,然后访问根节点,最后递归地访问右子树。
void inOrderTraversal(TreeNode node) {
if (node!= null) {
inOrderTraversal(node.left);
System.out.print(node.data + " ");
inOrderTraversal(node.right);
}
}
2.3.3后序遍历
后序遍历的顺序是先递归地访问左子树,然后递归地访问右子树,最后访问根节点。
void postOrderTraversal(TreeNode node) {
if (node!= null) {
postOrderTraversal(node.left);
postOrderTraversal(node.right);
System.out.print(node.data + " ");
}
}
前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 2 5 6 4 1
2.3.4 层序遍历
层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对⼆叉树进⾏层序遍历。设⼆叉树的根节
点所在层数为1,层序遍历就是从所在⼆叉树的根节点出发,⾸先访问第⼀层的树根节点,然后从左到
右访问第2层上的节点,接着是第三层的节点,以此类推,⾃上⽽下,⾃左⾄右逐层访问树的结点的过
程就是层序遍历。
以这棵树为例写下面题目:
1.某完全⼆叉树按层次输出(同⼀层从左到右)的序列为 ABCDEFGH 。该完全⼆叉树的前序序列为(A)
A: ABDHECFG
B: ABCDEFGH
C: HDBEAFCG
D: HDEBFGCA
2.⼆叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则⼆叉树根结点为(A)
A: E
B: F
C: G
D: H
3.设⼀课⼆叉树的中序遍历序列:badce,后序遍历序列:bdeca,则⼆叉树前序遍历序列为(D)
A: adbce
B: decab
C: debac
D: abcde
4.某⼆叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同⼀层从左到右)的
序列为(A)
A: FEDCBA
B: CBAFED
C: DEFCBA
D: ABCDEF
2.4完整代码示例
下面是一个包含二叉树构建和遍历功能的完整Java代码示例:
class TreeNode {
int data;
TreeNode left;
TreeNode right;
TreeNode(int data) {
this.data = data;
this.left = null;
this.right = null;
}
}
public class BinaryTreeMagic {
void preOrderTraversal(TreeNode node) {
if (node!= null) {
System.out.print(node.data + " ");
preOrderTraversal(node.left);
preOrderTraversal(node.right);
}
}
void inOrderTraversal(TreeNode node) {
if (node!= null) {
inOrderTraversal(node.left);
System.out.print(node.data + " ");
inOrderTraversal(node.right);
}
}
void postOrderTraversal(TreeNode node) {
if (node!= null) {
postOrderTraversal(node.left);
postOrderTraversal(node.right);
System.out.print(node.data + " ");
}
}
public static void main(String[] args) {
BinaryTreeMagic tree = new BinaryTreeMagic();
// 创建根节点
TreeNode root = new TreeNode(1);
// 创建左子节点
root.left = new TreeNode(2);
// 创建右子节点
root.right = new TreeNode(3);
// 为左子节点创建左子节点
root.left.left = new TreeNode(4);
// 为左子节点创建右子节点
root.left.right = new TreeNode(5);
System.out.println("前序遍历:");
tree.preOrderTraversal(root);
System.out.println();
System.out.println("中序遍历:");
tree.inOrderTraversal(root);
System.out.println();
System.out.println("后序遍历:");
tree.postOrderTraversal(root);
System.out.println();
}
}
三:二叉树的应用场景
- 搜索算法:二叉搜索树(BST)是一种特殊的二叉树,它满足左子树所有节点的值小于根节点的值,右子树所有节点的值大于根节点的值。这种特性使得在BST中进行搜索操作的时间复杂度为O(log n),大大提高了搜索效率。
- 表达式求值:通过构建表达式二叉树,可以方便地对数学表达式进行求值。例如,对于表达式“(3 + 4) * 2”,可以构建相应的二叉树来进行计算。
- 文件系统目录结构:可以用二叉树来模拟文件系统的目录结构,根节点表示根目录,子节点表示子目录或文件,方便进行文件管理和查找。
- ⽂件系统管理(⽬录和⽂件)
四:二叉树的重点
4.1概念
⼀棵⼆叉树是结点的⼀个有限集合,该集合:
- 或者为空
- 或者是由⼀个根节点加上两棵别称为左⼦树和右⼦树的⼆叉树组成
从上图可以看出:
1. ⼆叉树不存在度⼤于2的结点
2. ⼆叉树的⼦树有左右之分,次序不能颠倒,因此⼆叉树是有序树 注意:对于任意的⼆叉树都是由以下⼏种情况复合⽽成的:
4.2大自然的一些奇观
4.3两种特殊的⼆叉树
- 满⼆叉树: ⼀棵⼆叉树,如果每层的结点数都达到最⼤值,则这棵⼆叉树就是满⼆叉树。也就是
说,如果⼀棵⼆叉树的层数为K,且结点总数是 ,则它就是满⼆叉树。 - 完全⼆叉树: 完全⼆叉树是效率很⾼的数据结构,完全⼆叉树是由满⼆叉树⽽引出来的。对于深度
为K的,有n个结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从0⾄n-1的结
点⼀ 对应时称之为完全⼆叉树。 要注意的是满⼆叉树是⼀种特殊的完全⼆叉树。
4.4二叉树的一些性质
1. 若规定根结点的层数为1,则⼀棵⾮空⼆叉树的第i层上最多有(i>0)个结点
2. 若规定只有根结点的⼆叉树的深度为1,则深度为K的⼆叉树的最⼤结点数是(k>=0)
3. 对任何⼀棵⼆叉树, 如果其叶结点个数为 n0, 度为2的⾮叶结点个数为 n2,则有n0=n2+1
4. 具有n个结点的完全⼆叉树的深度k为上取整
5. 对于具有n个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的顺序对所有节点从0开始编号,则对
于序号为i的结点有:
◦ 若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,⽆双亲结点
◦ 若2i+1<n,左孩⼦序号:2i+1,否则⽆左孩⼦
◦ 若2i+2<n,右孩⼦序号:2i+2,否则⽆右孩⼦
下面一些题目为例:
- 某⼆叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该⼆叉树中的叶⼦结点数为( )
A 不存在这样的⼆叉树
B 200
C 198
D 199
答案:B
2.在具有 2n 个结点的完全⼆叉树中,叶⼦结点个数为( )
A n
B n+1
C n-1
D n/2
答案:A
3.⼀个具有767个节点的完全⼆叉树,其叶⼦节点个数为()
A 383
B 384
C 385
D 386
答案:B
4.⼀棵完全⼆叉树的节点数为531个,那么这棵树的⾼度为( )
A 11
B 10
C 8
D 12
答案:B
五:总结
Java二叉树就像代码世界里的神奇魔法棒,通过巧妙地构建和操作树形结构,为我们解决各种复杂的编程问题提供了有力的工具。无论是高效的搜索算法,还是复杂的表达式求值,二叉树都展现出了其独特的魅力和强大的功能。希望通过本文的介绍和代码示例,你能对Java二叉树有更深入的理解和认识,在编程的道路上运用这神奇的树形魔法创造出更多精彩的代码。
可以已经准备好进一步探索二叉树的更多高级特性和应用,比如平衡二叉树、红黑树等,它们将为你的编程技能库增添更多强大的武器。