系列博客目录
基础知识
在 Java 中,二叉树(Binary Tree)是一种常见的树形数据结构,它由一个节点组成,节点具有最多两个子节点,通常称为左子节点和右子节点。二叉树在计算机科学中广泛用于表示数据、优化搜索操作、实现排序算法等。
以下是二叉树的相关理论内容:
1. 二叉树的基本定义
-
节点:二叉树中的基本元素,每个节点包含:
- 一个值(或数据)
- 一个指向左子节点的引用
- 一个指向右子节点的引用
-
根节点:二叉树的第一个节点,也就是最上层的节点。每棵树只有一个根节点。
-
子树:树中的任意节点及其所有后代节点称为该节点的子树。每个节点的左子树和右子树本身也是二叉树。
-
叶子节点:没有子节点的节点称为叶子节点。叶子节点是二叉树的终端节点。
-
深度(Depth):节点的深度是指该节点从根节点开始的路径长度。根节点的深度为 0。
-
高度(Height):树的高度是根节点的深度。一个节点的高度是指从该节点到其所有子节点的最大深度。
2. 二叉树的性质
- 每个节点最多有两个子节点:每个节点最多有一个左子节点和一个右子节点。
- 最多有 2 d 2^d 2d 个节点:如果二叉树的高度为
d
,那么在最坏情况下(即完全二叉树),二叉树的节点数最多为 2 d − 1 2^d-1 2d−1。 - 二叉树的节点总数与高度的关系:
- 对于一个完全二叉树,如果有
n
个节点,树的高度大约是log₂n
。
- 对于一个完全二叉树,如果有
3. 二叉树的类型
-
满二叉树:一棵二叉树如果每个非叶子节点都有左右两个子节点,并且所有叶子节点都在同一层上,那么这棵树是满二叉树。
-
完全二叉树:一棵二叉树如果除了最后一层外,其他层都已经完全填满,且最后一层的叶子节点都靠左排列,那么它是完全二叉树。
-
平衡二叉树(AVL树):一棵二叉树如果它的左右子树高度差的绝对值不超过 1,那么它是平衡二叉树。
-
二叉搜索树(BST,Binary Search Tree):一种特殊的二叉树,具有以下特点:
- 左子树的所有节点的值小于根节点的值。
- 右子树的所有节点的值大于根节点的值。
- 每个节点的左子树和右子树也是二叉搜索树。
-
红黑树:一种自平衡的二叉搜索树,其中每个节点都包含一个颜色属性(红色或黑色),通过一定的规则来保持树的平衡。
4. 二叉树的遍历
遍历是指访问二叉树的所有节点,通常有以下几种方式:
-
前序遍历(Preorder Traversal):访问根节点 -> 遍历左子树 -> 遍历右子树。
- 递归实现:
void preorder(TreeNode root) { if (root == null) return; System.out.print(root.val + " "); preorder(root.left); preorder(root.right); }
- 递归实现:
-
中序遍历(Inorder Traversal):遍历左子树 -> 访问根节点 -> 遍历右子树。对于二叉搜索树,中序遍历的结果是有序的。
- 递归实现:
void inorder(TreeNode root) { if (root == null) return; inorder(root.left); System.out.print(root.val + " "); inorder(root.right); }
- 迭代实现:
class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); Deque<TreeNode> treeNodes = new LinkedList<>(); while(root!=null || !treeNodes.isEmpty()){ while(root!=null){ treeNodes.push(root); root = root.left; } final TreeNode pop = treeNodes.poll(); res.add(pop.val); root = pop.right; } return res; } }
- 递归实现:
-
后序遍历(Postorder Traversal):遍历左子树 -> 遍历右子树 -> 访问根节点。
- 递归实现:
void postorder(TreeNode root) { if (root == null) return; postorder(root.left); postorder(root.right); System.out.print(root.val + " "); }
- 递归实现:
-
层序遍历(Level-order Traversal):按照从上到下、从左到右的顺序逐层访问树的节点。通常使用队列(Queue)实现。
- 队列实现:
void levelOrder(TreeNode root) { if (root == null) return; Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { 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); } }
- 队列实现:
5. 二叉树的实现
二叉树通常使用节点类(TreeNode
)来实现,节点类包括值、左子节点和右子节点的引用。以下是一个简单的二叉树节点类:
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
6. 常见的二叉树算法
-
查找最大深度(高度):递归计算树的最大深度。
int maxDepth(TreeNode root) { if (root == null) return 0; int leftDepth = maxDepth(root.left); int rightDepth = maxDepth(root.right); return Math.max(leftDepth, rightDepth) + 1; }
-
查找最小深度:与最大深度类似,但返回的是树中最短的路径。
int minDepth(TreeNode root) { if (root == null) return 0; if (root.left == null) return minDepth(root.right) + 1; if (root.right == null) return minDepth(root.left) + 1; return Math.min(minDepth(root.left), minDepth(root.right)) + 1; }
-
判断二叉树是否对称:检查二叉树是否是镜像对称的(即左右子树镜像)。
boolean isSymmetric(TreeNode root) { if (root == null) return true; return isMirror(root.left, root.right); } 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); }
-
判断二叉搜索树:检查二叉树是否满足二叉搜索树的条件(左子树的值小于根,右子树的值大于根)。
boolean isValidBST(TreeNode root) { return isValidBSTHelper(root, Long.MIN_VALUE, Long.MAX_VALUE); } boolean isValidBSTHelper(TreeNode node, long min, long max) { if (node == null) return true; if (node.val <= min || node.val >= max) return false; return isValidBSTHelper(node.left, min, node.val) && isValidBSTHelper(node.right, node.val, max); }
7. 二叉树的应用
- 堆(Heap):一种完全二叉树,用于实现优先队列。最小堆和最大堆都是基于二叉树的结构。
- 前缀表达式和后缀表达式:二叉树可用于解析和计算前缀或后缀表达式。
- 最短路径和最小生成树:二叉树的变种,如哈夫曼树,可用于数据压缩。
总结
二叉树是一种非常重要的数据结构,具有许多不同的变种和应用。它可以通过递归方法非常方便地进行遍历和操作,并且广泛用于解决各种算法问题,如查找、排序、路径查找等。
例题
class TreeNode {
int val;
TreeNode left;
TreeNode right;