二叉树
1. 二叉树
树的特殊形式,树的每个节点最多只有两个子节点
- 满二叉树:一个二叉树的所有非叶子节点都存在左右孩子,并且所有叶子节点都在同一层级上。
- 完全二叉树:没有满二叉树那么严格,秩序保证最后一个节点之前的节点都齐全即可。
1.前序中序后序遍历
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
先序:考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)
中序:考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)
后序:考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)
1.1 递归实现
思路与算法
首先我们需要了解什么是二叉树的中序遍历:按照访问左子树——根节点——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候我们按照同样的方式遍历,直到遍历完整棵树。因此整个遍历过程天然具有递归的性质,我们可以直接用递归函数来模拟这一过程。
定义 inorder(root) 表示当前遍历到 root 节点的答案,那么按照定义,我们只要递归调用 inorder(root.left) 来遍历root 节点的左子树,然后将 root 节点的值加入答案,再递归调用inorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。
/**
* 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;
* }
* }
*/
class Solution {
private static final int CAPACITY = 10;
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>(CAPACITY);
PreLookup(root, result);
return result;
// return inorderTraversal2(root);
}
// 前序遍历
private void PreLookup(TreeNode node, List<Integer> result) {
if (node != null) {
result.add(node.val);
middlePreLookup(node.left, result);
middlePreLookup(node.right, result);
}
}
// 中序遍历
private void middleLookup(TreeNode node, List<Integer> result) {
if (node != null) {
middlePreLookup(node.left, result);
result.add(node.val);
middlePreLookup(node.right, result);
}
}
// 后序遍历
private void afterLookup(TreeNode node, List<Integer> result) {
if (node != null) {
middlePreLookup(node.left, result);
middlePreLookup(node.right, result);
result.add(node.val);
}
}
}
1.2 迭代实现
/**
* 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;
* }
* }
*/
class Solution {
private static final int CAPACITY = 10;
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>(CAPACITY);
middlePreLookup(root, result);
return result;
// return inorderTraversal2(root);
}
// 迭代方法 使用栈
// 中序遍历
public List<Integer> inorderTraversal2(TreeNode root) {
List<Integer> result = new ArrayList<>(CAPACITY);
Stack<TreeNode> stack = new Stack<>();
// 中序遍历:左根右,出栈顺序:左根右,入栈顺序:右根左
while (root != null || !stack.isEmpty()) {
// 当curRoot非空时,一直寻找左叶子节点并将其入栈
while (root != null) {
stack.push(root);
root = root.left;
}
// 此时root为null, 那么它的父节点为左叶子节点且已入栈,此时需要将它出栈
root = stack.pop();
result.add(root.val);
root = root.right;
}
return result;
}
}
// 前序遍历
public List<Integer> inorderTraversal2(TreeNode root) {
List<Integer> result = new ArrayList<>(CAPACITY);
Stack<TreeNode> stack = new Stack<>();
// 前序遍历:根左右,出栈顺序:根左右,入栈顺序:右左根
while (root != null || !stack.isEmpty()) {
// 当curRoot非空时,按照右左的顺序进行入栈
result.add(root.val);
if (root.right != null) {
stack.push(root.right);
}
if (root.left != null) {
stack.push(root.left);
}
root = stack.pop();
}
return result;
}
// 后序遍历
public List<Integer> inorderTraversal2(TreeNode root) {
List<Integer> result = new ArrayList<>(CAPACITY);
Stack<TreeNode> stack = new Stack<>();
// 后序遍历:左右根,出栈顺序:左右根,入栈顺序:根右左
// 精髓在于先出栈先入队(忘队尾方向)
// ArrayList.add(int index, E e) 在index处插入元素,原index处及以后的元素右移
if (root == null) return stack;
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
result.add(0, root.val);
if root.left != null stack.push(root.left);
if root.right != null stack.push(root.right);
}
return result;
}
}