二叉树遍历 迭代法 Morris法

迭代法

前序遍历

比较简单,注意入栈是右子节点先,后左节点,毕竟左结点马上要出栈,结束条件就是栈空

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            res.add(node.val);
            if(node.right != null){
                stack.push(node.right);
            }
            if(node.left != null){
                stack.push(node.left);
            }
        }
        return res;

    }
}

中序遍历

中序和后序要注意节点出栈到底意味着什么,中序意味着其左子树访问完全,后序在中序基础改进,也就需要多一步右子树的判断,如果不符合,还要将其放回栈中。

左子结点不断入栈,出栈的时候说明其左子树已经遍历完了,这个节点可以将其值加入res,后续将指针指向右子节点,继续循环

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        //栈空了也不能结束的,root节点被排出,此时为空,但是右子树还没遍历
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            res.add(cur.val);
            cur = cur.right;
        }
        return res;

    }
}

后序遍历

栈遍历版本: 建议先做中序遍历,后序只是在中序上多了一些操作。

与中序的不同之处在于:
中序遍历中,从栈中弹出的节点,其左子树是访问完了,可以直接访问该节点,然后接下来访问右子树。
后序遍历中,从栈中弹出的节点,我们只能确定其左子树肯定访问完了,但是无法确定右子树是否访问过。

因此,我们在后序遍历中,引入了一个prev来记录历史访问记录。
当访问完一棵子树的时候,我们用prev指向该节点。
这样,在回溯到父节点的时候,我们可以依据prev是指向左子节点,还是右子节点,来判断父节点的访问情况。

class Solution{
    public List<Integer> method1(TreeNode root) {
        List<Integer> ans=new LinkedList<>();
        Stack<TreeNode> stack=new Stack<>();
        TreeNode prev=null;
        //主要思想:
        //由于在某颗子树访问完成以后,接着就要回溯到其父节点去
        //因此可以用prev来记录访问历史,在回溯到父节点时,可以由此来判断,上一个访问的节点是否为右子树
        while(root!=null||!stack.isEmpty()){
            while(root!=null){
                stack.push(root);
                root=root.left;
            }
            //从栈中弹出的元素,左子树一定是访问完了的
            root=stack.pop();
            //现在需要确定的是是否有右子树,或者右子树是否访问过
            //如果没有右子树,或者右子树访问完了,也就是上一个访问的节点是右子节点时
            //说明可以访问当前节点
            if(root.right==null||prev==root.right){
                ans.add(root.val);
                //更新历史访问记录,这样回溯的时候父节点可以由此判断右子树是否访问完成
                prev=root;
                root=null;
            }else{
            //如果右子树没有被访问,那么将当前节点压栈,访问右子树
                stack.push(root);
                root=root.right;
            }
        }
        return ans;
    }
}

自己的版本

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode pre = null;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            if(cur.right == null || pre == cur.right){
                res.add(cur.val);
                //更新记录上一个节点
                pre = cur;
                //否则循环会再次其左子树遍历
                cur = null;
            }else{
                //右子树还没遍历,还不是你这个节点出栈的时机,还要把你放回去
                stack.push(cur);
                cur = cur.right;
            }
        }
        return res;

    }
}

Morris法

代码
DFS迭代或者递归都是用到了栈;BFS是会用到队列。
如果使用M o r r i s 遍历就不需要了, 他的实现过程其实就是把叶子节点的指针给利用起来, 因为一般情况下, 二叉树的叶子节点是没有子节点的, 也就是说他们是指向空, 这样总感觉有点浪费, M o r r i s 的实现原理其实就是把叶子节点的指针给利用了起来。

中序遍历,将左子树的最右节点的右指针指向root。
输出情况分为两种,一种是左孩子为空,另一种是左子树已经遍历完成,此时也就可以遍历右子树了
刚开始,找到左子树的最右节点后将他右指针指向root,后root= root.next,等到左孩子为空的时候,就要输出了,并且指向右孩子,这个右孩子不就是我们刚开始的节点,然后再来看他左子树的最右节点,如果发现已经指向了自己,说明已经遍历过了,将其指针取消,并且输出以及开始他的右子树。

在这里插入图片描述对节点内容的操作,也就是访问右孩子那一步

public static void morrisIn(Node head) {
    if(head == null){
        return;
    }
    Node cur = head;
    Node mostRight = null;
    while (cur != null){
        mostRight = cur.left;
        if(mostRight != null){
            while (mostRight.right !=null && mostRight.right != cur){
                mostRight = mostRight.right;
            }
            if(mostRight.right == null){
                mostRight.right = cur;
                cur = cur.left;
                continue;
            }else {
                mostRight.right = null;
            }
        }
        System.out.print(cur.value+" ");
        cur = cur.right;
    }
    System.out.println();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
叉树代遍主要有三种:前序遍、中序遍和后序遍。下面分别介绍这三种算代实现方: 1. 前序遍代算: 前序遍的顺序是根节点 -> 左子树 -> 右子树。使用代算实现前序遍时,可以通过栈来辅助实现。具体步骤如下: 1)将根节点入栈。 2)当栈不为空时,循环执行以下操作: a) 弹出栈顶节点,并将其值加入结果列表。 b) 若右子节点存在,则将右子节点入栈。 c) 若左子节点存在,则将左子节点入栈。 3)返回结果列表。 2. 中序遍代算: 中序遍的顺序是左子树 -> 根节点 -> 右子树。使用代算实现中序遍时,同样可以通过栈来辅助实现。具体步骤如下: 1)初始化一个空栈,并将当前节点置为根节点。 2)循环执行以下操作,直到栈为空: a) 将当前节点及其所有左子节点依次入栈,直到最左叶子节点。 b) 弹出栈顶节点,并将其值加入结果列表。 c) 若弹出节点存在右子节点,则将右子节点置为当前节点。 3)返回结果列表。 3. 后序遍代算: 后序遍的顺序是左子树 -> 右子树 -> 根节点。使用代算实现后序遍时,需要使用两个栈来辅助实现。具体步骤如下: 1)初始化两个空栈,分别为stack1和stack2。将根节点入栈stack1。 2)当stack1不为空时,循环执行以下操作: a) 弹出stack1的栈顶节点,并将其左子节点和右子节点依次入栈stack1。 b) 将弹出的节点压入stack2。 3)当stack1为空时,将stack2中的节点依次弹出并加入结果列表。 4)返回结果列表。 以上是三种常用的二叉树代算,根据不同的需求可以选择相应的算进行实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值