1. 二叉树基础概念与Java实现
二叉树是每个节点最多有两个子节点的树形数据结构,在Java中通常通过节点类(Node class)来实现。我们先来看最基础的二叉树节点定义:
java复制class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
这个简单的类定义包含了二叉树节点的三个核心要素:存储的值(val)、左子节点指针(left)和右子节点指针(right)。在实际工程中,我们通常会把这个类定义为静态内部类,或者单独放在一个文件中。
注意:在面试中经常会被要求手写这个基础结构,务必记住val和左右指针的命名规范。有些面试官会特别关注你是否把left/right写成lChild/rChild之类的变体。
1.1 二叉树的五种基本形态
二叉树有以下五种基本形态,理解这些形态对后续算法实现至关重要:
- 空树:没有任何节点的二叉树
- 只有根节点:没有子节点的单节点树
- 只有左子树:根节点+左子树,右子树为空
- 只有右子树:根节点+右子树,左子树为空
- 完全二叉树:左右子树都存在
在Java中,我们通过null引用来表示空子树。比如要创建一个只有根节点值为5的二叉树:
java复制TreeNode root = new TreeNode(5); // left和right自动初始化为null
1.2 二叉树的性质与计算
二叉树有几个重要性质经常在面试中被考察:
- 第i层最多有2^(i-1)个节点
- 深度为k的二叉树最多有2^k - 1个节点
- 对于任何非空二叉树,如果叶子节点数为n0,度为2的节点数为n2,则n0 = n2 + 1
这些性质在解决二叉树相关算法题时非常有用。例如要计算完全二叉树的节点数:
java复制public int countNodes(TreeNode root) {
if(root == null) return 0;
return 1 + countNodes(root.left) + countNodes(root.right);
}
这个递归方法的时间复杂度是O(n),因为每个节点都会被访问一次。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 二叉树的遍历方式
二叉树的遍历是面试最高频考点,必须熟练掌握四种遍历方式及其实现。
2.1 前序遍历(Preorder Traversal)
遍历顺序:根节点 → 左子树 → 右子树
递归实现:
java复制public void preorder(TreeNode root) {
if(root == null) return;
System.out.print(root.val + " "); // 先访问根节点
preorder(root.left);
preorder(root.right);
}
迭代实现(使用栈):
java复制public void preorderIterative(TreeNode root) {
if(root == null) return;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()) {
TreeNode node = stack.pop();
System.out.print(node.val + " ");
// 注意右子节点先入栈
if(node.right != null) stack.push(node.right);
if(node.left != null) stack.push(node.left);
}
}
2.2 中序遍历(Inorder Traversal)
遍历顺序:左子树 → 根节点 → 右子树
递归实现:
java复制public void inorder(TreeNode root) {
if(root == null) return;
inorder(root.left);
System.out.print(root.val + " "); // 中间访问根节点
inorder(root.right);
}
迭代实现:
java复制public void inorderIterative(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while(curr != null || !stack.isEmpty()) {
while(curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
System.out.print(curr.val + " ");
curr = curr.right;
}
}
面试技巧:中序遍历的迭代版本是面试中最常考的代码之一,务必能够白板写出。关键点是理解内层while循环把所有左节点压栈的过程。
2.3 后序遍历(Postorder Traversal)
遍历顺序:左子树 → 右子树 → 根节点
递归实现:
java复制public void postorder(TreeNode root) {
if(root == null) return;
postorder(root.left);
postorder(root.right);
System.out.print(root.val + " "); // 最后访问根节点
}
迭代实现(使用两个栈):
java复制public void postorderIterative(TreeNode root) {
if(root == null) return;
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
stack1.push(root);
while(!stack1.isEmpty()) {
TreeNode node = stack1.pop();
stack2.push(node);
if(node.left != null) stack1.push(node.left);
if(node.right != null) stack1.push(node.right);
}
while(!stack2.isEmpty()) {
System.out.print(stack2.pop().val + " ");
}
}
2.4 层序遍历(Level Order Traversal)
层序遍历使用队列实现,按层次从上到下访问节点:
java复制public void levelOrder(TreeNode root) {
if(root == null) return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int levelSize = queue.size();
for(int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
System.out.print(node.val + " ");
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
}
System.out.println(); // 换行表示新的一层
}
}
层序遍历在解决二叉树宽度、深度等问题时非常有用,也是许多其他算法的基础。
3. 二叉树的常见操作
3.1 插入节点
二叉树插入需要遵循一定的规则,这里以二叉搜索树为例:
java复制public TreeNode insert(TreeNode root, int val) {
if(root == null) return new TreeNode(val);
if(val < root.val) {
root.left = insert(root.left, val);
} else {
root.right = insert(root.right, val);
}
return root;
}
对于普通二叉树,通常需要指定插入位置,比如插入到第一个有空子节点的位置:
java复制public void insert(TreeNode root, int val) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
TreeNode node = queue.poll();
if(node.left == null) {
node.left = new TreeNode(val);
return;
} else {
queue.offer(node.left);
}
if(node.right == null) {
node.right = new TreeNode(val);
return;
} else {
queue.offer(node.right);
}
}
}
3.2 删除节点
删除节点是二叉树操作中最复杂的之一,特别是对于二叉搜索树:
java复制public TreeNode deleteNode(TreeNode root, int key) {
if(root == null) return null;
if(key < root.val) {
root.left = deleteNode(root.left, key);
} else if(key > root.val) {
root.right = deleteNode(root.right, key);
}
