zoukankan      html  css  js  c++  java
  • 二叉树DFS遍历递归和非递归做法

    二叉树DFS遍历有三种:pre-order, in-order, post-order。遍历的方法有recursion和iteration两种。

    1. pre-order前序遍历

    递归:这三种遍历的递归做法都非常简单。

    public class Solution {
      List<Integer> res = new ArrayList<>();
      public List<Integer> preOrder(TreeNode root) {
        // Write your solution here
        if(root == null) return res;
            res.add(root.key);
        preOrder(root.left);
        preOrder(root.right);
    
        return res;
    }
    }

    非递归:主要是用stack来做。这种更好!

    为什么用非递归更好?

    如果用递归,我们是要用内存中的call-stack来做,它是一个size-limited memory area,如果调用很多很多次,容易stackOverFlow exception

    但是用iteration做,使用数据结构中的stack,是在heap上,这样,在stack上消耗的空间是trivial的,我们不用change the total space consumption,而是把space consumption

    从stack移到了heap上

    思路是:root是stack里的top元素,一旦被遍历过,就『打印』出来,打印之后,要遍历左子树,此时要求右子树留在stack,

    所以,在压入的时候,先右后左

    public class Solution {
      List<Integer> res = new ArrayList<>();
      public List<Integer> preOrder(TreeNode root) {
        // iterative
        if(root == null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        stack.push(root);
        while(!stack.isEmpty()) {
          TreeNode cur = stack.pop(); 
          res.add(cur.key);
          if(cur.right != null) {
            stack.push(cur.right); 
          }
          if(cur.left != null) {
            stack.push(cur.left); 
          }
        }
        return res;
      }
    }

    2. in-order中序遍历:

    递归:

    /**
     * public class TreeNode {
     *   public int key;
     *   public TreeNode left;
     *   public TreeNode right;
     *   public TreeNode(int key) {
     *     this.key = key;
     *   }
     * }
     */
    public class Solution {
      List<Integer> res = new ArrayList<>();
      public List<Integer> inOrder(TreeNode root) {
        // Write your solution here
        if(root == null) return res;
        inOrder(root.left);
        res.add(root.key);
        inOrder(root.right);
        return res;
      }
    }

    非递归:

    和preorder不一样的地方在于,在遍历完左子树之前不能把stack中的root扔掉

    使用helper node来记录visiting node和subtree

    helper != null: 遍历左子树,并且把helper push进stack

    helper == null:说明左子树走完了,这时root是在stack的顶端,打印top,helper = top.right

    循环终点:helper == null && stack is empty

    (当helper == null时, 要先pop出helper,打印他的value,如果它有右子树,还要把它的右子树赋给helper(因为下一轮要打印的是它的右子树,而不是它的parent))

    /**
     * public class TreeNode {
     *   public int key;
     *   public TreeNode left;
     *   public TreeNode right;
     *   public TreeNode(int key) {
     *     this.key = key;
     *   }
     * }
     */
    public class Solution {
      List<Integer> res = new ArrayList<>();
      public List<Integer> inOrder(TreeNode root) {
        //iterative
        if(root == null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode helper;
        helper = root;
        while(helper != null || !stack.isEmpty()) {
          if(helper != null) {
             stack.push(helper);
             helper = helper.left;
          }else {
             helper = stack.pop();
             res.add(helper.key);
             helper = helper.right;
          }
        }
        return res;
      }
    }

    注:之前面试的时候,考了这道题,在我说出iteration解法之后,面试官要求我用空间复杂度为O(1)的非递归情况做。当时卡住了,但是后来做出来了,有空再实现代码。

    3. postorder后序遍历

    递归:

    /**
     * public class TreeNode {
     *   public int key;
     *   public TreeNode left;
     *   public TreeNode right;
     *   public TreeNode(int key) {
     *     this.key = key;
     *   }
     * }
     */
    public class Solution {
      List<Integer> res = new ArrayList<>();
      public List<Integer> postOrder(TreeNode root) {
        // Write your solution here
        if(root == null) return res;
        postOrder(root.left);
        postOrder(root.right);
        res.add(root.key);
        return res;
     }
    }

    非递归:

    第一种方法:

    主要思路是用了另一个stack来辅助

    例如:          5

                  /       

               2           8 

             /

          1     3

    按照Postorder打出来是这样的:1,3,2,8,5(left, right, root)

    把它reverse一下:5,8,2,3,1(root, right, left)

    是不是很像pre-order了呢!

    所以我们可以借鉴pre-order的方式,只不过left和right push进stack的顺序要变一下

    缺点:neet to store everything in memory before we can get the whole post order traversal sequence

    /**
     * public class TreeNode {
     *   public int key;
     *   public TreeNode left;
     *   public TreeNode right;
     *   public TreeNode(int key) {
     *     this.key = key;
     *   }
     * }
     */
    public class Solution {
      List<Integer> res = new ArrayList<>();
      public List<Integer> postOrder(TreeNode root) {
        // Write your solution here
        if(root == null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        Deque<TreeNode> temp = new LinkedList<>();
        temp.push(root);
        while(!temp.isEmpty()) {
           TreeNode cur = temp.pop();
           stack.push(cur);
           if(cur.left != null) {
             temp.push(cur.left); 
           }
           if(cur.right != null) {
             temp.push(cur.right); 
           }
        }
        while(!stack.isEmpty()) {
           res.add(stack.pop().key);
        }
        return res;
      }
    }

    第二种方法:

    这种方法很重要,因为它是最接近recursion在STACK中的运行机制的

    要注意的就是direction!

    设置一个prev指针,来判断我们接下来要向哪里走

    root = stack.top

    如果prev == null, 往下(left优先)

    如果prev是cur的parent,往下(left优先) 此时有个tricky的判断:cur == prev.left || cur == prev.right

    如果prev == cur.left,证明left subtree已经遍历完,我们要往右边走

    如果prev == cur.right,说明right subtree遍历完,往上走(stack.pop)

    /**
     * public class TreeNode {
     *   public int key;
     *   public TreeNode left;
     *   public TreeNode right;
     *   public TreeNode(int key) {
     *     this.key = key;
     *   }
     * }
     */
    public class Solution {
      List<Integer> res = new ArrayList<>();
      public List<Integer> postOrder(TreeNode root) {
        // Write your solution here
        //iterative method 2
        if(root == null) {
          return res;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur;
        TreeNode prev = null;
        stack.push(root);
        while(!stack.isEmpty()) {
          cur = stack.peek();
          if(prev == null || cur == prev.left || cur == prev.right) {//如果prev==null或者cur是prev的孩子
            //go down, left first
            if(cur.left != null) {
              stack.push(cur.left);
            }else if(cur.right != null) {
              stack.push(cur.right);
            }else {//left == null && right == null, 走到底了
              res.add(cur.key);
              stack.pop();
            }
          }else if(prev == cur.left) {//左子树走完了,走右边
            if(cur.right != null) {
              stack.push(cur.right); 
            }else {//如果cur.right == null,直接打印stack顶
               res.add(cur.key);
               stack.pop();
            }
          }else {//prev == cur.right,go up
             res.add(cur.key);
             stack.pop();
          }
          prev = cur;
        }
        return res;
      }
    }
  • 相关阅读:
    转载(腾讯云社区)——详解django-apscheduler的使用方法
    pipenv——python包管理工具
    xx系统需求进度02
    xx系统需求进度01
    Hbase简介
    第七周总结
    《软件需求十步走》阅读笔记一
    第六周总结
    HDFS
    金字塔表达方法
  • 原文地址:https://www.cnblogs.com/x1mercy/p/8680763.html
Copyright © 2011-2022 走看看