leetcode(145):Binary Tree Postorder Traversal

题目

Binary Tree Postorder Traversal
Given a binary tree, return the postorder traversal of its nodes’ values.

For example:
Given binary tree {1,#,2,3},
1
\
2
/
3
return [3,2,1].

思路

二叉树的后序遍历,递归和非递归两种。

有一个不用栈的非递归的方法,空间复杂度O(1),可参考:《Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)

递归解法

/**
 1. Definition for a binary tree node.
 2. public class TreeNode {
 3.     int val;
 4.     TreeNode left;
 5.     TreeNode right;
 6.     TreeNode(int x) { val = x; }
 7. }
 */
public class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<Integer>();
        if (root != null) {
            list.addAll(postorderTraversal(root.left));
            list.addAll(postorderTraversal(root.right));
            list.add(root.val);
        }
        return list;
    }
}

非递归解法

后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。
参考:《二叉树的非递归遍历

方法一

对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是否是第一次出现在栈顶。

public class Solution {
    private class StackNode {
        TreeNode node;
        boolean isFirst;
    }
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<Integer>();
        Stack<StackNode> stack = new Stack<StackNode>();
        TreeNode node = root;
        StackNode temp;
        while (node != null || !stack.empty()) {
            while (node != null) {//沿左子树一直往下搜索,直至出现没有左子树的结点 
                StackNode snode = new StackNode();
                snode.node = node;
                snode.isFirst = true;
                stack.push(snode);
                node = node.left;
            }
            if (!stack.empty()) {
                temp = stack.pop();
                if (temp.isFirst == true) {//第一次出现在暂定,保留在栈中,访问右孩子
                    temp.isFirst = false;
                    stack.push(temp);
                    node = temp.node.right;
                }
                else {//第二次出现在栈顶,则弹出栈
                    list.add(temp.node.val);
                }
            }
        }
        return list;
    }
}

方法二

第二种思路:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

void postOrder3(BinTree *root)     //非递归后序遍历
{
    stack<BinTree*> s;
    BinTree *cur;                      //当前结点 
    BinTree *pre=NULL;                 //前一次访问的结点 
    s.push(root);
    while(!s.empty())
    {
        cur=s.top();
        if((cur->lchild==NULL&&cur->rchild==NULL)||
           (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
        {
            cout<<cur->data<<" ";  //如果当前结点没有孩子结点或者孩子节点都已被访问过 
              s.pop();
            pre=cur; 
        }
        else
        {
            if(cur->rchild!=NULL)
                s.push(cur->rchild);
            if(cur->lchild!=NULL)    
                s.push(cur->lchild);
        }
    }    
}

方法三

很奇妙的一种方法:改造前序遍历来实现。
pre-order traversal is root-left-right, and post order is left-right-root. modify the code for pre-order to make it root-right-left, and then reverse the output so that we can get left-right-root .

  1. Create an empty stack, Push root node to the stack.
  2. Do following while stack is not empty.
    2.1. pop an item from the stack and print it.
    2.2. push the left child of popped item to stack.
    2.3. push the right child of popped item to stack.
  3. reverse the ouput.

方法四

是对方法三的改进。还是将前序遍历顺序改为root-right-left,但是在往List中保存节点 数据时,不采用list.add()方法(尾插法),而直接采用LinkedList的头插法:list.addFirst(node.val)list.add(0,node.val),这样就避免最后再进行反转

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值