一、前序遍历
题目链接:144. 二叉树的前序遍历
/**
* <pre>
* 1.递归
* 2.迭代:显式的模拟一个栈,本质上等价于递归
* 3.Morris:以某个根节点开始,找到他左子树的最右侧节点之后与这个根节点进行连接(其实就是利用null节点来模拟栈,在先序遍历中左子树的最右侧节点的下一个节点就应该回到根节点了(即递归回去以寻找右节点),因为没有用栈额外存储,所以用该null节点事先保存下来,以便后续遍历完之后能回去根节点)
* </pre>
*
* @author <a href="https://github.com/Ken-Chy129">Ken-Chy129</a>
* @date 2023/1/13 10:52
*/
public class 二叉树的前序遍历144 {
// 递归
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
preorder(res, root);
return res;
}
public void preorder(List<Integer> res, TreeNode root) {
if (root == null) {
return;
}
res.add(root.val);
preorder(res, root.left);
preorder(res, root.right);
}
// 迭代
public List<Integer> preorderTraversal2(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (!stack.empty() || node != null) {
while (node != null) {
// 根->左
res.add(node.val);
stack.push(node);
node = node.left;
}
node = stack.pop().right; // 右
}
return res;
}
// Morris
public List<Integer> preorderTraversal3(TreeNode root) {
List<Integer> res = new ArrayList<>();
TreeNode cur1 = root, cur2; // cur1表示当前遍历的节点
while (cur1 != null) {
cur2 = cur1.left; // 左子树的根节点
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
// 寻找左子树最右侧节点
cur2 = cur2.right;
}
if (cur2.right == null) {
// 将最右侧节点的右指针连接上当前遍历的根节点
cur2.right = cur1;
res.add(cur1.val);
cur1 = cur1.left;
continue;
} else {
// 最右侧节点已经连接过根节点,则说明这是第二次来到此处,表明已经处理完了左子树,准备回到根节点处,则将其重置为null还原
cur2.right = null;
}
} else {
res.add(cur1.val);
}
// 如果当前便利的节点的左子树为空,则往右遍历
cur1 = cur1.right;
}
return res;
}
}
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;
}
}
二、中序遍历
题目链接:94. 二叉树的中序遍历
/**
* <pre>
*
* </pre>
*
* @author <a href="https://github.com/Ken-Chy129">Ken-Chy129</a>
* @date 2023/1/13 14:00
*/
public class 二叉树的中序遍历94 {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
inorder(res, root);
return res;
}
public void inorder(List<Integer> res, TreeNode root) {
if (root == null) {
return;
}
inorder(res, root.left);
res.add(root.val);
inorder(res, root.right);
}
// 迭代
public List<Integer> inorderTraversal2(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (!stack.empty() || node != null) {
while (node != null) {
// 找到最左子节点
stack.push(node);
node = node.left;
}
TreeNode parent = stack.pop();
res.add(parent.val); // 根(左)
node = parent.right; // 右
}
return res;
}
// morris
public List<Integer> inorderTraversal3(TreeNode root) {
List<Integer> res = new ArrayList<>();
TreeNode cur1 = root, cur2;
while (cur1 != null) {
cur2 = cur1.left;
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
cur2 = cur2.right;
}
if (cur2.right == null) {
cur2.right = cur1;
cur1 = cur1.left;
continue;
} else {
cur2.right = null;
}
}
res.add(cur1.val);
cur1 = cur1.right;
}
return res;
}
}
三、后序遍历
题目链接:145. 二叉树的后序遍历
/**
* <pre>
* 与中序的不同之处在于:
* 中序遍历中,从栈中弹出的节点,其左子树是访问完了,可以直接访问该节点,然后接下来访问右子树。
* 后序遍历中,从栈中弹出的节点,我们只能确定其左子树肯定访问完了,但是无法确定右子树是否访问过。
* 因此,我们在后序遍历中,引入了一个prev来记录历史访问记录。当访问完一棵子树的时候,我们用prev指向该节点。
* </pre>
*
* @author <a href="https://github.com/Ken-Chy129">Ken-Chy129</a>
* @date 2023/1/13 14:43
*/
public class 二叉树的后序遍历145 {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
postorder(res, root);
return res;
}
public void postorder(List<Integer> res, TreeNode root) {
if (root == null) {
return;
}
postorder(res, root.left);
postorder(res, root.right);
res.add(root.val);
}
// 迭代法
public List<Integer> postorderTraversal2(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null; // 记录上次访问的节点
while (!stack.empty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
// 左子树访问完
root = stack.pop();
// 先需要确认是否有右子树或者右子树是否已经遍历过
// 如果没有或者已经访问过则当前节点可以加入
if (root.right == null || pre == root.right) {
res.add(root.val);
pre = root;
root = null;
} else {
stack.push(root);
root = root.right;
}
}
return res;
}
}
四、层序遍历
题目链接:102. 二叉树的层序遍历
/**
* <pre>
* 广度优先搜索,使用队列维护
* </pre>
*
* @author <a href="https://github.com/Ken-Chy129">Ken-Chy129</a>
* @date 2023/1/13 15:19
*/
public class 二叉树的层序遍历102 {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if (root == null) {
// 需要直接返回,不然将null入队会导致后面出现空指针异常
return res;
}
queue.add(root);
while (!queue.isEmpty()) {
List<Integer> list = new ArrayList<>();
int size = queue.size(); // 需要提前保存size,不然后面add会导致size一直改变
for (int i=0; i<size; i++) {
TreeNode poll = queue.poll();
if (poll.left != null) {
queue.add(poll.left);
}
if (poll.right != null) {
queue.add(poll.right);
}
list.add(poll.val);
}
res.add(list);
}
return res;
}
}