145. 二叉树的后序遍历(非递归实现)
题目描述:
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]
.
Note: Recursive solution is trivial, could you do it iteratively?
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
方法1
思路
对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问, 因为其右孩子还没被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就 保证了正确的访问顺序。可以看出,在这个过程中,当我们第三次访问一个节点时,才能把它弹出栈。因此需要多设置一个变量表示这个节点已经被访问了几次。
重新定义节点结构:
//另外设计一种节点结构加入堆栈,不破坏原来的树结构
/**
* Definition for binary tree
* struct NewTreeNode {
* TreeNode *node;
* int has_visited_cnt;
* NewTreeNode() : node(NULL),has_visited_cnt(0) {}
* };
*/
代码实现
- C++实现:
vector<int> postorderTraversal(TreeNode *root){
vector<int> result;
if(root == nullptr)
return result;
stack<NewTreeNode*> s;
TreeNode *p=root;
while(p!=NULL||!s.empty())
{
while(p!=NULL)
{
NewTreeNode *new_p = (NewTreeNode *)malloc(sizeof(NewTreeNode));
new_p->node = p;
new_p->has_visited_cnt=1;
s.push(new_p);
p=p->left;
}
if(!s.empty())
{
NewTreeNode *temp=s.top();
s.pop();
if(temp->has_visited_cnt==1)
{
temp->has_visited_cnt=2;
s.push(temp);
p=temp->node->right;
}
else
{
result.push_back(temp->node->val);
p=NULL;//此时的当前节点路径仍然需要从备用的栈中获得
}
}
}
return result;
}
- java实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
class NewTreeNode{
public TreeNode node;
public boolean has_handle_right_node;
NewTreeNode(TreeNode node,boolean flag){
this.node = node;
this.has_handle_right_node = flag;
}
}
List<Integer> ret = new ArrayList<>();
Deque<NewTreeNode> stk = new LinkedList<>();
TreeNode p = root;
while(true){
while(p!=null){
NewTreeNode temp = new NewTreeNode(p,false);
stk.push(temp);
p = p.left;
}
if(!stk.isEmpty()){
NewTreeNode t = stk.peek();
if(!t.has_handle_right_node){
t.has_handle_right_node = true;
p = t.node.right;
}else{
ret.add(t.node.val);
stk.pop();
p = null;
}
}else
break;
}
return ret;
}
}
方法2
思路
要保证根结点在左孩子和右孩子访问之后才能访问,因此,
- ↓对于任一结点P,先将其入栈(因为这个节点的孩子节点还没有访问,所以这个节点暂时还不能访问,所以先暂时把他放在一个地方-栈中)。
- ↓如果P不存在左孩子和右孩子,则可以直接访问它(触发访问条件);或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点(另一种触发访问的条件)。
- ↓若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了
- 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
- 右儿子暂时不向下增长,左儿子一侧向下增长并完成访问,然后右儿子一侧向下增长并完成访问,最后访问父节点
- ↓加入堆栈的顺序自然保证了先考察左子节点,再考察右子节点,最后考察根节点,所以不会出现考察当前节点cur时,它本来有两个儿子,但此时pre等于left的情况,因为加入堆栈的顺序,访问栈顶时,只能是left->right->cur的顺序,不可能出现已经访问过left,跳过right,直接访问cur,造成此时pre等于left的情况,它没法跳,这是由加入堆栈的顺序决定的
代码实现
- C++实现:
class Solution {
public:
vector<int> postorderTraversal(TreeNode *root)
{
vector<int> result;
if(root == nullptr)
return result;
//先加入的是上层的父节点,后加入的是下层的子节点,但是访问时是
//下面的子节点访问过了,才能访问上面的父节点,可以利用堆栈数据
//结构辅助解决问题
stack<TreeNode*> s;
TreeNode *cur;//当前结点
TreeNode *pre=NULL;//前一次访问的结点
s.push(root);
while(!s.empty())
{
cur=s.top();
//考察当前节点是否有访问的资格,如果有资格访问的话,以当前
//节点为根的子树就已经全部访问完了,不会再向下增长了,没有
//继续利用的价值了
if((cur->left==NULL && cur->right==NULL) || (pre!=NULL && (pre==cur->left || pre==cur->right)))
{
result.push_back(cur->val);
s.pop();
pre=cur;
}
else//当前节点现在还不能访问,还需要继续暂存堆栈中
{
if(cur->right!=NULL)//当前节点还能向下增长加入子节点
s.push(cur->right);
if(cur->left!=NULL)
s.push(cur->left);
}
}
return result;
}
};
node.h:
using namespace std;
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
post_order.cpp:
#include "node.h"
#include <vector>
#include <stack>
using namespace std;
vector<int> postorderTraversal(TreeNode *root)
{
if (root == nullptr)
return {};
vector<int> ret;
stack<TreeNode *> stk;
stk.push(root);
TreeNode *pre = nullptr;
while (!stk.empty())
{
TreeNode *temp = stk.top();
if ((!temp->left && !temp->right) || (pre != nullptr && (pre == temp->left || pre == temp->right)))
{
ret.push_back(temp->val);
stk.pop();
pre = temp;
}
else
{
if (temp->right != nullptr)
stk.push(temp->right);
if (temp->left != nullptr)
stk.push(temp->left);
}
}
return ret;
}
- java实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
if(root==null)
return ret;
Deque<TreeNode> stk = new LinkedList<>();
stk.push(root);
TreeNode cur = null;
TreeNode pre = null;
while(!stk.isEmpty()){
cur = stk.peek();
if((cur.left==null && cur.right==null) || (pre!=null && (pre==cur.left || pre==cur.right))){
ret.add(cur.val);
stk.pop();
pre = cur;
}else{
if(cur.right!=null)
stk.push(cur.right);
if(cur.left!=null)
stk.push(cur.left);
}
}
return ret;
}
}