* [做项目(多个C++、Java、Go、测开、前端项目)](https://www.programmercarl.com/other/kstar.html) * [刷算法(两个月高强度学算法)](https://www.programmercarl.com/xunlian/xunlianying.html) * [背八股(40天挑战高频面试题)](https://www.programmercarl.com/xunlian/bagu.html) > 统一写法是一种什么感觉 # 二叉树的统一迭代法 ## 思路 此时我们在[二叉树:一入递归深似海,从此offer是路人](https://programmercarl.com/二叉树的递归遍历.html)中用递归的方式,实现了二叉树前中后序的遍历。 在[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)中用栈实现了二叉树前后中序的迭代遍历(非递归)。 之后我们发现**迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。** 实践过的同学,也会发现使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中的一种遍历方式,其他两种只要稍稍改一下节点顺序就可以了。 其实**针对三种遍历方式,使用迭代法是可以写出统一风格的代码!** **重头戏来了,接下来介绍一下统一写法。** 我们以中序遍历为例,在[二叉树:听说递归能做的,栈也能做!](https://programmercarl.com/二叉树的迭代遍历.html)中提到说使用栈的话,**无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况**。 **那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。** 如何标记呢? * 方法一:**就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。** 这种方法可以叫做`空指针标记法`。 * 方法二:**加一个 `boolean` 值跟随每个节点,`false` (默认值) 表示需要为该节点和它的左右儿子安排在栈中的位次,`true` 表示该节点的位次之前已经安排过了,可以收割节点了。** 这种方法可以叫做`boolean 标记法`,样例代码见下文`C++ 和 Python 的 boolean 标记法`。 这种方法更容易理解,在面试中更容易写出来。 ### 迭代法中序遍历 > 中序遍历(空指针标记法)代码如下:(详细注释) ```CPP class Solution { public: vector inorderTraversal(TreeNode* root) { vector result; stack st; if (root != NULL) st.push(root); while (!st.empty()) { TreeNode* node = st.top(); if (node != NULL) { st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 if (node->right) st.push(node->right); // 添加右节点(空节点不入栈) st.push(node); // 添加中节点 st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。 if (node->left) st.push(node->left); // 添加左节点(空节点不入栈) } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 st.pop(); // 将空节点弹出 node = st.top(); // 重新取出栈中元素 st.pop(); result.push_back(node->val); // 加入到结果集 } } return result; } }; ``` 看代码有点抽象我们来看一下动画(中序遍历): ![中序遍历迭代(统一写法)](https://file1.kamacoder.com/i/algo/%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E8%BF%AD%E4%BB%A3%EF%BC%88%E7%BB%9F%E4%B8%80%E5%86%99%E6%B3%95%EF%BC%89.gif) 动画中,result数组就是最终结果集。 可以看出我们将访问的节点直接加入到栈中,但如果是处理的节点则后面放入一个空节点, 这样只有空节点弹出的时候,才将下一个节点放进结果集。 > 中序遍历(boolean 标记法): ```c++ class Solution { public: vector inorderTraversal(TreeNode* root) { vector result; stack> st; if (root != nullptr) st.push(make_pair(root, false)); // 多加一个参数,false 为默认值,含义见下文注释 while (!st.empty()) { auto node = st.top().first; auto visited = st.top().second; //多加一个 visited 参数,使“迭代统一写法”成为一件简单的事 st.pop(); if (visited) { // visited 为 True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了 result.push_back(node->val); continue; } // visited 当前为 false, 表示初次访问本节点,此次访问的目的是“把自己和两个儿子在栈中安排好位次”。 // 中序遍历是'左中右',右儿子最先入栈,最后出栈。 if (node->right) st.push(make_pair(node->right, false)); // 把自己加回到栈中,位置居中。 // 同时,设置 visited 为 true,表示下次再访问本节点时,允许收割。 st.push(make_pair(node, true)); if (node->left) st.push(make_pair(node->left, false)); // 左儿子最后入栈,最先出栈 } return result; } }; ``` 此时我们再来看前序遍历代码。 ### 迭代法前序遍历 迭代法前序遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**) ```CPP class Solution { public: vector preorderTraversal(TreeNode* root) { vector result; stack st; if (root != NULL) st.push(root); while (!st.empty()) { TreeNode* node = st.top(); if (node != NULL) { st.pop(); if (node->right) st.push(node->right); // 右 if (node->left) st.push(node->left); // 左 st.push(node); // 中 st.push(NULL); } else { st.pop(); node = st.top(); st.pop(); result.push_back(node->val); } } return result; } }; ``` ### 迭代法后序遍历 > 后续遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**) ```CPP class Solution { public: vector postorderTraversal(TreeNode* root) { vector result; stack st; if (root != NULL) st.push(root); while (!st.empty()) { TreeNode* node = st.top(); if (node != NULL) { st.pop(); st.push(node); // 中 st.push(NULL); if (node->right) st.push(node->right); // 右 if (node->left) st.push(node->left); // 左 } else { st.pop(); node = st.top(); st.pop(); result.push_back(node->val); } } return result; } }; ``` > 迭代法后序遍历(boolean 标记法): ```c++ class Solution { public: vector postorderTraversal(TreeNode* root) { vector result; stack> st; if (root != nullptr) st.push(make_pair(root, false)); // 多加一个参数,false 为默认值,含义见下文 while (!st.empty()) { auto node = st.top().first; auto visited = st.top().second; //多加一个 visited 参数,使“迭代统一写法”成为一件简单的事 st.pop(); if (visited) { // visited 为 True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了 result.push_back(node->val); continue; } // visited 当前为 false, 表示初次访问本节点,此次访问的目的是“把自己和两个儿子在栈中安排好位次”。 // 后序遍历是'左右中',节点自己最先入栈,最后出栈。 // 同时,设置 visited 为 true,表示下次再访问本节点时,允许收割。 st.push(make_pair(node, true)); if (node->right) st.push(make_pair(node->right, false)); // 右儿子位置居中 if (node->left) st.push(make_pair(node->left, false)); // 左儿子最后入栈,最先出栈 } return result; } }; ``` ## 总结 此时我们写出了统一风格的迭代法,不用在纠结于前序写出来了,中序写不出来的情况了。 但是统一风格的迭代法并不好理解,而且想在面试直接写出来还有难度的。 所以大家根据自己的个人喜好,对于二叉树的前中后序遍历,选择一种自己容易理解的递归和迭代法。 ## 其他语言版本 ### Java: 迭代法前序遍历(空指针标记法)代码如下: ```java class Solution { public List preorderTraversal(TreeNode root) { List result = new LinkedList<>(); Stack st = new Stack<>(); if (root != null) st.push(root); while (!st.empty()) { TreeNode node = st.peek(); if (node != null) { st.pop(); // 将该节点弹出,避免重复操作,下面再将右左中节点添加到栈中(前序遍历-中左右,入栈顺序右左中) if (node.right != null) st.push(node.right); // 添加右节点(空节点不入栈) if (node.left != null) st.push(node.left); // 添加左节点(空节点不入栈) st.push(node); // 添加中节点 st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 st.pop(); // 将空节点弹出 node = st.peek(); // 重新取出栈中元素 st.pop(); result.add(node.val); // 加入到结果集 } } return result; } } ``` 迭代法中序遍历(空指针标记法)代码如下: ```java class Solution { public List inorderTraversal(TreeNode root) { List result = new LinkedList<>(); Stack st = new Stack<>(); if (root != null) st.push(root); while (!st.empty()) { TreeNode node = st.peek(); if (node != null) { st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中(中序遍历-左中右,入栈顺序右中左) if (node.right != null) st.push(node.right); // 添加右节点(空节点不入栈) st.push(node); // 添加中节点 st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 if (node.left != null) st.push(node.left); // 添加左节点(空节点不入栈) } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 st.pop(); // 将空节点弹出 node = st.peek(); // 重新取出栈中元素 st.pop(); result.add(node.val); // 加入到结果集 } } return result; } } ``` 迭代法后序遍历(空指针标记法)代码如下: ```java class Solution { public List postorderTraversal(TreeNode root) { List result = new LinkedList<>(); Stack st = new Stack<>(); if (root != null) st.push(root); while (!st.empty()) { TreeNode node = st.peek(); if (node != null) { st.pop(); // 将该节点弹出,避免重复操作,下面再将中右左节点添加到栈中(后序遍历-左右中,入栈顺序中右左) st.push(node); // 添加中节点 st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈) if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈) } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 st.pop(); // 将空节点弹出 node = st.peek(); // 重新取出栈中元素 st.pop(); result.add(node.val); // 加入到结果集 } } return result; } } ``` 迭代法前序遍历(boolean 标记法)代码如下: ```java // LeetCode提交时注意添加导包语句 import java.util.AbstractMap.SimpleEntry; class Solution { public List preorderTraversal(TreeNode root) { List result = new ArrayList<>(); if (root == null) return result; Stack> stack = new Stack<>(); stack.push(new SimpleEntry<>(root, false)); while (!stack.isEmpty()) { TreeNode node = stack.peek().getKey(); // 多加一个visited参数,使“迭代统一写法”成为一件简单的事 boolean visited = stack.peek().getValue(); stack.pop(); // visited为True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了 if (visited) { result.add(node.val); continue; } // visited当前为false, 表示初次访问本节点,此次访问的目的是“把自己和两个儿子在栈中安排好位次” // 前序遍历是'中左右',右儿子最先入栈,最后出栈 if (node.right != null) { stack.push(new SimpleEntry<>(node.right, false)); } // 左儿子位置居中 if (node.left != null) { stack.push(new SimpleEntry<>(node.left, false)); } // 节点自己最后入栈,最先出栈 // 同时,设置 visited 为 true,表示下次再访问本节点时,允许收割 stack.push(new SimpleEntry<>(node, true)); } return result; } } ``` 迭代法中序遍历(boolean 标记法)代码如下: ```java // LeetCode提交时注意添加导包语句 import java.util.AbstractMap.SimpleEntry; class Solution { public List inorderTraversal(TreeNode root) { List result = new ArrayList<>(); if (root == null) return result; Stack> stack = new Stack<>(); stack.push(new SimpleEntry<>(root, false)); while (!stack.isEmpty()) { TreeNode node = stack.peek().getKey(); // 多加一个visited参数,使“迭代统一写法”成为一件简单的事 boolean visited = stack.peek().getValue(); stack.pop(); // visited为True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了 if (visited) { result.add(node.val); continue; } // visited当前为false, 表示初次访问本节点,此次访问的目的是“把自己和两个儿子在栈中安排好位次” // 中序遍历是'左中右',右儿子最先入栈,最后出栈 if (node.right != null) { stack.push(new SimpleEntry<>(node.right, false)); } // 把自己加回到栈中,位置居中 // 同时,设置 visited 为 true,表示下次再访问本节点时,允许收割 stack.push(new SimpleEntry<>(node, true)); // 左儿子最后入栈,最先出栈 if (node.left != null) { stack.push(new SimpleEntry<>(node.left, false)); } } return result; } } ``` 迭代法后序遍历(boolean 标记法)代码如下: ```java // LeetCode提交时注意添加导包语句 import java.util.AbstractMap.SimpleEntry; class Solution { public List postorderTraversal(TreeNode root) { List result = new ArrayList<>(); if (root == null) return result; Stack> stack = new Stack<>(); stack.push(new SimpleEntry<>(root, false)); while (!stack.isEmpty()) { TreeNode node = stack.peek().getKey(); // 多加一个visited参数,使“迭代统一写法”成为一件简单的事 boolean visited = stack.peek().getValue(); stack.pop(); // visited为True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了 if (visited) { result.add(node.val); continue; } // visited当前为false, 表示初次访问本节点,此次访问的目的是“把自己和两个儿子在栈中安排好位次” // 后序遍历是'左右中',节点自己最先入栈,最后出栈 // 同时,设置 visited 为 true,表示下次再访问本节点时,允许收割 stack.push(new SimpleEntry<>(node, true)); // 右儿子位置居中 if (node.right != null) { stack.push(new SimpleEntry<>(node.right, false)); } // 左儿子最后入栈,最先出栈 if (node.left != null) { stack.push(new SimpleEntry<>(node.left, false)); } } return result; } } ``` ### Python: > 迭代法前序遍历(空指针标记法): ```python class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: result = [] st= [] if root: st.append(root) while st: node = st.pop() if node != None: if node.right: #右 st.append(node.right) if node.left: #左 st.append(node.left) st.append(node) #中 st.append(None) else: node = st.pop() result.append(node.val) return result ``` > 迭代法中序遍历(空指针标记法): ```python class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: result = [] st = [] if root: st.append(root) while st: node = st.pop() if node != None: if node.right: #添加右节点(空节点不入栈) st.append(node.right) st.append(node) #添加中节点 st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记。 if node.left: #添加左节点(空节点不入栈) st.append(node.left) else: #只有遇到空节点的时候,才将下一个节点放进结果集 node = st.pop() #重新取出栈中元素 result.append(node.val) #加入到结果集 return result ``` > 迭代法后序遍历(空指针标记法): ```python class Solution: def postorderTraversal(self, root: TreeNode) -> List[int]: result = [] st = [] if root: st.append(root) while st: node = st.pop() if node != None: st.append(node) #中 st.append(None) if node.right: #右 st.append(node.right) if node.left: #左 st.append(node.left) else: node = st.pop() result.append(node.val) return result ``` > 中序遍历,统一迭代(boolean 标记法): ```python class Solution: def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: values = [] stack = [(root, False)] if root else [] # 多加一个参数,False 为默认值,含义见下文 while stack: node, visited = stack.pop() # 多加一个 visited 参数,使“迭代统一写法”成为一件简单的事 if visited: # visited 为 True,表示该节点和两个儿子的位次之前已经安排过了,现在可以收割节点了 values.append(node.val) continue # visited 当前为 False, 表示初次访问本节点,此次访问的目的是“把自己和两个儿子在栈中安排好位次”。 # 中序遍历是'左中右',右儿子最先入栈,最后出栈。 if node.right: stack.append((node.right, False)) stack.append((node, True)) # 把自己加回到栈中,位置居中。同时,设置 visited 为 True,表示下次再访问本节点时,允许收割 if node.left: stack.append((node.left, False)) # 左儿子最后入栈,最先出栈 return values ``` > 后序遍历,统一迭代(boolean 标记法): ```python class Solution: def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: values = [] stack = [(root, False)] if root else [] # 多加一个参数,False 为默认值,含义见下文 while stack: node, visited = stack.pop() # 多加一个 visited 参数,使“迭代统一写法”成为一件简单的事 if visited: # visited 为 True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了 values.append(node.val) continue # visited 当前为 False, 表示初次访问本节点,此次访问的目的是“把自己和两个儿子在栈中安排好位次” # 后序遍历是'左右中',节点自己最先入栈,最后出栈。 # 同时,设置 visited 为 True,表示下次再访问本节点时,允许收割。 stack.append((node, True)) if node.right: stack.append((node.right, False)) # 右儿子位置居中 if node.left: stack.append((node.left, False)) # 左儿子最后入栈,最先出栈 return values ``` ### Go: > 前序遍历统一迭代法 ```GO /** type Element struct { // 元素保管的值 Value interface{} // 内含隐藏或非导出字段 } func (l *List) Back() *Element 前序遍历:中左右 压栈顺序:右左中 **/ func preorderTraversal(root *TreeNode) []int { if root == nil { return nil } var stack = list.New()//栈 res:=[]int{}//结果集 stack.PushBack(root) var node *TreeNode for stack.Len()>0{ e := stack.Back() stack.Remove(e)//弹出元素 if e.Value==nil{// 如果为空,则表明是需要处理中间节点 e=stack.Back()//弹出元素(即中间节点) stack.Remove(e)//删除中间节点 node=e.Value.(*TreeNode) res=append(res,node.Val)//将中间节点加入到结果集中 continue//继续弹出栈中下一个节点 } node = e.Value.(*TreeNode) //压栈顺序:右左中 if node.Right!=nil{ stack.PushBack(node.Right) } if node.Left!=nil{ stack.PushBack(node.Left) } stack.PushBack(node)//中间节点压栈后再压入nil作为中间节点的标志符 stack.PushBack(nil) } return res } ``` > 中序遍历统一迭代法 ```go /** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */ //中序遍历:左中右 //压栈顺序:右中左 func inorderTraversal(root *TreeNode) []int { if root==nil{ return nil } stack:=list.New()//栈 res:=[]int{}//结果集 stack.PushBack(root) var node *TreeNode for stack.Len()>0{ e := stack.Back() stack.Remove(e) if e.Value==nil{// 如果为空,则表明是需要处理中间节点 e=stack.Back()//弹出元素(即中间节点) stack.Remove(e)//删除中间节点 node=e.Value.(*TreeNode) res=append(res,node.Val)//将中间节点加入到结果集中 continue//继续弹出栈中下一个节点 } node = e.Value.(*TreeNode) //压栈顺序:右中左 if node.Right!=nil{ stack.PushBack(node.Right) } stack.PushBack(node)//中间节点压栈后再压入nil作为中间节点的标志符 stack.PushBack(nil) if node.Left!=nil{ stack.PushBack(node.Left) } } return res } ``` > 后序遍历统一迭代法 ```go //后续遍历:左右中 //压栈顺序:中右左 func postorderTraversal(root *TreeNode) []int { if root == nil { return nil } var stack = list.New()//栈 res:=[]int{}//结果集 stack.PushBack(root) var node *TreeNode for stack.Len()>0{ e := stack.Back() stack.Remove(e) if e.Value==nil{// 如果为空,则表明是需要处理中间节点 e=stack.Back()//弹出元素(即中间节点) stack.Remove(e)//删除中间节点 node=e.Value.(*TreeNode) res=append(res,node.Val)//将中间节点加入到结果集中 continue//继续弹出栈中下一个节点 } node = e.Value.(*TreeNode) //压栈顺序:中右左 stack.PushBack(node)//中间节点压栈后再压入nil作为中间节点的标志符 stack.PushBack(nil) if node.Right!=nil{ stack.PushBack(node.Right) } if node.Left!=nil{ stack.PushBack(node.Left) } } return res } ``` ### JavaScript: > 前序遍历统一迭代法 ```js // 前序遍历:中左右 // 压栈顺序:右左中 var preorderTraversal = function(root, res = []) { const stack = []; if (root) stack.push(root); while(stack.length) { const node = stack.pop(); if(!node) { res.push(stack.pop().val); continue; } if (node.right) stack.push(node.right); // 右 if (node.left) stack.push(node.left); // 左 stack.push(node); // 中 stack.push(null); }; return res; }; ``` > 中序遍历统一迭代法 ```js // 中序遍历:左中右 // 压栈顺序:右中左 var inorderTraversal = function(root, res = []) { const stack = []; if (root) stack.push(root); while(stack.length) { const node = stack.pop(); if(!node) { res.push(stack.pop().val); continue; } if (node.right) stack.push(node.right); // 右 stack.push(node); // 中 stack.push(null); if (node.left) stack.push(node.left); // 左 }; return res; }; ``` > 后序遍历统一迭代法 ```js // 后续遍历:左右中 // 压栈顺序:中右左 var postorderTraversal = function(root, res = []) { const stack = []; if (root) stack.push(root); while(stack.length) { const node = stack.pop(); if(!node) { res.push(stack.pop().val); continue; } stack.push(node); // 中 stack.push(null); if (node.right) stack.push(node.right); // 右 if (node.left) stack.push(node.left); // 左 }; return res; }; ``` ### TypeScript: ```typescript // 前序遍历(迭代法) function preorderTraversal(root: TreeNode | null): number[] { let helperStack: (TreeNode | null)[] = []; let res: number[] = []; let curNode: TreeNode | null; if (root === null) return res; helperStack.push(root); while (helperStack.length > 0) { curNode = helperStack.pop()!; if (curNode !== null) { if (curNode.right !== null) helperStack.push(curNode.right); if (curNode.left !== null) helperStack.push(curNode.left); helperStack.push(curNode); helperStack.push(null); } else { curNode = helperStack.pop()!; res.push(curNode.val); } } return res; }; // 中序遍历(迭代法) function inorderTraversal(root: TreeNode | null): number[] { let helperStack: (TreeNode | null)[] = []; let res: number[] = []; let curNode: TreeNode | null; if (root === null) return res; helperStack.push(root); while (helperStack.length > 0) { curNode = helperStack.pop()!; if (curNode !== null) { if (curNode.right !== null) helperStack.push(curNode.right); helperStack.push(curNode); helperStack.push(null); if (curNode.left !== null) helperStack.push(curNode.left); } else { curNode = helperStack.pop()!; res.push(curNode.val); } } return res; }; // 后序遍历(迭代法) function postorderTraversal(root: TreeNode | null): number[] { let helperStack: (TreeNode | null)[] = []; let res: number[] = []; let curNode: TreeNode | null; if (root === null) return res; helperStack.push(root); while (helperStack.length > 0) { curNode = helperStack.pop()!; if (curNode !== null) { helperStack.push(curNode); helperStack.push(null); if (curNode.right !== null) helperStack.push(curNode.right); if (curNode.left !== null) helperStack.push(curNode.left); } else { curNode = helperStack.pop()!; res.push(curNode.val); } } return res; }; ``` ### Scala: ```scala // 前序遍历 object Solution { import scala.collection.mutable def preorderTraversal(root: TreeNode): List[Int] = { val res = mutable.ListBuffer[Int]() val stack = mutable.Stack[TreeNode]() if (root != null) stack.push(root) while (!stack.isEmpty) { var curNode = stack.top if (curNode != null) { stack.pop() if (curNode.right != null) stack.push(curNode.right) if (curNode.left != null) stack.push(curNode.left) stack.push(curNode) stack.push(null) } else { stack.pop() res.append(stack.pop().value) } } res.toList } } // 中序遍历 object Solution { import scala.collection.mutable def inorderTraversal(root: TreeNode): List[Int] = { val res = mutable.ListBuffer[Int]() val stack = mutable.Stack[TreeNode]() if (root != null) stack.push(root) while (!stack.isEmpty) { var curNode = stack.top if (curNode != null) { stack.pop() if (curNode.right != null) stack.push(curNode.right) stack.push(curNode) stack.push(null) if (curNode.left != null) stack.push(curNode.left) } else { // 等于空的时候好办,弹出这个元素 stack.pop() res.append(stack.pop().value) } } res.toList } } // 后序遍历 object Solution { import scala.collection.mutable def postorderTraversal(root: TreeNode): List[Int] = { val res = mutable.ListBuffer[Int]() val stack = mutable.Stack[TreeNode]() if (root != null) stack.push(root) while (!stack.isEmpty) { var curNode = stack.top if (curNode != null) { stack.pop() stack.push(curNode) stack.push(null) if (curNode.right != null) stack.push(curNode.right) if (curNode.left != null) stack.push(curNode.left) } else { stack.pop() res.append(stack.pop().value) } } res.toList } } ``` ### Rust: ```rust impl Solution{ // 前序 pub fn preorder_traversal(root: Option>>) -> Vec { let mut res = vec![]; let mut stack = vec![]; if root.is_some(){ stack.push(root); } while !stack.is_empty(){ if let Some(node) = stack.pop().unwrap(){ if node.borrow().right.is_some(){ stack.push(node.borrow().right.clone()); } if node.borrow().left.is_some(){ stack.push(node.borrow().left.clone()); } stack.push(Some(node)); stack.push(None); }else{ res.push(stack.pop().unwrap().unwrap().borrow().val); } } res } // 中序 pub fn inorder_traversal(root: Option>>) -> Vec { let mut res = vec![]; let mut stack = vec![]; if root.is_some() { stack.push(root); } while !stack.is_empty() { if let Some(node) = stack.pop().unwrap() { if node.borrow().right.is_some() { stack.push(node.borrow().right.clone()); } stack.push(Some(node.clone())); stack.push(None); if node.borrow().left.is_some() { stack.push(node.borrow().left.clone()); } } else { res.push(stack.pop().unwrap().unwrap().borrow().val); } } res } // 后序 pub fn postorder_traversal(root: Option>>) -> Vec { let mut res = vec![]; let mut stack = vec![]; if root.is_some() { stack.push(root); } while !stack.is_empty() { if let Some(node) = stack.pop().unwrap() { stack.push(Some(node.clone())); stack.push(None); if node.borrow().right.is_some() { stack.push(node.borrow().right.clone()); } if node.borrow().left.is_some() { stack.push(node.borrow().left.clone()); } } else { res.push(stack.pop().unwrap().unwrap().borrow().val); } } res } } ``` ### C# ```csharp // 前序遍历 public IList PreorderTraversal(TreeNode root) { var res = new List(); var st = new Stack(); if (root == null) return res; st.Push(root); while (st.Count != 0) { var node = st.Peek(); if (node == null) { st.Pop(); node = st.Peek(); st.Pop(); res.Add(node.val); } else { st.Pop(); if (node.right != null) st.Push(node.right); if (node.left != null) st.Push(node.left); st.Push(node); st.Push(null); } } return res; } ``` ```csharp // 中序遍历 public IList InorderTraversal(TreeNode root) { var res = new List(); var st = new Stack(); if (root == null) return res; st.Push(root); while (st.Count != 0) { var node = st.Peek(); if (node == null) { st.Pop(); node = st.Peek(); st.Pop(); res.Add(node.val); } else { st.Pop(); if (node.right != null) st.Push(node.right); st.Push(node); st.Push(null); if (node.left != null) st.Push(node.left); } } return res; } ``` ```csharp // 后序遍历 public IList PostorderTraversal(TreeNode root) { var res = new List(); var st = new Stack(); if (root == null) return res; st.Push(root); while (st.Count != 0) { var node = st.Peek(); if (node == null) { st.Pop(); node = st.Peek(); st.Pop(); res.Add(node.val); } else { st.Pop(); if (node.left != null) st.Push(node.left); if (node.right != null) st.Push(node.right); st.Push(node); st.Push(null); } } res.Reverse(0, res.Count); return res; } ```