LeetCode刷题小记——树的先序遍历
没事就来刷一刷
题目
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
输入:root = [1,null,2,3]
输出:[1,2,3]
总共分两大类:
- 递归
- 非递归(迭代)
- 模拟系统栈
- 手工辅助栈
- 写法一(好理解,但不通用)
- 写法二(较麻烦,但同时适用中序与后序,强烈推荐)
递归
递归就不说了,太简单。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
preorder(root, res);
return res;
}
public void preorder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
res.add(root.val);
preorder(root.left, res);
preorder(root.right, res);
}
}
非递归
有两种解法:
- 模仿系统栈(纯模仿)
- 使用手工栈辅助(好写)
模仿系统栈
特点:入栈时访问
思想:
遍历到最左边的节点(同时入栈访问),直至为空,然后取出栈顶节点,把其右孩子压栈。
代码:
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
//手动建立栈
Deque<TreeNode> stack = new LinkedList<>();
List<Integer> list = new ArrayList<>();
TreeNode node;
//先判空
if(root == null) return list;
node = root;
while(!stack.isEmpty() || node!=null){
while(node!=null){
list.add(node.val);
stack.push(node);
node = node.left;
}
node = stack.pop();
node = node.right;//右孩子为根
}
return list;
}
}
手工栈辅助
写法一
特点:出栈时访问
思想:出栈时访问,由于访问的顺序是中,左,右。而栈的后进先出的,所以先让右孩子入栈(后访问),再让左孩子入栈。
注意:这种方式好理解,也好写,但是并不是模拟系统栈的迭代写法。
步骤:
- 数据初始化,先把根节点压栈。
- 栈顶出栈,访问。
- 如果右孩子不为空,入栈。
- 如果左孩子不为空,入栈。
- 重复2-4。
代码:
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
//手动建立栈
Deque<TreeNode> stack = new LinkedList<>();
List<Integer> list = new ArrayList<>();
TreeNode node;
//先判空
if(root == null) return list;
stack.push(root);//先把根节点入栈
while(!stack.isEmpty()){
//出栈访问
node = stack.pop();
list.add(node.val);
//先加入右孩子
if(node.right!=null)
stack.push(node.right);
//再加入左孩子
if(node.left!=null)
stack.push(node.left);
}
return list;
}
}
写法二
思想:利用栈的特点,把节点按照逆序放进去,如先序遍历,放入节点顺序为(右,左,中)。
以当前子树为单位,放入栈。
这里借鉴了大佬的颜色标记法。本文用节点类型代替颜色标识。
在放入当前子树根节点的时候,放的是值,不是节点。以此来代替颜色标记。
此代码思想适用于先序遍历与后序遍历,强烈推荐!!!
代码:
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
//初始化
List<Integer> res = new ArrayList<>();//存放结果
Stack stack = new Stack<>();//手工栈
TreeNode node;//工具人
//先判空
if(root == null) return res;
node = root;
stack.push(node);
while(!stack.isEmpty()){
Object o = stack.pop();
if(o instanceof TreeNode){//如果是个未访问子树,压栈
node = (TreeNode)o;
//三个节点压栈,按照后进先出的原则,注意根节点放的是值,不是节点。
if(node.right != null)
stack.push(node.right);
if(node.left != null)
stack.push(node.left);
stack.push(node.val); //放的是值,以此来判断当前子树是否已被压栈。
}else{ //如果是个已访问子树,直接放结果
res.add((Integer)o);
}
}
return res;
}
}
结语:
个人感觉,非递归写法中,手工辅助栈的写法一较为容易理解,也好写。
但是写法二适用于中序与后序遍历,强烈推荐!!!
看个人习惯,喜欢哪个用那个。
这篇博客详细介绍了如何解决LeetCode中的树的先序遍历问题,包括递归和非递归两种方法。递归方式简洁易懂,非递归则分为模拟系统栈和手工辅助栈两种实现。对于非递归,作者推荐了一种适用于中序与后序遍历的写法,通过栈的特点调整节点入栈顺序,实现了通用的遍历。博客还对比了不同方法的优缺点,帮助读者理解并选择适合自己的解题策略。
1055

被折叠的 条评论
为什么被折叠?



