跳到主要内容
Java 二叉树基础概念、遍历与基本操作 | 极客日志
Java java 算法
Java 二叉树基础概念、遍历与基本操作 介绍 Java 中二叉树的基础知识。涵盖二叉树定义、满二叉树与完全二叉树概念、五大性质及存储结构(链式)。详细讲解前序、中序、后序及层序遍历的递归与非递归实现原理与代码。包含获取节点总数、叶子节点数、第 K 层节点数、树高度、查找元素及判断完全二叉树等基本操作的 Java 代码实现。通过具体示例帮助理解二叉树的遍历逻辑与核心算法应用。
漫步 发布于 2026/2/6 更新于 2026/5/31 24 浏览1. 二叉树
1.1 二叉树的概念
一棵二叉树是结点的一个有限集合,该集合:
或者为空
或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
1.2 两种特殊的二叉树
满二叉树 : 一颗二叉树,如果每层的结点数都达到最大值,则这颗二叉树就是满二叉树。若满二叉树的层数为 K,那么其节点总数是 $2^K - 1$。
完全二叉树 : 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树引出来的。对于深度为 K 有 n 个节点的二叉树,当且仅当其每一个节点都与深度为 K 的满二叉树中编号从 0 至 n-1 的节点一一对应时称之为完全二叉树。满二叉树其实就是一种特殊的完全二叉树。
1.3 二叉树的性质
若规定根结点的层数为 1,则一棵非空二叉树的第 i 层上最多有 $2^{(i-1)}$ (i>0) 个结点。
若规定只有根结点的二叉树的深度为 1,则深度为 K 的二叉树的最大结点数是 $2^K - 1$ (k>=0)。
对任何一棵二叉树,如果其叶结点个数为 n0,度为 2 的非叶结点个数为 n2,则有 n0=n2+1。
具有 n 个结点的完全二叉树的深度 k 为 $\lceil \log_2(n+1) \rceil$(上取整)。
对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从 0 开始编号,则对于序号为 i 的结点有:若 i>0,将 i 看作孩子节点,双亲序号:$(i-1)/2$;将 i 看作父亲节点且 $2i+1<n$,左孩子序号:$2i+1$;$2i+2<n$,右孩子序号:$2i+2$。
1.4 二叉树的存储
二叉树的存储结构 分为:顺序存储 和类似于链表的链式存储 。顺序存储在下节介绍。二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式 ,具体如下:
class TreeNode {
char val;
TreeNode left;
TreeNode right;
}
{
val;
TreeNode left;
TreeNode right;
TreeNode parent;
}
class
TreeNode
char
孩子双亲表示法后序在平衡树位置介绍,本文采用孩子表示法来构建二叉树。
1.5 二叉树的基本操作
1.5.1 二叉树的简单创建 在学习二叉树的基本操作前,需先创建一棵二叉树,然后才能学习其相关的基本操作。为了简单些,此处手动创建一颗简单的二叉树。先学习二叉树的操作,后续再研究二叉树真正的创建方式。
public class BinaryTree {
static class TreeNode {
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode (char val) {
this .val = val;
}
}
public void createTree () {
TreeNode A = new TreeNode ('A' );
TreeNode B = new TreeNode ('B' );
TreeNode C = new TreeNode ('C' );
TreeNode D = new TreeNode ('D' );
TreeNode E = new TreeNode ('E' );
TreeNode F = new TreeNode ('F' );
TreeNode G = new TreeNode ('G' );
TreeNode H = new TreeNode ('H' );
A.left = B; A.right = C;
B.left = D; B.right = E;
C.left = F; C.right = G;
E.right = H;
}
}
空树
非空:根节点,根节点的左子树、根节点的右子树组成的。
1.5.2 二叉树的遍历
1. 前中后序遍历
NLR(前序遍历 Preorder Traversal) :访问根结点 --> 根的左子树 --> 根的右树。
LNR(中序遍历 Inorder Traversal) :根的左子树 --> 根节点 --> 根的右子树。
LRN(后序遍历 Postorder Traversal) :根的左子树 --> 根的右子树 --> 根节点。
下图是前序遍历的递归过程,中序遍历和后序遍历类似。
前序遍历结果:A B D E H C F G
中序遍历结果:D B H E A F C G
后序遍历结果:D H E B F G C A
注意 : 根的左子树,不只是根的左节点,而是根左边的整颗子树。右子树同理。如上右图。
代码实现前分析 (以前序遍历为例): 前序遍历的顺序为 根 左子树 右子树,凡是递归必有一终止条件,以上右图为例,从根节点往左递到 H,H 的左子树为 null,则返回。所以终止条件为 root 等于 null 则返回。按照顺序:先打印根节点,再递归左子树,后递归右子树。
public void preOrder (TreeNode root) {
if (root == null ) return ;
System.out.print(root.val + " " );
preOrder(root.left);
preOrder(root.right);
}
public void inOrder (TreeNode root) {
if (root == null ) return ;
inOrder(root.left);
System.out.print(root.val + " " );
inOrder(root.right);
}
public void postOrder (TreeNode root) {
if (root == null ) return ;
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val + " " );
}
2. 层序遍历 层序遍历 :除了前序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为 1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第 2 层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历,如下图所示,层序遍历的结果:A B C D E F G H
代码实现非递归 层序遍历:非递归层序遍历,我们可以借助队列,利用队列'先进先出'的特点,实现层序遍历思路:二叉树以根节点、左节点、右节点的顺序入队,出队顺序即为层序遍历,如下图:
具体步骤:先将根节点入队,进入循环 while(!queue.isEmpty()),弹出栈顶元素赋给 cur。打印根节点,将 cur 的左右节点入队。
import java.util.LinkedList;
import java.util.Queue;
public void levelOrder1 (TreeNode root) {
if (root == null ) { return ; }
Queue<TreeNode> queue = new LinkedList <>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
System.out.print(cur.val + " " );
if (cur.left != null ) { queue.offer(cur.left); }
if (cur.right != null ) { queue.offer(cur.right); }
}
}
1.5.3 二叉树的基本操作
1. 获取树中节点的个数 int size(Node root) 第一种方法:通过二叉树的遍历求,每递归遍历一次,size++。
public int nodeSize (TreeNode root) {
if (root == null ) return 0 ;
return 1 + nodeSize(root.left) + nodeSize(root.right);
}
第二种方法:通过子问题解决:总节点数 = 左子树 + 右子树 + 1。
public int nodeSize2 (TreeNode root) {
if (root == null ) return 0 ;
return nodeSize2(root.left) + nodeSize2(root.right) + 1 ;
}
2. 获取叶子节点的个数 int getLeafNodeCount(Node root) 第一种思路:定义 leafSize 存储叶子节点的个数,在前序遍历中,当某个节点的左右节点都为 null 时,则 leafSize++。
public int gerLeafSize (TreeNode root) {
if (root == null ) return 0 ;
if (root.left == null && root.right == null ) { return 1 ; }
return gerLeafSize(root.left) + gerLeafSize(root.right);
}
第二种思路:求二叉树叶子节点的个数 = 左子树叶子节点的个数 + 右子树叶子节点的个数。
public int getLeafSize2 (TreeNode root) {
if (root == null ) return 0 ;
if (root.left == null && root.right == null ) { return 1 ; }
return getLeafSize2(root.left) + getLeafSize2(root.right);
}
3. 获取第 K 层节点的个数 int getKLevelNodeCount(Node root, int k) public int getKLeveNodeCount (TreeNode root, int k) {
if (root == null ) return 0 ;
if (k == 1 ) { return 1 ; }
return getKLeveNodeCount(root.left, k-1 ) + getKLeveNodeCount(root.right, k-1 );
}
4. 获取二叉树的高度 int getHeight(Node root) 思路:二叉树的高度 = 左子树与右子树之中最大高度 + 1。
public int getHeight (TreeNode root) {
if (root == null ) return 0 ;
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return Math.max(leftHeight, rightHeight) + 1 ;
}
5. 检测值为 value 的元素是否存在 boolean find(Node root, char key) 还是一样的,通过遍历来确定二叉树是否存在值为 value 的节点,以前序遍历为例,判断根节点,递归左子树,再判断左子树,递归右子树,再判断右子树。如果全都没找到则 return false。
public boolean find (TreeNode root, char key) {
if (root == null ) return false ;
if (root.val == key) { return true ; }
boolean leftVal = find(root.left, key);
if (leftVal == true ) { return true ; }
boolean rightVal = find(root.right, key);
if (rightVal == true ) { return true ; }
return false ;
}
6. 判断一棵树是不是完全二叉树 boolean isCompleteTree(Node root) 思路:利用层序遍历,将节点入队,当 cur 遇到 null 之后,如果后面还有不为空的节点就说明该二叉树不是完全二叉树,否则是。
public boolean isCompleteTree (TreeNode root) {
if (root == null ) { return true ; }
Queue<TreeNode> queue = new LinkedList <>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if (cur != null ) {
queue.offer(cur.left);
queue.offer(cur.right);
} else {
break ;
}
}
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if (cur != null ) {
return false ;
}
}
return true ;
}
}
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online