• 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
  • 相关阅读:
    WebApi开启CORS支持跨域POST
    VueJs生产环境部署
    VueJs开发笔记—IDE选择和优化、框架特性、数据调用、路由选项及使用
    微信分享JSSDK-invalid signature签名错误的解决方案
    关于后台系统自动生成的一点思考
    编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]
    编写高质量代码改善C#程序的157个建议[10-12]
    编写高质量代码改善C#程序的157个建议[4-9]
    编写高质量代码改善C#程序的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]
    Oracle初级索引学习总结
  • 原文地址:https://www.cnblogs.com/cing/p/7802583.html
Copyright © 2020-2023  润新知