引言

树是非线性数据结构中最重要的一种,二叉树更是面试高频考点。本文系统讲解树的核心概念和经典算法,帮助你全面掌握树数据结构。

一、树的基本概念

1.1 树的定义

1
2
3
4
5
6
7
8
9
10
// 二叉树节点
public class TreeNode {
int val;
TreeNode left;
TreeNode right;

TreeNode(int val) {
this.val = val;
}
}

1.2 树的术语

  • 根节点:树的顶部节点
  • 叶子节点:没有子节点的节点
  • 高度:节点到叶子节点的最长路径
  • 深度:节点到根节点的路径长度
  • 层:根节点为第1层

二、二叉树遍历

2.1 前序遍历(根-左-右)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 递归
public void preorder(TreeNode root) {
if (root == null) return;
System.out.print(root.val + " ");
preorder(root.left);
preorder(root.right);
}

// 迭代
public List<Integer> preorderIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;

Stack<TreeNode> stack = new Stack<>();
stack.push(root);

while (!stack.isEmpty()) {
TreeNode 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;
}

2.2 中序遍历(左-根-右)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 递归
public void inorder(TreeNode root) {
if (root == null) return;
inorder(root.left);
System.out.print(root.val + " ");
inorder(root.right);
}

// 迭代
public List<Integer> inorderIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
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();
result.add(curr.val);
curr = curr.right;
}

return result;
}

2.3 后序遍历(左-右-根)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 递归
public void postorder(TreeNode root) {
if (root == null) return;
postorder(root.left);
postorder(root.right);
System.out.print(root.val + " ");
}

// 迭代
public List<Integer> postorderIterative(TreeNode root) {
LinkedList<Integer> result = new LinkedList<>();
if (root == null) return result;

Stack<TreeNode> stack = new Stack<>();
stack.push(root);

while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.addFirst(node.val); // 头部插入

if (node.left != null) stack.push(node.left);
if (node.right != null) stack.push(node.right);
}

return result;
}

2.4 层序遍历(BFS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;

Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);

while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> level = new ArrayList<>();

for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);

if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}

result.add(level);
}

return result;
}

三、二叉树经典题目

3.1 最大深度(LeetCode 104)

1
2
3
4
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}

3.2 翻转二叉树(LeetCode 226)

1
2
3
4
5
6
7
8
9
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;

TreeNode temp = root.left;
root.left = invertTree(root.right);
root.right = invertTree(temp);

return root;
}

3.3 对称二叉树(LeetCode 101)

1
2
3
4
5
6
7
8
9
10
11
12
public boolean isSymmetric(TreeNode root) {
return isMirror(root, root);
}

private boolean isMirror(TreeNode t1, TreeNode t2) {
if (t1 == null && t2 == null) return true;
if (t1 == null || t2 == null) return false;

return t1.val == t2.val
&& isMirror(t1.left, t2.right)
&& isMirror(t1.right, t2.left);
}

3.4 路径总和(LeetCode 112)

1
2
3
4
5
6
7
8
9
10
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) return false;

if (root.left == null && root.right == null) {
return root.val == targetSum;
}

return hasPathSum(root.left, targetSum - root.val)
|| hasPathSum(root.right, targetSum - root.val);
}

3.5 最近公共祖先(LeetCode 236)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) {
return root;
}

TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);

if (left != null && right != null) {
return root;
}

return left != null ? left : right;
}

四、二叉搜索树

4.1 BST性质

  • 左子树所有节点 < 根节点
  • 右子树所有节点 > 根节点
  • 左右子树也是BST
  • 中序遍历结果有序

4.2 BST插入

1
2
3
4
5
6
7
8
9
10
11
12
13
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) {
return new TreeNode(val);
}

if (val < root.val) {
root.left = insertIntoBST(root.left, val);
} else {
root.right = insertIntoBST(root.right, val);
}

return root;
}

4.3 BST删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
} else {
// 找到要删除的节点
if (root.left == null) return root.right;
if (root.right == null) return root.left;

// 两个子节点都存在:用右子树最小节点替换
TreeNode minNode = findMin(root.right);
root.val = minNode.val;
root.right = deleteNode(root.right, minNode.val);
}

return root;
}

private TreeNode findMin(TreeNode node) {
while (node.left != null) {
node = node.left;
}
return node;
}

4.4 验证BST(LeetCode 98)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean isValidBST(TreeNode root) {
return isValid(root, null, null);
}

private boolean isValid(TreeNode node, Integer min, Integer max) {
if (node == null) return true;

if ((min != null && node.val <= min) || (max != null && node.val >= max)) {
return false;
}

return isValid(node.left, min, node.val)
&& isValid(node.right, node.val, max);
}

五、平衡二叉树(AVL树)

5.1 AVL树定义

任意节点的左右子树高度差不超过1

5.2 旋转操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 左旋
private TreeNode leftRotate(TreeNode node) {
TreeNode newRoot = node.right;
node.right = newRoot.left;
newRoot.left = node;

// 更新高度
updateHeight(node);
updateHeight(newRoot);

return newRoot;
}

// 右旋
private TreeNode rightRotate(TreeNode node) {
TreeNode newRoot = node.left;
node.left = newRoot.right;
newRoot.right = node;

// 更新高度
updateHeight(node);
updateHeight(newRoot);

return newRoot;
}

六、复杂度分析

操作 平均 最坏
搜索 O(log n) O(n)
插入 O(log n) O(n)
删除 O(log n) O(n)
遍历 O(n) O(n)

总结

树算法核心:

  1. 遍历:前中后序、层序
  2. BST:插入、删除、验证
  3. 递归:大部分树问题的解法
  4. 平衡树:AVL、红黑树

参考资料