二叉树理论基础
满二叉树:总共有k层,则节点总数为2^k - 1。根节点为第一层。
完全二叉树:除了最后一层,其余的都是满二叉树。并且最下面一层从左到右连续。
二叉搜索树:设x为二叉树中的一个节点,x节点包含关键字Key,节点x的Key值记为Key[x]。如果y是x的左子树中的一个节点,则Key[y]<=Key[x];如果y是x的有子树的一个节点,则Key[y]>=Key[x]。
平衡二叉搜索树:左右子树的高度差不能超过1。Java中TreeMap底层的红黑树也是一种平衡二叉树。
可以转换为线性存储的数组:root的下标为i 则左孩子:2i+1;右孩子:2i+2(使用前序遍历)。
遍历方式:
1.深度优先搜索(二叉树的前、中、后序遍历)
利用递归的方式实现。也可利用迭代法实现。
2.广度优先搜索(层序遍历)
利用迭代法实现。依赖队列实现。
二叉树的遍历:
思路一:递归
leetcode 144 二叉树的前序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
preorder(root, result);
return result;
}
private void preorder(TreeNode node, List<Integer> result) {
if (node == null) return;
result.add(node.val); // 访问根节点
preorder(node.left, result); // 遍历左子树
preorder(node.right, result);// 遍历右子树
}
leetcode 145 二叉树的后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
postorder(root, result);
return result;
}
private void postorder(TreeNode node, List<Integer> result) {
if (node == null) return;
postorder(node.left, result); // 遍历左子树
postorder(node.right, result); // 遍历右子树
result.add(node.val); // 访问根节点
}
leetcode 94 二叉树的中序遍历
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
inorder(root, result);
return result;
}
private void inorder(TreeNode node, List<Integer> result) {
if (node == null) return;
inorder(node.left, result); // 遍历左子树
result.add(node.val); // 访问根节点
inorder(node.right, result); // 遍历右子树
}
思路二:迭代
leetcode 144 二叉树的前序遍历
public List<Integer> preorderTraversal(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;
}
leetcode 145 二叉树的后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();
Stack<TreeNode> output = new Stack<>(); // 辅助栈
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
output.push(node);
if (node.left != null) stack.push(node.left); // 先左后右
if (node.right != null) stack.push(node.right);
}
while (!output.isEmpty()) {
result.add(output.pop().val); // 反向输出
}
return result;
}
leetcode 94 二叉树的中序遍历
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current); // 左子树节点入栈
current = current.left;
}
current = stack.pop(); // 访问根节点
result.add(current.val);
current = current.right; // 访问右子树
}
return result;
}
时间与空间复杂度
时间复杂度:所有遍历方法均为 O(n),其中 n 是节点总数。
空间复杂度:递归:O(h),其中 h 是树的高度。迭代:O(h),由于使用了栈来保存节点。
总结
前序遍历 常用于拷贝树或表达式求值。
中序遍历 常用于生成升序排序的序列。
后序遍历 常用于删除或释放内存。