二叉树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;
  }
}
posted @ 2018-03-31 07:54  机智的小八  阅读(964)  评论(0编辑  收藏  举报