摘要
本文主要介绍了二叉树的理论基础,并提供了多种二叉树遍历方式,包括递归遍历、迭代遍历以及统一迭代方式。
1、理论基础
1.1 什么是二叉树
二叉树(Binary Tree)是一种树状数据结构,其中每个节点最多有两个子节点,分别是左子节点和右子节点。每个节点可以包含数据(通常是一个值)以及指向其左子节点和右子节点的指针。
二叉树有以下几个重要特性:
- 根节点(Root): 二叉树的顶端节点,是整个树的起始点。
- 节点(Node): 二叉树中的每个元素都被称为节点,每个节点可以包含一个值或数据。
- 父节点和子节点: 除了根节点,每个节点都有一个父节点,以及零个、一个或两个子节点。这些子节点分别是左子节点和右子节点。
- 叶子节点(Leaf Node): 没有子节点的节点被称为叶子节点,它们位于二叉树的末端。
- 深度(Depth): 从根节点到某个节点的路径的长度,表示该节点的深度。
- 高度(Height): 从某个节点到其最远叶子节点的路径的长度,表示以该节点为根的子树的高度。根节点的高度为整棵树的高度。
1.2 二叉树的种类
- 满二叉树(Full Binary Tree): 满二叉树是一种特殊的二叉树,每个节点要么没有子节点(叶子节点),要么恰好有两个子节点。这意味着所有的非叶子节点都有两个子节点,使得树的高度达到最小可能的高度。满二叉树通常用于一些数学和计算机科学的理论研究中。
- 完全二叉树(Complete Binary Tree): 完全二叉树是一种二叉树,除了最后一层,其它每一层都是满的,最后一层的节点从左到右填充,没有留下空缺。完全二叉树常常用于堆的实现,因为它具有很好的平衡性和性能。
- 二叉搜索树(Binary Search Tree,BST): 二叉搜索树是一种有序的二叉树,其中每个节点的值都大于其左子树中的所有节点的值,小于其右子树中的所有节点的值。这个性质使得BST非常适合进行快速的查找、插入和删除操作。BST的性能取决于树的平衡性,最坏情况下可能退化为链表,导致操作的时间复杂度变为O(n)。
- 平衡二叉搜索树(Balanced Binary Search Tree): 平衡二叉搜索树是一种特殊的BST,它的左右子树的高度差不超过1。平衡二叉搜索树的目的是确保树的高度保持较小的范围,以保证各种操作的时间复杂度都接近O(log n)。常见的平衡二叉搜索树包括AVL树和红黑树。
1.3 二叉树的存储方式
- 链式存储(Linked Representation): 这是最常见的方式,也是最直观的。每个二叉树节点都包含数据以及指向其左子树和右子树的指针(引用)。这种方式易于实现和理解,适用于树结构的动态操作,但它可能会占用更多的内存空间,因为每个节点都需要额外的指针。
- 数组存储(Array Representation): 这种方式将二叉树的节点按照某种顺序存储在数组中。通常使用广度优先遍历(层序遍历)的方式进行存储,根节点存储在索引0处,左子节点在2i+1处,右子节点在2i+2处。这种方式节省了指针的内存开销,但对于不完全二叉树,可能会浪费一些数组空间。
1.4 二叉树的定义
二叉树(Binary Tree)是一种数据结构,其中每个节点最多有两个子节点,分别是左子节点和右子节点。二叉树的定义可以使用Java代码表示如下:
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
在上面的代码中,TreeNode 类表示二叉树的节点。每个节点包含一个整数值 val 作为节点的数据,以及左子节点 left 和右子节点 right 的引用。初始时,这两个引用都为空,表示该节点没有左子节点和右子节点。
你可以使用这个定义创建二叉树的节点,连接它们以构建二叉树。例如,创建一个包含三个节点的简单二叉树可以如下所示:
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
这段代码创建了一个根节点为1,左子节点为2,右子节点为3的二叉树。
这只是二叉树的基本定义,实际上,二叉树的应用非常广泛,包括二叉搜索树、平衡二叉树、堆等。具体的二叉树类型和操作会根据应用的不同而有所不同。
2、递归遍历
2.1 前序遍历
// 前序遍历:中左右
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
doPreorderTraversal(root, list);
return list;
}
public void doPreorderTraversal(TreeNode root, List<Integer> list) {
if(root == null) {
return;
}
list.add(root.val);
doPreorderTraversal(root.left, list);
doPreorderTraversal(root.right, list);
}
2.2 中序遍历
// 中序遍历:左中右
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
doInorderTraversal(root, list);
return list;
}
public void doInorderTraversal(TreeNode root, List<Integer> list) {
if(root == null) {
return;
}
doInorderTraversal(root.left, list);
list.add(root.val);
doInorderTraversal(root.right, list);
}
2.3 后序遍历
// 后序遍历:左右中
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
doPostorderTraversal(root, list);
return list;
}
public void doPostorderTraversal(TreeNode root, List<Integer> list) {
if(root == null) {
return;
}
doPostorderTraversal(root.left, list);
doPostorderTraversal(root.right, list);
list.add(root.val);
}