数据结构--二叉树--前序/中序/后序遍历(递归和非递归)

       前言 

        逆水行舟,不进则退!!!     


       题目

        144. 二叉树的前序遍历 - 力扣(LeetCode)     

        94. 二叉树的中序遍历 - 力扣(LeetCode)

        145. 二叉树的后序遍历 - 力扣(LeetCode)


       递归      

        对二叉树这种结构,遍历的话,递归是最合适的解决方式。

       前序遍历      

        前序遍历递归嘛,很好写,先录入根节点,再递归左子树,然后再递归右子树。没有什么需要注意的。

public void preorderTraversal(TreeNode root, List<Integer> list) {
        
        if(root == null ) {
            return ;
        }else {
            list.add(root.val);
            preorderTraversal(root.left, list);
            preorderTraversal(root.right, list);
        }
        
    }
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        preorderTraversal(root, list);
        return list;
    }

       中序遍历 

        和前序遍历没什么区别,调整一下顺序即可。

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<Integer>();
        if(root == null) {
            return list;
        }
        inorderTraversal1(list, root);
        return list;
    }
    public void inorderTraversal1(List<Integer> list, TreeNode root) {
        if(root == null) {
            return;
        }
        inorderTraversal1(list, root.left);
        
        list.add(root.val);

        inorderTraversal1(list, root.right);
        

    }

       后序遍历

        后序遍历递归也好写,先将左右孩子录入队列,然后再返回将根节点录入队列。

 public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<Integer>();
        if(root == null) {
            return list;
        } 
        
        postorderTraversal1(list, root);
        list.add(root.val);
        return list;
    }
    public void postorderTraversal1(List<Integer> list, TreeNode root) {
        if(root == null) {
            return;
        } 
        postorderTraversal1(list, root.left);
        if(root.left != null) {
            list.add(root.left.val);
        }
        postorderTraversal1(list, root.right);
        
        if(root.right != null) {
            list.add(root.right.val);
        }
        
        

    }

       非递归 

        非递归实现二叉树的前中后序遍历的关键是:用压栈和出栈的方式来控制访问的次序,用循环来遍历每一个结点。

       前序遍历      

        定义一个cur变量来遍历整个二叉树,cur每指向一个结点,都将其压入栈中,并且录入链表。先让cur遍历左子树,当cur == null时,弹出栈顶结点,并将cur指向所弹出结点的右子树,然后循环上述步骤。

public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();     
        if(root == null) {
            return list;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        
        while(cur != null || !stack.isEmpty()) {
            
            while(cur != null) {
                stack.push(cur);     
                list.add(cur.val);   //先入栈,然后录入结点,非递归实现先序遍历的精髓
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;
        }
        return list;
    }

       中序遍历      

        与前序遍历的入栈并录入不同, 中序是出栈便录入到链表当中。先让cur一直向左遍历,每指向一个结点,便压入栈中,cur为空了就弹出栈顶结点并将弹出结点录入到链表中,然后cur指向该弹出结点的右子树,重复上述步骤。

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<Integer>();
        if(root == null) {
            return list;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                //list.add(cur.val);
                cur = cur.left;  //先不停地入栈,直到第一次cur==null
            }
            cur = stack.pop();   //cur==null 就相当于已经遍历了栈顶元素的左子树,该录入根节点了,所以要弹出该结点
            list.add(cur.val);
            cur = cur.right;   //根结点录入了,就该右子树了,然后判断右子树的左子树(不断循环)
        }
        return list;
    }

       后序遍历     

        非递归的后序遍历用代码实现还是有一点难度的。主要是在右子树的处理上,具体原因我已经注释在代码中了。

public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        if(root == null) {
            return list;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        TreeNode top = null;
        while(!stack.isEmpty() || cur != null) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.peek();
            cur = cur.right;
            if(cur == null || cur ==top) {  // 我觉得最难解决的是对右子树的处理
                                            //因为在右子树 X 不为空时,程序会继续判断X的左右子树,
                                            //这时左右都为null,弹出栈顶结点X 并录入,
                                            //然后cur指向了当前的栈顶元素:X的父结点,
                                            // 然后,cur又指向了父结点的右子树X ,陷入了死循环。
                                            //所以需要一个top引用,来记录上一个弹出的结点,
                                            // 如果top记录的结点和cur指向的结点相同,就继续弹出。
                                            //如此 便破解了死循环。
                top = stack.pop();
                list.add(top.val);
                cur = null;
            }
        }
        return list;
    }

        我是专注学习的章鱼哥~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值