一个套路,写出来二叉树的迭代遍历

开发 前端
实践过的同学,也会发现使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中的一种遍历方式,其他两种只要稍稍改一下节点顺序就可以了。

[[411706]]

二叉树的统一迭代法

此时我们在二叉树:一入递归深似海,从此offer是路人中用递归的方式,实现了二叉树前中后序的遍历。

在二叉树:听说递归能做的,栈也能做!中用栈实现了二叉树前后中序的迭代遍历(非递归)。

之后我们发现迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。

实践过的同学,也会发现使用迭代法实现先中后序遍历,很难写出统一的代码,不像是递归法,实现了其中的一种遍历方式,其他两种只要稍稍改一下节点顺序就可以了。

其实针对三种遍历方式,使用迭代法是可以写出统一风格的代码!

重头戏来了,接下来介绍一下统一写法。

我们以中序遍历为例,在二叉树:听说递归能做的,栈也能做!中提到说使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况。

那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。

如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。

迭代法中序遍历

中序遍历代码如下:(详细注释)

  1. class Solution { 
  2. public
  3.     vector<int> inorderTraversal(TreeNode* root) { 
  4.         vector<int> result; 
  5.         stack<TreeNode*> st; 
  6.         if (root != NULL) st.push(root); 
  7.         while (!st.empty()) { 
  8.             TreeNode* node = st.top(); 
  9.             if (node != NULL) { 
  10.                 st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 
  11.                 if (node->right) st.push(node->right);  // 添加右节点(空节点不入栈) 
  12.  
  13.                 st.push(node);                          // 添加中节点 
  14.                 st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。 
  15.  
  16.                 if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈) 
  17.             } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 
  18.                 st.pop();           // 将空节点弹出 
  19.                 node = st.top();    // 重新取出栈中元素 
  20.                 st.pop(); 
  21.                 result.push_back(node->val); // 加入到结果集 
  22.             } 
  23.         } 
  24.         return result; 
  25.     } 
  26. }; 

看代码有点抽象我们来看一下动画(中序遍历):

中序遍历迭代(统一写法)

动画中,result数组就是最终结果集。

可以看出我们将访问的节点直接加入到栈中,但如果是处理的节点则后面放入一个空节点, 这样只有空节点弹出的时候,才将下一个节点放进结果集。

此时我们再来看前序遍历代码。

迭代法前序遍历

迭代法前序遍历代码如下:(注意此时我们和中序遍历相比仅仅改变了两行代码的顺序)

  1. class Solution { 
  2. public
  3.     vector<int> preorderTraversal(TreeNode* root) { 
  4.         vector<int> result; 
  5.         stack<TreeNode*> st; 
  6.         if (root != NULL) st.push(root); 
  7.         while (!st.empty()) { 
  8.             TreeNode* node = st.top(); 
  9.             if (node != NULL) { 
  10.                 st.pop(); 
  11.                 if (node->right) st.push(node->right);  // 右 
  12.                 if (node->left) st.push(node->left);    // 左 
  13.                 st.push(node);                          // 中 
  14.                 st.push(NULL); 
  15.             } else { 
  16.                 st.pop(); 
  17.                 node = st.top(); 
  18.                 st.pop(); 
  19.                 result.push_back(node->val); 
  20.             } 
  21.         } 
  22.         return result; 
  23.     } 
  24. }; 

迭代法后序遍历

后续遍历代码如下:(注意此时我们和中序遍历相比仅仅改变了两行代码的顺序)

  1. class Solution { 
  2. public
  3.     vector<int> postorderTraversal(TreeNode* root) { 
  4.         vector<int> result; 
  5.         stack<TreeNode*> st; 
  6.         if (root != NULL) st.push(root); 
  7.         while (!st.empty()) { 
  8.             TreeNode* node = st.top(); 
  9.             if (node != NULL) { 
  10.                 st.pop(); 
  11.                 st.push(node);                          // 中 
  12.                 st.push(NULL); 
  13.  
  14.                 if (node->right) st.push(node->right);  // 右 
  15.                 if (node->left) st.push(node->left);    // 左 
  16.  
  17.             } else { 
  18.                 st.pop(); 
  19.                 node = st.top(); 
  20.                 st.pop(); 
  21.                 result.push_back(node->val); 
  22.             } 
  23.         } 
  24.         return result; 
  25.     } 
  26. }; 

总结

此时我们写出了统一风格的迭代法,不用在纠结于前序写出来了,中序写不出来的情况了。

但是统一风格的迭代法并不好理解,而且想在面试直接写出来还有难度的。

所以大家根据自己的个人喜好,对于二叉树的前中后序遍历,选择一种自己容易理解的递归和迭代法。

其他语言版本

Java:迭代法前序遍历代码如下:

  1. class Solution { 
  2.     public List<Integer> preorderTraversal(TreeNode root) { 
  3.         List<Integer> result = new LinkedList<>(); 
  4.         Stack<TreeNode> st = new Stack<>(); 
  5.         if (root != null) st.push(root); 
  6.         while (!st.empty()) { 
  7.             TreeNode node = st.peek(); 
  8.             if (node != null) { 
  9.                 st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 
  10.                 if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈) 
  11.                 if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈) 
  12.                 st.push(node);                          // 添加中节点 
  13.                 st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 
  14.                  
  15.             } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 
  16.                 st.pop();           // 将空节点弹出 
  17.                 node = st.peek();    // 重新取出栈中元素 
  18.                 st.pop(); 
  19.                 result.add(node.val); // 加入到结果集 
  20.             } 
  21.         } 
  22.         return result; 
  23.     } 

迭代法中序遍历代码如下:

  1. class Solution { 
  2. public List<Integer> inorderTraversal(TreeNode root) { 
  3.         List<Integer> result = new LinkedList<>(); 
  4.     Stack<TreeNode> st = new Stack<>(); 
  5.     if (root != null) st.push(root); 
  6.     while (!st.empty()) { 
  7.         TreeNode node = st.peek(); 
  8.         if (node != null) { 
  9.             st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 
  10.             if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈) 
  11.             st.push(node);                          // 添加中节点 
  12.             st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 
  13.  
  14.             if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈) 
  15.         } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 
  16.             st.pop();           // 将空节点弹出 
  17.             node = st.peek();    // 重新取出栈中元素 
  18.             st.pop(); 
  19.             result.add(node.val); // 加入到结果集 
  20.         } 
  21.     } 
  22.     return result; 

迭代法后序遍历代码如下:

  1. class Solution { 
  2.    public List<Integer> postorderTraversal(TreeNode root) { 
  3.         List<Integer> result = new LinkedList<>(); 
  4.         Stack<TreeNode> st = new Stack<>(); 
  5.         if (root != null) st.push(root); 
  6.         while (!st.empty()) { 
  7.             TreeNode node = st.peek(); 
  8.             if (node != null) { 
  9.                 st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 
  10.                 st.push(node);                          // 添加中节点 
  11.                 st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。 
  12.                 if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈) 
  13.                 if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)          
  14.                                 
  15.             } else { // 只有遇到空节点的时候,才将下一个节点放进结果集 
  16.                 st.pop();           // 将空节点弹出 
  17.                 node = st.peek();    // 重新取出栈中元素 
  18.                 st.pop(); 
  19.                 result.add(node.val); // 加入到结果集 
  20.             } 
  21.         } 
  22.         return result; 
  23.    } 

Python:

迭代法前序遍历:

  1. class Solution: 
  2.     def preorderTraversal(self, root: TreeNode) -> List[int]: 
  3.         result = [] 
  4.         st= [] 
  5.         if root: 
  6.             st.append(root) 
  7.         while st: 
  8.             node = st.pop() 
  9.             if node != None: 
  10.                 if node.right: #右 
  11.                     st.append(node.right
  12.                 if node.left: #左 
  13.                     st.append(node.left
  14.                 st.append(node) #中 
  15.                 st.append(None) 
  16.             else
  17.                 node = st.pop() 
  18.                 result.append(node.val) 
  19.         return result 

迭代法中序遍历:

  1. class Solution: 
  2.     def inorderTraversal(self, root: TreeNode) -> List[int]: 
  3.         result = [] 
  4.         st = [] 
  5.         if root: 
  6.             st.append(root) 
  7.         while st: 
  8.             node = st.pop() 
  9.             if node != None: 
  10.                 if node.right: #添加右节点(空节点不入栈) 
  11.                     st.append(node.right
  12.                  
  13.                 st.append(node) #添加中节点 
  14.                 st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记。 
  15.                  
  16.                 if node.left: #添加左节点(空节点不入栈) 
  17.                     st.append(node.left
  18.             else: #只有遇到空节点的时候,才将下一个节点放进结果集 
  19.                 node = st.pop() #重新取出栈中元素 
  20.                 result.append(node.val) #加入到结果集 
  21.         return result 

迭代法后序遍历:

  1. class Solution: 
  2.     def postorderTraversal(self, root: TreeNode) -> List[int]: 
  3.         result = [] 
  4.         st = [] 
  5.         if root: 
  6.             st.append(root) 
  7.         while st: 
  8.             node = st.pop() 
  9.             if node != None: 
  10.                 st.append(node) #中 
  11.                 st.append(None) 
  12.                  
  13.                 if node.right: #右 
  14.                     st.append(node.right
  15.                 if node.left: #左 
  16.                     st.append(node.left
  17.             else
  18.                 node = st.pop() 
  19.                 result.append(node.val) 
  20.         return result 

旧文链接:二叉树:前中后序迭代方式的写法就不能统一一下么?

 

责任编辑:武晓燕 来源: 代码随想录
相关推荐

2021-07-13 11:32:41

二叉树数据结构算法

2013-07-15 16:35:55

二叉树迭代器

2020-04-27 07:05:58

二叉树左子树右子树

2022-10-26 23:58:02

二叉树数组算法

2021-04-20 08:37:14

数据结构二叉树

2023-05-08 15:57:16

二叉树数据结构

2021-09-15 07:56:32

二叉树层次遍历

2021-02-28 22:00:28

二叉树节点序列

2021-09-13 17:58:11

二叉树层序算法

2021-01-13 10:03:36

二叉树层序遍历层次遍历

2021-05-06 17:46:30

二叉树数据结构

2021-04-19 07:47:42

数据结构二叉树Tree

2024-01-23 12:54:00

C++编程语言代码

2009-08-11 13:29:57

C#二叉树遍历

2021-09-15 07:40:50

二叉树数据结构算法

2021-03-17 08:19:22

二叉树LeetCode

2021-11-29 10:40:58

二叉树镜像节点

2021-04-28 20:12:27

数据结构创建

2022-11-06 19:43:10

二叉树指针节点

2022-07-27 07:45:53

二叉树镜像函数
点赞
收藏

51CTO技术栈公众号