二叉树的迭代遍历,递归能做到的,栈也能!附上java和PYTHON实现代码
文章来自代码随机笔记,作者程序员卡尔
二叉树的迭代遍历
读完这篇文章后,你可以用迭代的方法解决以下三个leet问题代码问题: ?那么顺序遍历呢?
我们在栈和队列中提到:匹配问题是栈的强项。 递归的实现是:每次递归调用都会将函数的局部变量、参数值和返回地址压入调用栈。 并且当递归返回时,上一次递归的参数会从栈顶弹出,所以这就是递归可以返回到上一层的原因。
至此大家应该都知道了,我们也可以使用栈来实现二叉树的前后中序遍历。
预购审核(迭代法)
首先我们来看看预购审核。
预订查看位于中间和左侧。每次先处理中间节点时,先将后面的节点放入堆栈,然后将右子节点添加到堆栈中,然后添加左子节点。
为什么要先添加右孩子,再添加左孩子?因为这是出栈时的中、左、右顺序。
动画如下: ![]()
写出下面的代码并不难:(注意代码中的空节点并没有入栈)A这时候你会发现用迭代的方法来写前序遍历看似不难,其实也不难。
此时,您想更改预订评论的顺序并按顺序创建评论吗?
其实,真的不行!
但是当你使用按顺序写遍历的迭代方法时,你会发现套路不一样了。当前前序遍历的逻辑不能直接应用于中序遍历。
中序遍历(迭代法)
为了解释清楚,我先解释一下,现在迭代过程中,我们实际上有两个操作:
- 处理:将元素放入结果数组中
- 访问:节点的遍历
分析一下为什么刚才写的前序遍历代码不能和中序遍历共用,因为前序遍历的顺序是围绕中间的,先访问的元素是中间节点,要处理的元素也在中间节点,所以我现在可以写出一些相当简洁的代码,因为要访问的元素和要处理的元素是相同的顺序,而且都是中间节点。
然后按顺序看攻略。复习的顺序是左、中、右。首先访问二叉树顶部的节点,然后逐层访问,直到到达树左侧的底部,然后开始处理节点(即将节点的值放入到结果数组中),导致处理顺序和访问顺序不一致。
所以使用迭代方式按顺序写遍历时,是用指针遍历来帮助访问节点,而栈则是用来处理节点上的元素。
动画如下: ![]()
中序遍历可以写如下代码:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
};
后序遍历(迭代法)
我们再看一下后序遍历。售前审核是左右,售后审核是左右。 ,那么我们只需要将前序遍历的代码顺序调整为center、right、left的遍历顺序,然后将结果数组反转,输出结果顺序为left、center,如下图:![]()
所以只有邮购-审核需要预购审核的代码可能会略有变化。代码如下:
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
if (node->right) st.push(node->right); // 空节点不入栈
}
reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
return result;
}
};
总结
目前我们采用迭代的方式,按顺序写出二叉树的前向和后向遍历。可以看到,预购和中购是完全不同的。这种编码风格不像递归编写,可以稍微调整代码来实现从前到后的顺序。
这是因为在前序遍历中访问节点(吞吐量节点)和处理节点(向结果数组中插入元素)可以同步处理,但顺序遍历无法同步!
上面这句话可能有同学不明白。我建议你自己用迭代的方法,先写引言,然后尝试看看你能不能写出序言,然后你就能理解了。
那么问题又来了,实现二叉树前后中序遍历的迭代方法是不是可以有统一的风格(即前序遍历可以通过以下方式实现中序和后序)更改代码顺序)?
这样的写法当然不容易理解。我们将在下一篇文章中重点讲解,敬请期待!
其他语言版本
Java:
// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null){
stack.push(node.right);
}
if (node.left != null){
stack.push(node.left);
}
}
return result;
}
}
// 中序遍历顺序: 左-中-右 入栈顺序: 左-右
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()){
if (cur != null){
stack.push(cur);
cur = cur.left;
}else{
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}
}
// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.left != null){
stack.push(node.left);
}
if (node.right != null){
stack.push(node.right);
}
}
Collections.reverse(result);
return result;
}
}
Python:
# 前序遍历-迭代-LC144_二叉树的前序遍历
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
# 根结点为空则返回空列表
if not root:
return []
stack = [root]
result = []
while stack:
node = stack.pop()
# 中结点先处理
result.append(node.val)
# 右孩子先入栈
if node.right:
stack.append(node.right)
# 左孩子后入栈
if node.left:
stack.append(node.left)
return result
# 中序遍历-迭代-LC94_二叉树的中序遍历
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [] # 不能提前将root结点加入stack中
result = []
cur = root
while cur or stack:
# 先迭代访问最底层的左子树结点
if cur:
stack.append(cur)
cur = cur.left
# 到达最左结点后处理栈顶结点
else:
cur = stack.pop()
result.append(cur.val)
# 取栈顶元素右结点
cur = cur.right
return result
# 后序遍历-迭代-LC145_二叉树的后序遍历
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [root]
result = []
while stack:
node = stack.pop()
# 中结点先处理
result.append(node.val)
# 左孩子先入栈
if node.left:
stack.append(node.left)
# 右孩子后入栈
if node.right:
stack.append(node.right)
# 将最终的数组翻转
return result[::-1] 版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网