数据结构:二叉树的前、中、后序遍历(递归和非递归)

public 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;
    }
}

1,先根遍历-递归

【思路先根遍历,即第一次遇到的结点就开始打印。一直遍历左子树,直到为空,然后开始递归右子树,直到为空。

【过程】先将1进入方法,2,3执行方法,2先执行,在2中将4,5执行方法,直到发现4无子树,然后轮到5执行方法。直到发现5没有子树,此时2方法结束,轮到3执行方法。

class Solution {
    List a = new ArrayList();
    public List<Integer> preorderTraversal(TreeNode root) {
        if (root != null) {
            a.add(root.val);
            preorderTraversal(root.left);    //按先根次序遍历p的左子树,递归调用,参数为左孩子
            preorderTraversal(root.right);     //按先根次序遍历p的右子树,递归调用,参数为右孩子
        }
        return a;
    }
}

LeetCode589:给定一个 n 叉树的根节点  root ,返回 其节点值的 前序遍历 。 

class Solution {
    List a = new ArrayList();
    public List<Integer> preorder(Node root) {
        if (root != null) {
            a.add(root.val);
            for (int i=0;i<root.children.size();i++){
                preorder(root.children.get(i));
            }
        }
        return a;
    }
}

2,先根遍历-非递归

【思路】还是先根遍历,遇到结点就压入栈中,每当发现某个结点无子树,将结点设置为从栈中弹出元素。如果不为空就打印。

【过程】先将1压入栈中,然后让结点=1.left,1的左子树=2,发现2不为空压入栈中,继续。。。当发现4无子树时,从栈中弹出2,使得p=2,right=5,此时栈中只有1,等发现5没有子树时,p弹栈=1。

class Solution {
    List a = new ArrayList();
    Stack<TreeNode> stack = new Stack();

    public List<Integer> preorderTraversal(TreeNode root) {
        while (root != null || !stack.isEmpty()) {
            if (root != null) {
                a.add(root.val);
                stack.push(root);
                root = root.left;
            } else {
                root = stack.pop();
                root = root.right;
            }
        }
        return a;
    }
}

3,中根遍历-递归

【思路】开始从跟结点遍历,当第二次遍历到该结点时打印。如何区分是第二次了,在递归中只需要在左右子树中间打印即可。因为但遍历完左子树时,回到父母结点时为第二次遍历到父母结点,第一次为通过父母接到遍历到孩子结点时。

【过程】从1开始进入遍历方法,发现1不为空,将2,3进入遍历方法。由于2先开始,此时3一直在等待2的结束。2发现4.5不是空,将4,5进入遍历方法,此时没有打印2.当4结束时在通过2进入5之前打印此时结点,即为中根遍历。

class Solution {
    List a = new ArrayList();
    public List<Integer> inorderTraversal(TreeNode root) {
        if (root != null) {
            inorderTraversal(root.left);
            a.add(root.val);
            inorderTraversal(root.right);
        }
        return a;
    }
}

4,中根遍历-非递归

【思路】参考先根遍历中的非递归方法。先根遍历是在第一次遇到非空的时候打印。中根稍有区别,在第一次非空时入栈,不打印,当发现子树为空时,弹栈时回到空子树的父母结点时,打印,此时为第二次访问,即中根遍历。

【过程】1开始进入while循环,发现1.left=2不为空入栈,此时不要打印,然后4,进入循环,入栈,发现4左子树为空,此时弹栈,弹出4,立即打印,达到中根遍历。其余相似。

class Solution {
    List a = new ArrayList();
    Stack<TreeNode> stack = new Stack();

    public List<Integer> inorderTraversal(TreeNode root) {
        while (root != null || !stack.isEmpty()) {
            if (root != null) {
                stack.push(root);
                root = root.left;
            } else {
                root = stack.pop();
                a.add(root.val);
                root = root.right;
            }
        }
        return a;
    }
}

5,后根遍历-递归

【思路】现遍历子树,等左右子树都遍历完后,在打印结点。跟之前的类比一下很容易想到。

【过程】1进入递归,发现2,3非空,然后2进入递归,3等待,2又发现4,5非空,此时4进入递归,5等待,然后4发现左右子树都为空,此时不再进行递归,打印4结点的值。

class Solution {
    List a = new ArrayList();
    public List<Integer> postorderTraversal(TreeNode root) {
        if (root != null) {
            postorderTraversal(root.left); 
            postorderTraversal(root.right);
            a.add(root.val);
        }
        return a;
    }
}

6,后根遍历-非递归

【思路】由于需要确定当前结点被访问过两次,此时需要额外一个结点来储存之前访问的结点。符合打印条件的就一共两种情况,一是无子结点,直接打印。二是当前访问的结点其右子树已被访问,或者右子树不存子,左子树被访问过。

!!!一定要注意,先将右子树入栈,再将左子树入栈,因为访问时是现访问左子树,再访问右子树。

class Solution {
    List a = new ArrayList();
    Stack<TreeNode> stack = new Stack<>();
    public List<Integer> postorderTraversal(TreeNode root) {
        if (root==null){
           return new ArrayList<>();
        }
        TreeNode cur, pre = null;       //pre为额外结点,记录前一个结点,用于打印
        stack.push(root);
        while (!stack.isEmpty()) {
            cur = stack.peek();
            if ((cur.left == null && cur.right == null) || (pre != null && (cur.left == pre || cur.right == pre))) {            //判断是否为上述条件决定是否打印
                TreeNode temp = stack.pop();
                a.add(temp.val);
                pre = temp;
            } else {
                if (cur.right != null) {        //右结点入栈
                    stack.push(cur.right);
                }
                if (cur.left != null) {
                    stack.push(cur.left);
                }
            }
        }
        return a;
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燕双嘤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值