代码随想录|二叉树的遍历

文章:代码随想录

状态:只会递归的遍历,迭代遍历要多重复思考

递归的写法:

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result= new ArrayList<>();
        preorder(root,result);
        return result;
    }

    private void preorder(TreeNode root, List<Integer> result) {
        if (root==null){return;}
        result.add(root.val);
        preorder(root.left,result);
        preorder(root.right,result);
    }

    private void inorder(TreeNode root, List<Integer> result) {
        if(root==null){return;}
        inorder(root.left, result);
        result.add(root.val);
        inorder(root.right,result);

    }

   private void postorder(TreeNode root, List<Integer> result) {
    if(root==null){return;}
    postorder(root.left,result);
    postorder(root.right,result);
    result.add(root.val);

    }

然后介绍下统一迭代和不统一迭代的方法,代码中写明了思路:

不统一迭代:

//基本所有递归都可以通过栈实现
//前序遍历,中左右,栈是先进后出,所以要先加入右子节点才是左子节点。
//但是这里的逻辑最简单,因为过程是从上往下走的过程中就可以记录中节点的值
public class PreTraversal {
    public List<Integer> preorderTraversal(TreeNode root) {
        Stack<TreeNode> nodes=new Stack<>();
        List<Integer> result=new ArrayList<>();
        nodes.push(root);
        while (!nodes.empty()){
            TreeNode top = nodes.pop();
            if(top==null){continue;}
            result.add(top.val);
            nodes.push(top.right);
            nodes.push(top.left);
        }

        return result;
    }
}   



//其实这里也是需要遍历到底才能开始记录,但是可以通过翻转前序遍历的过程来实现这个结果.
    //比如前序遍历是记录中左右,我们让左右节点加入栈的顺序调换,就变成了中右左.那么list记录的结果就是中右左.我们只需要将最后结果list做reverse,那么就变成了左右中。
    public List<Integer> preorderTraversal(TreeNode root) {
        Stack<TreeNode> nodes=new Stack<>();
        List<Integer> result=new ArrayList<>();
        nodes.push(root);
        while (!nodes.empty()){
            TreeNode top = nodes.pop();
            if(top==null){continue;}
            result.add(top.val);
            nodes.push(top.left);
            nodes.push(top.right);
        }
        Collections.reverse(result);
        return result;
    }


   //中序遍历 前中后
    //这里用一个指针来模拟从头指针往下遍历,当指针为空的时候就到叶子节点了。那么我们一路向左走的话,遇到第一个叶子节点就是我们要处理的值。
    //将这个值pop出来记录到list中之后,再检查这个节点的右边,如果这个节点的右边也没有了那么就要继续pop,此时为上一层的父节点。这时对上一层的父节点来说左边已经pop掉了,
    // 所以这时候pop的值就是中了,然后指针再变成这个节点的右节点再开始循环。
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result= new ArrayList<>();
        Stack<TreeNode> nodes=new Stack<>();
        //初始化指针
        TreeNode cur=root;
        //要当指针指向为空同时栈为空时才能证明所有元素处理完了。
        //因为在pop之后我们要再下一个循环才判断是否为空,那如果这时候遍历到根节点.pop掉之后,cur指向根节点的又子树,但是这时候栈空了循环就会退出,所以要同时满足两者.
        while (cur!=null ||!nodes.empty()){
            //如果这时候指针不为空,那么就证明当前是访问的的元素只需要记录,然后继续往左走。
            if(cur!=null){
                nodes.push(cur);
                //指针往左走
                cur=cur.left;
            }else{
                //当前指针为空时,那么指针的父节点就是当前需要处理的节点。
                //我们同时用cur来记录这个节点
                cur=nodes.pop();
                //list添加结果
                result.add(cur.val);
                //往这个节点的右节点走,遍历右边节点
                cur=cur.right;

            }

        }
        return result;
    }

统一迭代:

public class Traversal {
    //我觉得着这种思路要理解空节点标记
    //我认为这个null空节点标记始终标记中节点,因为只有中节点的值可以直接pop,但是左和右可以一直往下延申找到叶子节点。
    // 然后每一轮(每三个节点)最后压入栈的就是遍历顺序的第一个节点。比如前序遍历最后压入栈的就是中节点,中序就是左节点。
    //这个第一个节点其实我的理解就是每次往下遍历的入口,同时也是最后需要第一个pop出的元素。比如前序遍历就是顺着中节点往下走,中序遍历就是顺着左子节点一直往下遍历,后续就是从左到右最后才是中.
    //这个null标记后面的元素其实可以理解为每一轮需要留下的/静止的元素.比如前序的每一轮遍历完需要把中节点放最后,因为前序遍历可以一边访问的过程一边就处理中节点,而中后序遍历就必须先沿着一条路访问到子节点才行.
    //那么中序的每一轮就是栈中就是 [右 中 null 左],然后左会被不断的pop然后沿着这条路往下走直到遇到叶子节点,所以null指针也可以理解为每一轮的不变量,等左遍历完之后才轮到中
    //这也是为什么null不能入栈


    //前序遍历
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右左中节点添加到栈中(前序遍历顺序中左右)
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。

            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
    }

    //中序遍历 左中右
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中(中序遍历顺序左中右)
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)

                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
    }

    //后序遍历 左右中
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将中右左节点添加到栈中(中序遍历顺序左右中)
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值