树的遍历 迭代算法——思路:初始化stack,pop stack利用pop的node,push new node to stack,可以考虑迭代一颗树 因为后序遍历最后还要要访问根结点一次,所以要访问根结点两次是难点
144. Binary Tree Preorder Traversal
Given a binary tree, return the preorder traversal of its nodes' values.
For example:
Given binary tree [1,null,2,3]
,
1 \ 2 / 3
return [1,2,3]
.
Note: Recursive solution is trivial, could you do it iteratively?
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution(object): def preorderTraversal(self, root): """ :type root: TreeNode :rtype: List[int] """ ans = [] if root is None: return ans # init stack stack = [root] # pop stack and push new node to it while stack: node = stack.pop() ans.append(node.val) if node.right: stack.append(node.right) if node.left: stack.append(node.left) return ans
94. Binary Tree Inorder Traversal
Given a binary tree, return the inorder traversal of its nodes' values.
For example:
Given binary tree [1,null,2,3]
,
1 \ 2 / 3
return [1,3,2]
.
Note: Recursive solution is trivial, could you do it iteratively?
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution(object): def inorderTraversal(self, root): """ :type root: TreeNode :rtype: List[int] """ ans = [] if not root: return ans # init stack stack = [] node = root while node: stack.append(node) node = node.left # pop node from stack and push node to stack while stack: node = stack.pop() ans.append(node.val) node = node.right while node: stack.append(node) node = node.left return ans
145. Binary Tree Postorder Traversal
Given a binary tree, return the postorder traversal of its nodes' values.
For example:
Given binary tree [1,null,2,3]
,
1 \ 2 / 3
return [3,2,1]
.
Note: Recursive solution is trivial, could you do it iteratively?
迭代的后序遍历是比较难的,但是有简单解法,那就是实际上它做的是反向的先序遍历。亦即遍历的顺序是:节点 -> 右子树 -> 左子树。这生成的是后序遍历的逆序输出。
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution(object): def postorderTraversal(self, root): """ :type root: TreeNode :rtype: List[int] """ if not root: return [] stack = [root] ans = [] while stack: node = stack.pop() ans.append(node.val) if node.left: stack.append(node.left) if node.right: stack.append(node.right) return ans[::-1]
后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。下面介绍两种思路。
第一种思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。
所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是否是第一次出现在栈顶。
void PostOrderDev(TreeNode *root)
{
if(root == NULL)
{
debug <<"The tree is NULL..." <<endl;
}
stack<TreeNode *> nstack;
TreeNode *node = root;
while(node != NULL || nstack.empty( ) != true)
{
// 遍历直至最左节点
while(node != NULL)
{
node->isFirst = 1; // 当前节点首次被访问
nstack.push(node);
node = node->left;
}
if(nstack.empty() != true)
{
node = nstack.top( );
nstack.pop( );
if(node->isFirst == 1) // 第一次出现在栈顶
{
node->isFirst++;
nstack.push(node);
node = node->right;
}
else if(node->isFirst == 2)
{
cout <<node->val;
node = NULL;
}
}
}
}
第二种思路:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
void PostOrderDev(TreeNode *root)
{
if(root == NULL)
{
debug <<"The tree is NULL..." <<endl;
}
stack<TreeNode *> nstack;
TreeNode *cur; //当前结点
TreeNode *pre = NULL; //前一次访问的结点
nstack.push(root);
while(nstack.empty( ) != true)
{
cur = nstack.top( );
if((cur->left == NULL && cur->right == NULL) // 左右还是均为NULL, 可以被输出
|| (pre != NULL && ((pre == cur->left && cur->right == NULL) || pre == cur->right))) // 左右还是被输出了, 递归返回
// 其实当前节点要是想被输出, 要么
// 1--其左右孩子均为NULL
// 2--其左孩子刚被输出,而其右孩子为NULL
// 3--其右孩子刚被输出
//
// 但是这里有一个优化, 入栈时候, 先是根入栈, 然后是右孩子, 然后是左孩子,
// 因此当跟元素位于栈顶的时候, 其左右孩子必然已经弹出,即被输出,
// 也就是说, 当前
{
cout<<cur->val; //如果当前结点没有孩子结点或者孩子节点都已被访问过
nstack.pop( );
pre = cur;
}
else
{
// 由于栈是先进后出,因此先如后孩子, 再左孩子可以保证递归返回时先遍历左孩子
if(cur->right != NULL)
{
nstack.push(cur->right);
}
if(cur->left != NULL)
{
nstack.push(cur->left);
}
}
}
}
其实后序遍历中当前节点要是想被输出, 要么
-
其左右孩子均为NULL
-
其左孩子刚被输出,而其右孩子为NULL
-
其右孩子刚被输出
但是这里有一个优化, 入栈时候, 先是根入栈, 然后是右孩子, 然后是左孩子,
因此当跟元素位于栈顶的时候, 其左右孩子必然已经弹出,即被访问并且输出,
也就是说, 判断当前节点是否需要输出时,只需要之前被输出的节点pre是当前栈定节点cur的孩子就行
即后序遍历中当前栈顶元素要是想被输出
-
其左右孩子均为NULL
-
其孩子(不论左右)刚被输出即可
而且如果刚被输出的节点是其左孩子,那么我们可以确定其有孩子必为NULL,否则它后于父节点入栈,应该在父节点之前被弹出并且输出
因此我们的输出判断可以改为
if((cur->left == NULL && cur->right == NULL) // 左右还是均为NULL, 可以被输出
|| (pre != NULL && ((pre == cur->left /*&& cur->right == NULL*/) || pre == cur->right))) // 其孩子刚被被输出了, 递归返回
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution(object): def postorderTraversal(self, root): """ :type root: TreeNode :rtype: List[int] """ if not root: return [] stack = [root] ans = [] accessed_node = None while stack: node = stack[-1] if (not node.left and not node.right) or (accessed_node and (accessed_node is node.left or accessed_node is node.right)): stack.pop() accessed_node = node ans.append(node.val) else: if node.right: stack.append(node.right) if node.left: stack.append(node.left) return ans