二叉树
什么是二叉树?
二叉树(Binary Tree) 是每个节点最多有两个子树的树结构,通常子树被称作"左子树"和"右子树"。
首先,定义二叉树节点的Java类:
java
public class TreeNode<T> {
public T val; // 节点值
public TreeNode<T> left; // 左子节点
public TreeNode<T> right; // 右子节点
public TreeNode() {}
public TreeNode(T val) {
this.val = val;
}
public TreeNode(T val, TreeNode<T> left, TreeNode<T> right) {
this.val = val;
this.left = left;
this.right = right;
}
}
二叉树有五种基本形态:
- 空二叉树
- 只有一个根节点
- 只有左子树
- 只有右子树
- 左右子树都有
二叉树的分类
满二叉树
除了叶子节点外,每个节点都有左右两个子节点,且所有叶子节点都在最底层。
完全二叉树
除最后一层外,其余各层节点数都达到最大,且最后一层的叶子节点从左到右连续排列 。Java中的PriorityQueue底层就是基于完全二叉树的数组实现。
二叉搜索树(BST)
具有以下性质:
- 左子树上所有节点的值 < 根节点的值
- 右子树上所有节点的值 > 根节点的值
- 左右子树本身也是BST
平衡二叉树(AVL树)
任意节点的左右子树高度差不超过1。Java的TreeMap底层使用的红黑树就是一种自平衡二叉搜索树。
二叉树常用操作
二叉树的遍历
前序遍历(根 → 左 → 右)
java
// 递归实现
public void preOrder(TreeNode<T> root, List<T> result) {
if (root == null) return;
result.add(root.val); // 访问根
preOrder(root.left, result); // 遍历左子树
preOrder(root.right, result); // 遍历右子树
}
java
// 迭代实现(栈)
public List<T> preOrderIterative(TreeNode<T> root) {
List<T> result = new ArrayList<>();
if (root == null) return result;
Deque<TreeNode<T>> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode<T> node = stack.pop();
result.add(node.val);
// 先压右,再压左(栈是后进先出)
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
return result;
}
中序遍历(左 → 根 → 右)
对于二叉搜索树,中序遍历会得到一个有序序列。
dart
// 递归实现
public void inOrder(TreeNode<T> root, List<T> result) {
if (root == null) return;
inOrder(root.left, result);
result.add(root.val);
inOrder(root.right, result);
}
java
// 迭代实现
public List<T> inOrderIterative(TreeNode<T> root) {
List<T> result = new ArrayList<>();
Deque<TreeNode<T>> stack = new ArrayDeque<>();
TreeNode<T> curr = root;
while (curr != null || !stack.isEmpty()) {
// 一路向左
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
result.add(curr.val);
curr = curr.right;
}
return result;
}
后序遍历(左 → 右 → 根)
常用于先处理子节点再处理父节点的场景。
java
// 递归实现
public void postOrder(TreeNode<T> root, List<T> result) {
if (root == null) return;
postOrder(root.left, result);
postOrder(root.right, result);
result.add(root.val);
}
层序遍历(BFS)
借助队列实现,常用于解决最短路径类问题。
java
public List<List<T>> levelOrder(TreeNode<T> root) {
List<List<T>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode<T>> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<T> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode<T> node = queue.poll();
currentLevel.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(currentLevel);
}
return result;
}
求节点总数
java
// 1. 求节点总数
public int countNodes(TreeNode<T> root) {
if (root == null) return 0;
return countNodes(root.left) + countNodes(root.right) + 1;
}
求叶子节点个数
java
public int countLeaves(TreeNode<T> root) {
if (root == null) return 0;
if (root.left == null && root.right == null) return 1;
return countLeaves(root.left) + countLeaves(root.right);
}
求树的最大深度
java
// 3. 求树的最大深度
public int maxDepth(TreeNode<T> root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
判断两棵树是否相同
java
// 4. 判断两棵树是否相同
public boolean isSameTree(TreeNode<T> p, TreeNode<T> q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
if (!p.val.equals(q.val)) return false;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
判断是否是对称二叉树
java
// 5. 判断是否是对称二叉树
public boolean isSymmetric(TreeNode<T> root) {
if (root == null) return true;
return isMirror(root.left, root.right);
}
private boolean isMirror(TreeNode<T> left, TreeNode<T> right) {
if (left == null && right == null) return true;
if (left == null || right == null) return false;
return left.val.equals(right.val)
&& isMirror(left.left, right.right)
&& isMirror(left.right, right.left);
}
翻转二叉树(镜像)
java
// 6. 翻转二叉树(镜像)
public TreeNode<T> invertTree(TreeNode<T> root) {
if (root == null) return null;
// 交换左右子树
TreeNode<T> temp = root.left;
root.left = invertTree(root.right);
root.right = invertTree(temp);
return root;
}
Java集合框架中的二叉树
Java标准库中多处使用了基于二叉树的实现:
| 类名 | 底层数据结构 | 特点 |
|---|---|---|
| TreeMap | 红黑树 | 有序键值对,O(log n)操作 |
| TreeSet | 红黑树 | 有序不重复集合 |
| PriorityQueue | 二叉堆(完全二叉树) | 优先级队列 |
| HashMap(JDK8+) | 数组+链表+红黑树 | 冲突严重时链表转红黑树 |