二叉树遍历

零、基础知识:

        ①前序遍历

                前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。

         ②中序遍历

                中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。

         ③后序遍历

                后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。

一、递归法

        ①前序遍历

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();//创建list接受遍历结果
        treeSort(root,res);
        return res;
    }

    public void treeSort(TreeNode root,List<Integer> res){
        if(root==null){ //终止条件
            return;
        }
        res.add(root.val);
        treeSort(root.left,res);//递归,遍历当前结点的左结点的左结点的...
        treeSort(root.right,res);
    }
}

②中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        treeSort(root,list);
        return list;
    }

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

③后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        treeSort(root,list);
        return list;
    }

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

二、迭代法

        利用栈先进后出的特性,记录结点。

①前序遍历

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Deque<TreeNode> stack = new ArrayDeque<>();//栈,用来记录节点

        while(root!=null || !stack.isEmpty()){
            while(root!=null){
                stack.push(root);
                res.add(root.val);
                root = root.left;
            }
            root = stack.pop().right;
        }
        return res;
    }
}

②中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        Deque<TreeNode> deque = new LinkedList<>();
        
        while(root!=null || deque.size()!=0){
            while(root!=null){
                deque.push(root);
                root = root.left;
            }
            root = deque.pop();
            list.add(root.val);
            root = root.right;
        }
        return list;
    } 
}

③后序遍历

        后序遍历比较复杂,左→右→结点,在将中间结点的左结点存入list列表之后,此时需要存入右结点,所以中间结点出栈后不能立即存入list中,需要再次存入栈中。中间结点的右结点存入之后,中间第二次出栈再存入list中。

        方法一:记录已经存入list中的最后结点,如果该结点是当前出栈的root右结点,说明root结点已经是第二次出栈了,它的右子树已经遍历过了,root可以存入list中了。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode last = null;//存储上一个放入list中的节点

        while(root!=null || !stack.isEmpty()){
            while(root!=null){
                stack.push(root);
                root=root.left;
            }
    
            root = stack.pop();
            if(root.right==null || root.right == last){ //比较list中最后存储的结点是不是root的右结点,如果是,说明root已经是第二次出栈,root的右侧子树已经遍历完了
                list.add(root.val);
                last = root;
                root = null;
            }else{
                stack.push(root);//如果root有右节点,把root重新放入栈中
                root = root.right;
            }
            
        }

        return list;
        
    }
}

        方法二:断开结点的右连接,使得下次该结点出栈时没有右侧结点,直接存入list列表中。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> stack = new LinkedList<>();

        while(root!=null || !stack.isEmpty()){
            while(root!=null){
                stack.push(root);
                root=root.left;
            }
            root = stack.pop();
            if(root.right==null){
                list.add(root.val);
                root = null;
            }else{          //如果root.right!=null,表明该节点还有右子树
                TreeNode temp = root;
                root = root.right;
                temp.right = null;//断开右子树,相当于标记已经遍历过的结点,下次该节点出栈时,就不会再遍历右子树
                stack.push(temp);//将该节点再次入栈
            }
        }
        return list;
    }
}

三、Morris 遍历

记作当前节点为root。

  1. 如果root无左子树,root向右移动
  2. 如果root有左子树,找到root左子树上最右的节点,记为temp
    1. 如果temp的right指针指向空,让其指向root,root向左移动;
    2. 如果temp的right指针指向root,让其指向null,root向右移动。

        ①前序

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();

        while(root!=null){
            if(root.left==null){
                list.add(root.val);
                root = root.right;
            }else{
                TreeNode temp = root.left;
                while(temp.right!=null && temp.right!=root){
                    temp=temp.right;
                }
                if(temp.right==null){
                    list.add(root.val);
                    temp.right=root;
                    root=root.left;
                }else{
                    temp.right=null;
                    root=root.right;
                }
            }
        }
        return list;
    }
}

        ②中序

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();

        while(root!=null){
            if(root.left==null){
                list.add(root.val);
                root = root.right;
            }else{
                TreeNode temp = root.left;
                while(temp.right!=null && temp.right!=root){
                    temp=temp.right;
                }
                if(temp.right==null){
                    temp.right=root;
                    root=root.left;
                }else{
                    list.add(root.val);
                    temp.right=null;
                    root=root.right;
                }
            }
        }

        return list;
    }
}

        ③后序

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
     List<Integer> list = new ArrayList<>();
        
        // 新建一个虚拟节点,将root挂在虚拟节点的left上
        // 原因:下面输出节点的代码可以发现,输出左树是正确的,因此把root挂在虚拟节点的left上,转化为要输出的部分全部是左树
        TreeNode virtual = new TreeNode(-1);
        virtual.left = root;
        root = virtual;

        while(root!=null){
            if(root.left==null){
                root = root.right;
            }else{
                TreeNode temp = root.left;
                while(temp.right!=null && temp.right!=root){
                    temp=temp.right;
                }
                if(temp.right==null){
                    temp.right=root;
                    root=root.left;
                }else{
                    temp.right=null;

                    // 左子树输出(逆序输出)
                    List<Integer> tmpList = new ArrayList<>();
                    temp = root.left;
                    while(temp != null){
                        tmpList.add(0, temp.val);
                        temp = temp.right;
                    }
                    list.addAll(tmpList);

                    root=root.right;
                }
            }
        }

        return list;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值