每日算法 - 二叉树前序遍历(基础算法)

目录

题目

解题思路

代码

迭代 + 栈

递归实现前序遍历(最基础)

莫里斯遍历(了解)

总结

完整测试代码及结果


题目

给定一个二叉树,返回它的 前序 遍历。

 示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 


解题思路

https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/er-cha-shu-de-qian-xu-bian-li-by-leetcode/

 

关于二叉树的遍历,有两种主流方式:

  • 深度优先搜索(DFS

在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。

深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为前序遍历,中序遍历和后序遍历

  • 宽度优先搜索(BFS

我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。

 


前序遍历代码

 

  • 迭代 + 栈

使用栈遍历数,然后弹出栈顶元素用栈保存结果序列。

/**
     * 迭代:
     * 使用栈保存数。从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。
     */
    public List<Integer> preorderTraversal(TreeNode root){
        LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
        LinkedList<Integer> res = new LinkedList<Integer>();
        if (root == null) {
            return res;
        }

        stack.add(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pollLast();
            res.add(node.val);
            
            if (node.right != null) {
                stack.add(node.right);// 栈:先进后出 ,所以出来的res为(left,right)
            }
            if (node.left != null) {
                stack.add(node.left);
            }
        }
        return res;
    }

算法复杂度

时间复杂度:访问每个节点恰好一次,时间复杂度为 O(N)O(N) ,其中 NN 是节点的个数,也就是树的大小。
空间复杂度:取决于树的结构,最坏情况存储整棵树,因此空间复杂度是 O(N)O(N)。


  • 递归实现前序遍历(最基础)

 

/**
     * 递归实现前序遍历
     */

    public static List<Integer> preorderTraversalByRecursive(TreeNode root) {
        List<Integer> res = new LinkedList<Integer>();
        if (root == null) return res;

        dfs(root, res);

        return res;
    }

    private static void dfs(TreeNode tree , List<Integer> res) {

        // 根
        res.add(tree.val);
        // 左
        if (tree.left != null ) {
            dfs(tree.left, res);
        }
        // 右
        if (tree.right != null){
            dfs(tree.right, res);
        }
    }

 

 


  • 莫里斯遍历(了解)

        方法基于 莫里斯原理,可以优化空间复杂度。算法不会使用额外空间,只需要保存最终的输出结果。如果实时输出结果,那么空间复杂度是 O(1)O(1)。

 

算法的思路是从当前节点向下访问先序遍历的前驱节点,每个前驱节点都恰好被访问两次。

首先从当前节点开始,向左孩子走一步然后沿着右孩子一直向下访问,直到到达一个叶子节点(当前节点的中序遍历前驱节点),所以我们更新输出并建立一条伪边 predecessor.right = root 更新这个前驱的下一个点。如果我们第二次访问到前驱节点,由于已经指向了当前节点,我们移除伪边并移动到下一个顶点。

如果第一步向左的移动不存在,就直接更新输出并向右移动。

/**
     *  Morris
     */

    public static List<Integer> preorderTraversalByMorris(TreeNode root) {
        LinkedList<Integer> output = new LinkedList<Integer>();

        TreeNode node = root;
        while (node != null) {
            if (node.left == null) {
                output.add(node.val);
                node = node.right;
            } else {
                TreeNode predecessor = node.left;
                while ((predecessor.right != null) && (predecessor.right != node)) {
                    predecessor = predecessor.right;
                }

                if (predecessor.right == null) {
                    output.add(node.val);
                    predecessor.right = node;
                    node = node.left;
                } else {
                    predecessor.right = null;
                    node = node.right;
                }
            }
        }
        return output;
    }

总结

1、前序遍历的三种常见写法;

2、二叉树的遍历总结,见连接。


完整测试代码及结果

 

 

import java.util.LinkedList;
import java.util.List;

/**
 * 前序遍历
 */
public class PreorderTraversal {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.setLeft(new TreeNode(0));
        root.setRight(new TreeNode(2));
        root.getRight().setLeft(new TreeNode(3));
        root.getRight().setRight(new TreeNode(0));

        // 递归实现
        List<Integer> res = preorderTraversalByRecursive(root);

        // 迭代实现
        List<Integer> res2 = preorderTraversal(root);

        // Morris实现
        List<Integer> res3 = preorderTraversalByMorris(root);

        System.out.println("递归实现:");
        for (Integer item : res) {
            System.out.print(item + " ");
        }

        System.out.println(" ");
        System.out.println("迭代实现:");
        for (Integer item : res2) {
            System.out.print(item + " ");
        }

        System.out.println(" ");
        System.out.println("Morris实现:");
        for (Integer item : res2) {
            System.out.print(item + " ");
        }
    }


    /**
     * 迭代:
     * 使用栈保存数。从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。
     */
    public static List<Integer> preorderTraversal(TreeNode root) {
        LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
        LinkedList<Integer> res = new LinkedList<Integer>();
        if (root == null) {
            return res;
        }

        stack.add(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pollLast();
            res.add(node.val);

            if (node.right != null) {
                stack.add(node.right);// 栈:先进后出 ,所以出来的res为(left,right)
            }
            if (node.left != null) {
                stack.add(node.left);
            }
        }
        return res;
    }

    /**
     * 递归实现前序遍历
     */

    public static List<Integer> preorderTraversalByRecursive(TreeNode root) {
        List<Integer> res = new LinkedList<Integer>();
        if (root == null) return res;

        dfs(root, res);

        return res;
    }

    private static void dfs(TreeNode tree, List<Integer> res) {
        // 根
        res.add(tree.val);
        // 左
        if (tree.left != null) {
            dfs(tree.left, res);
        }
        // 右
        if (tree.right != null) {
            dfs(tree.right, res);
        }
    }

    /**
     *  Morris
     */

    public static List<Integer> preorderTraversalByMorris(TreeNode root) {
        LinkedList<Integer> output = new LinkedList<Integer>();

        TreeNode node = root;
        while (node != null) {
            if (node.left == null) {
                output.add(node.val);
                node = node.right;
            } else {
                TreeNode predecessor = node.left;
                while ((predecessor.right != null) && (predecessor.right != node)) {
                    predecessor = predecessor.right;
                }

                if (predecessor.right == null) {
                    output.add(node.val);
                    predecessor.right = node;
                    node = node.left;
                } else {
                    predecessor.right = null;
                    node = node.right;
                }
            }
        }
        return output;
    }


}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    public TreeNode(int val) {
        this.val = val;
    }

    public void setVal(int val) {
        this.val = val;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }

    public int getVal() {
        return val;
    }

    public TreeNode getLeft() {
        return left;
    }

    public TreeNode getRight() {
        return right;
    }
}

 

测试结果

输入结构如题,null用0代替。


递归实现:
1 0 2 3 0  
迭代实现:
1 0 2 3 0  
Morris实现:
1 0 2 3 0 
Process finished with exit code 0

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值