一、递归遍历(深度优先遍历)
1. 前序
public class lc144 {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
traversal(root,res);
return res;
}
private void traversal(TreeNode root, List<Integer> res) {
if (root==null) {
return;
}
res.add(root.val);
traversal(root.left,res);
traversal(root.right,res);
}
}
2. 中序
public class lc94 {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
traversal(root,res);
return res;
}
private void traversal(TreeNode root, List<Integer> res) {
if (root==null) {
return;
}
traversal(root.left,res);
res.add(root.val);
traversal(root.right,res);
}
}
3. 后序
public class lc145 {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
traversal(root,res);
return res;
}
private void traversal(TreeNode root, List<Integer> res) {
if (root==null) {
return;
}
traversal(root.left,res);
traversal(root.right,res);
res.add(root.val);
}
二、迭代遍历
迭代遍历就是利用【栈】来模拟递归过程。
1. 前序
前序遍历:根左右
在迭代法中,访问的元素就是要处理的元素。
入栈顺序:根节点 → 右孩子 → 左孩子
这样,出栈就是【根左右】
public class lc145 {
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;
}
}
2. 后序
前序遍历:左右根
在迭代法中,访问的元素就是要处理的元素。
只需要按照前序代码的思路,将入栈顺序改为:根节点 → 左孩子 → 右孩子,
再反转result数组,就可以得到【左右根】
public class lc145 {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
if (root==null) {
return res;
}
Stack<TreeNode> stack=new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
res.add(node.val);
if (node.left!=null) {
stack.push(node.left);
}
if (node.right!=null) {
stack.push(node.right);
}
}
Collections.reverse(res);
return res;
}
}
3. 中序
中序遍历:左根右
在迭代法中,访问的元素并不是要处理的元素。
需要借用指针的遍历来访问节点,栈则用来处理节点上的元素。
从根节点开始,一层一层向下访问并入栈,直到到达树左面的最底部,再开始处理节
点:将当前节点出栈并放入result数组,再将右孩子放入result数组。
public class lc94 {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
if (root==null) {
return res;
}
Stack<TreeNode> stack=new Stack<>();
TreeNode cur = root;
while (cur!=null || !stack.isEmpty()) {
if (cur!=null) {
stack.push(cur);
cur=cur.left;
}else {
TreeNode node = stack.pop();
res.add(node.val);
cur=node.right;
}
}
return res;
}
}
三、层序遍历(广度优先遍历)
借助【队列】来保存每一层的遍历过的元素。
每弹出一个节点时,要把这个节点的左右孩子加入队列。
public class lc102 {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if (root==null) {
return res;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> list=new ArrayList<>();
int size= queue.size();
while (size-- >0) {
TreeNode node = queue.poll();
list.add(node.val);
//每当弹出一个节点, 将它的左右节点入队列
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
res.add(list);
}
return res;
}
}
四、深度与高度
深度:任意一个节点到根节点的距离
前序遍历
高度:任意一个节点到叶子节点的距离
后序遍历
五、翻转二叉树
1. 前序
public class lc226 {
public TreeNode invertTree(TreeNode root) {
//终止条件
if (root == null) {
return null;
}
//前序
swapChildren(root);
invertTree(root.left);
invertTree(root.right);
return root;
}
private void swapChildren(TreeNode root) {
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
}
}
2. 层序
public class lc226 {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
while (size-- >0) {
TreeNode node = queue.poll();
swapChildren(node);
//每当弹出一个节点, 将它的左右节点入队列
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
}
return root;
}
private void swapChildren(TreeNode root) {
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
}
}