二叉树的非递归遍历(前序、中序和后序)

最近有小伙伴面试的时候被问到写循环遍历二叉树,虽然我面了几次还没被问到相关的,但是想着也是该会的东西,就花了点时间写了点代码,废话不多说,上菜。


我的二叉树的声明如下:
这个节点是在offer这个包里面的,所以下面的代码都会 import offer.TreeNode

package offer;

/**
 * @author WangPan 
 * @date 2019/2/25 19:44
 * @description
 **/
public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int x){ val = x;}
}

前序遍历

我一般对三种遍历方式的记法就是看“根”的位置,遍历的前中后其实就是对应的根节点的遍历顺序,前序遍历即:根左右
思路:循环遍历,这里使用一个栈,栈先入后出,因此循环先弹出栈顶节点,再将该栈顶节点的右节点和左节点入栈。代码如下:

package binaryTree;

import java.util.Stack;
import offer.TreeNode;
/**
 * @author WangPan
 * @date 2019/4/20 17:01
 * @description 二叉树的前序遍历,非递归版本
 **/
public class preorder {

    public static void pre(TreeNode root){
        if(root == null)
            return;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode temp = stack.pop();
            System.out.println(temp.val);
            //每出栈一个,依次把右左子点入栈
            if(temp.right != null)
                stack.push(temp.right);
            if(temp.left != null)
                stack.push(temp.left);
        }
    }
}

中序遍历

遍历方式:左根右
思路:仍然使用栈来保存节点,中序遍历要求先访问到最左节点,因此先要将left路径上所有元素依次入栈,再进行出栈操作,如果栈顶元素的右节点不为空,则将其右节点入栈,对其右子树进行中序遍历。

package binaryTree;

import java.util.Stack;
import offer.TreeNode;

/**
 * @author WangPan 
 * @date 2019/4/20 19:08
 * @description 二叉树的中序遍历,非递归版本
 **/
public class inorder {

    public static void in(TreeNode root){
        if(root == null)
            return;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            //首先让这个节点的所有左节点一次入栈
            while (root.left != null){
                stack.push(root.left);
                root = root.left;
            }
            TreeNode temp = stack.pop();
            System.out.println(temp.val);
            //出栈节点的右节点如果不是null则要进栈,对其右子树进行中序遍历
            if(temp.right != null){
                root = temp.right;
                stack.push(root);
            }
        }
    }
}

后序遍历

遍历方式:左右根
思路1:因为仍然要先从左开始遍历,所以大框架根中序遍历较相似,这里使用两个栈来实现,一个主栈保存遍历到的节点,一个辅助栈来保存暂时不输出的“根节点”,一个难点就是输出辅助栈中的根节点时候的判断条件,具体请看代码。该方法将使用两个栈,空间复杂度大一点。

package binaryTree;

import java.util.Stack;
import offer.TreeNode;

/**
 * @author WangPan 
 * @date 2019/4/20 19:33
 * @description 二叉树的后序遍历,利用两个栈,一个主栈一个辅助栈,当两个栈栈顶元素相同时输出,在中序遍历的基础上进行改进
 **/
public class postorder {

    public static void post(TreeNode root){
        if(root == null)
            return;
        Stack<TreeNode> stack = new Stack<>();
        //stack1用来保存那些暂时不输出的根节点
        Stack<TreeNode> stack1 = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            while (root.left != null){
                stack.push(root.left);
                root = root.left;
            }

            TreeNode temp = stack.peek();
            //右节点为null时这个节点可以输出,无论其是左节点还是右节点
            if(temp.right == null){
                System.out.println(stack.pop().val + " ");
                //当temp是右节点时候要把根节点也输出,输出的条件是两个栈的栈顶元素相同
                while(!stack1.isEmpty() && stack.peek() == stack1.peek()){
                    System.out.print(stack1.pop().val + " ");
                    stack.pop();
                }
            } else {
                stack1.push(temp);//根节点要进入stack1
                root = temp.right;
                stack.push(root);
            }
        }
    }

    }
}

思路2:使用一个栈保存节点,当要输出的时候判断其是否满足可输出条件,即为叶子节点或其子节点已输出完毕,使用变量pre来保存前一个输出的节点,具体请看代码。

package binarytree;

import java.util.Stack;
import offer.TreeNode;

/**
 * @author WangPan 
 * @date 2019/4/20 19:33
 * @description 二叉树的后序遍历,非递归版本,利用一个栈,在前序遍历的基础上修改
 **/
public class PostOrder {
    //使用一个栈来访问,思路与前序遍历相似,前序遍历时根节点直接输出,这里先判断是该节点的子节点是否输出过,如果输出过或为叶子节点则可以直接输出,否则将其右左子节点一次入栈
    public static void postOneStack(TreeNode root){
        if(root == null) {
            return;
        }

        Stack<TreeNode> stack = new Stack<>();
        TreeNode pre = null;
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode temp = stack.peek();
            if((temp.left == null && temp.right == null) 
            || (pre != null && (pre == temp.left || pre == temp.right))) {
                System.out.print(stack.pop().val + " ");
                pre = temp;
            }else {
                if(temp.right != null) {
                    stack.push(temp.right);
                }
                if(temp.left != null) {
                    stack.push(temp.left);
                }
            }
        }
    }
}

总结

循环遍历二叉树的方法可能会在面试中问到,当然也是作为一名程序员应该掌握的东西,希望能对大家有点帮助,如有不当之处还请指出,一起学习,一起进步。我的GitHub主页:WangPanHUST

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值