zoukankan      html  css  js  c++  java
  • 145.Binary Tree Postorder Traversal---二叉树后序非递归遍历

    题目链接

    题目大意:后序遍历二叉树。

    法一:普通递归,只是这里需要传入一个list来存储遍历结果。代码如下(耗时1ms):

     1     public List<Integer> postorderTraversal(TreeNode root) {
     2         List<Integer> list = new ArrayList<Integer>();
     3         list = dfs(root, list);
     4         return list;
     5     }
     6     public static List<Integer> dfs(TreeNode root, List<Integer> list) {
     7         if(root == null) {
     8             return list;
     9         }
    10         else {
    11             list = dfs(root.left, list);
    12             list = dfs(root.right, list);
    13             list.add(root.val);
    14             return list;
    15         }
    16     }
    View Code

    法二(借鉴):后序遍历顺序是“左右根”,这里将其反过来遍历,也就是“根右左”,然后将遍历结果反转返回即可。这里用到了LinkedList.addFirst()方法,即将值插到链表头部。(addLast()方法与add()方法一样是插到链表尾部)。这里也可以用ArrayList.add(),在最后再调用Collections.reverse(list)方法即可。此方法代码简单,但不是很好想。代码如下(耗时1ms):

     1     public List<Integer> postorderTraversal(TreeNode root) {
     2         LinkedList<Integer> list = new LinkedList<Integer>();
     3         if(root == null) {
     4             return list;
     5         }
     6         Stack<TreeNode> stack = new Stack<TreeNode>();
     7         stack.push(root);
     8         TreeNode tmp = null;
     9         while(!stack.isEmpty()) {
    10             tmp = stack.pop();
    11             list.addFirst(tmp.val);
    12             if(tmp.left != null) {
    13                 stack.push(tmp.left);
    14             }
    15             if(tmp.right != null) {
    16                 stack.push(tmp.right);
    17             }
    18         }
    19         return list;
    20     }
    View Code

    法三(借鉴):普通后序非递归遍历,这里用一个辅助栈来标记结点是否已经访问右结点,如果已经访问右结点,则将根值加入list中,否则访问右结点压栈。因为有两个栈要压栈出栈,耗时较长。也可以在TreeNode结点中加入一个标记属性flag来标记是否访问过右结点,这样就不需要辅助栈了,时间应该会快一些。代码如下(耗时2ms):

     1     public List<Integer> postorderTraversal(TreeNode root) {
     2         List<Integer> list = new ArrayList<Integer>();
     3         if(root == null) {
     4             return list;
     5         }
     6         Stack<TreeNode> stackNode = new Stack<TreeNode>();
     7         //0表示右结点未访问,1表示右结点已访问
     8         Stack<Integer> stackFlag = new Stack<Integer>();
     9         stackNode.push(root);
    10         stackFlag.push(0);
    11         TreeNode tmp = root.left;//已压栈,则访问其左结点
    12         while(!stackNode.isEmpty()) {
    13             while(tmp != null) {
    14                 stackNode.push(tmp);
    15                 stackFlag.push(0);
    16                 tmp = tmp.left;
    17             }
    18             if(stackFlag.peek() == 0) {
    19                 //右结点未访问,则访问右结点
    20                 tmp = stackNode.peek().right;
    21                 stackFlag.pop();
    22                 stackFlag.push(1);//将访问右结点状态置1
    23             }
    24             else {
    25                 //右结点已访问,则将根结点加入list队列,并将根节点弹出
    26                 list.add(stackNode.pop().val);
    27                 stackFlag.pop();//弹出根节点状态值
    28             }
    29         }
    30         return list;
    31     }
    View Code

    法四(借鉴):保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存 在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。这个比法二还要难理解,特别是要先压right再压left。代码如下(耗时2ms):

     1     public List<Integer> postorderTraversal(TreeNode root) {
     2         List<Integer> list = new ArrayList<Integer>();
     3         if(root == null) {
     4             return list;
     5         }
     6         Stack<TreeNode> stack = new Stack<TreeNode>();
     7         stack.push(root);
     8         TreeNode pre = null, cur = null;
     9         while(!stack.isEmpty()) {
    10             cur = stack.peek();//判断当前结点情况,所以用peek不用pop
    11             if((cur.left == null && cur.right == null) || 
    12                 (pre != null && (pre == cur.left || pre == cur.right))) {
    13                 //如果当前结点没有左右孩子则直接弹出当前结点
    14                 //如果当前结点的左右孩子都已经访问完则弹出当前结点
    15                 list.add(cur.val);
    16                 pre = cur;
    17                 stack.pop();
    18             }
    19             else {
    20                 //注意这里一定要先压right再压left,因为栈的先进后出的原则,到时候会先弹出left再弹出right,这样的顺序才正确。
    21                 if(cur.right != null) {
    22                     stack.push(cur.right);
    23                 }
    24                 if(cur.left != null) {
    25                     stack.push(cur.left);
    26                 }
    27             }
    28         }
    29         return list;
    30     }
    View Code

     法五(借鉴):最接近先序、中序非递归遍历的方法,先压左结点再判断栈顶元素。代码如下(耗时2ms):

     1 public List<Integer> postorderTraversal(TreeNode root) {
     2         List<Integer> list = new ArrayList<Integer>();
     3         if(root == null) {
     4             return list;
     5         }
     6         Stack<TreeNode> stack = new Stack<TreeNode>();
     7         stack.push(root);
     8         TreeNode pre = null, cur = root.left;
     9         while(!stack.isEmpty()) {
    10             while(cur != null) {
    11                 stack.push(cur);
    12                 cur = cur.left;
    13             }
    14             //判断栈顶结点
    15             cur = stack.peek();
    16             //判断是否访问栈顶结点
    17             if(cur.right != null && pre != cur.right) {
    18                 //如果不是从右孩子返回,即还未访问右孩子,则访问
    19                 cur = cur.right;
    20             }
    21             else {
    22                 //如果没有右孩子或右孩子已经访问过,则直接弹出当前节点进行访问
    23                 list.add(cur.val);
    24                 stack.pop();
    25                 //记录当前访问的结点
    26                 pre = cur;
    27                 //将当前结点赋空
    28                 cur = null;
    29             }
    30         }
    31         return list;
    32     }
    View Code
  • 相关阅读:
    为什么 PCB 生产时推荐出 Gerber 给工厂?
    Fedora Redhat Centos 有什么区别和关系?
    【KiCad】 如何给元件给元件的管脚加上划线?
    MCU ADC 进入 PD 模式后出现错误的值?
    FastAdmin 生产环境升级注意
    EMC EMI 自行评估记录
    如何让你的 KiCad 在缩放时不眩晕?
    KiCad 5.1.0 正式版终于发布
    一次单片机 SFR 页引发的“事故”
    java基础之集合
  • 原文地址:https://www.cnblogs.com/cing/p/7802583.html
Copyright © 2011-2022 走看看