迭代实现二叉树的遍历-算法通关村

迭代实现二叉树的遍历-算法通关村


  • 理论上,递归能做的迭代一定能做,但可能会比较复杂。有时候面试官要求不使用递归实现三种遍历,递归就是每次执行方法调用都会先把当前的局部变量、参数值和返回地址等压入栈中,后面在递归返回的时候,从栈顶弹出上一层的各项参数继续执行,这就是递归为什么可以自动返回并执行上一层方法的原因。

1 迭代法实现前序遍历

  • 前序遍历是中左右,如果还有左子树就一直向下找。完了之后再返回从最底层逐步向上向右找。

    不难写出如下代码:(注意代码中,空节点不入栈)

java 复制代码
  public List<Integer> preOrderTraversal(TreeNode root){
          List<Integer> res = new ArrayList<>();//存放遍历的结果
          if(root == null){
              return res;
          }
          Deque<TreeNode> stack = new LinkedList<>();
          TreeNode node = root;
          while(!stack.isEmpty() || node != null){
              while(node != null){//空节点不入栈
                  res.add(node.val);
                  stack.push(node);
                  node = node.left;
              }
              //当当前节点的所有左子节点都已遍历后,
              // 将栈顶元素出栈,并将node更新为该节点的右子节点。
              // 然后继续执行内部循环,遍历右子树。
              node = stack.pop();
              node = node.right;
          }
          return res;
      }

2 迭代法实现中序遍历

  • 中序遍历是左中右,先访问的是二叉树左子树的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进res列表中)。在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。看代码:

java 复制代码
  public List<Integer> inOrderTraversal(TreeNode root){
          List<Integer> res = new ArrayList<>();
          Deque<TreeNode> stack = new LinkedList<>();
          while(!stack.isEmpty() || root != null){
              while(root != null){
                  //将当前节点入栈,并将root更新为其左子节点。
                  // 这个循环会一直执行,直到当前节点为空,
                  // 即遍历完当前节点的所有左子节点。
                  stack.push(root);
                  root = root.left;
              }
              root = stack.pop();
              res.add(root.val);
              //将root更新为该节点的右子节点,以便后续遍历右子树
              root = root.right;
          }
          return res;
      }

3 迭代法实现后序遍历

  • 后序遍历的非递归实现有三种基本的思路:反转法、访问标记法、和Morris法 ,可惜三种理解起来都有些难度,如果头发不够,可以等一等再学习。

    个人感觉访问标记法是最难理解的方法,而Morris法是一个老外发明的巧妙思想:不使用栈,而是用好树中的nul指针,但是实现后序仍然非常麻烦,我们这里不再展开,感兴趣的同学可以查一

    下。

  • 这里只介绍一种好理解又好实现的方法:反转法

  • 如下图,我们先观察后序遍历的结果是 seq = { 9 5 7 4 3 },如果我们将其整体反转的话就是

    new_seq = {3 4 7 5 9}.

  • 有没有发现要得到new_seq的方法和前序遍历思路几乎一致,只不过是左右反了。前序是先中间,再左边然后右边,而这里是先中间,再后边然后左边。那我们完全可以改造一下前序遍历,得到序列new_seq之后再reverse一下就是想要的结果了,代码如下:

java 复制代码
  public List<Integer> postOrderTraversal(TreeNode root){
          List<Integer> res = new ArrayList<>();
          if(root == null){
              return res;
          }
          Deque<TreeNode> stack = new LinkedList<>();
          TreeNode node = root;
          while(!stack.isEmpty() || node != null){
              //将当前节点的值添加到结果列表res中,
              // 然后将当前节点入栈,并将node更新为其右子节点。
              // 这个循环会一直执行,直到当前节点为空
              while(node != null){
                  res.add(node.val);
                  stack.push(node);
                  node = node.right;
              }
              node = stack.pop();
              //将node更新为该节点的左子节点,以便后续遍历左子树。
              node = node.left;
          }
          //列表中的顺序是左子树-右子树-根节点。
          // 但是后序遍历的正确顺序应该是左子树-右子树-根节点,
          // 所以需要将结果列表res反转。
          Collections.reverse(res);
          return res;
      }
复制代码
相关推荐
醉颜凉19 分钟前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
阿维的博客日记23 分钟前
java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程
java·jvm
qiyi.sky24 分钟前
JavaWeb——Web入门(8/9)- Tomcat:基本使用(下载与安装、目录结构介绍、启动与关闭、可能出现的问题及解决方案、总结)
java·前端·笔记·学习·tomcat
lapiii35828 分钟前
图论-代码随想录刷题记录[JAVA]
java·数据结构·算法·图论
RainbowSea31 分钟前
4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明
java·spring·spring cloud
程序员小明z31 分钟前
基于Java的药店管理系统
java·开发语言·spring boot·毕业设计·毕设
爱敲代码的小冰1 小时前
spring boot 请求
java·spring boot·后端
Lyqfor1 小时前
云原生学习
java·分布式·学习·阿里云·云原生
程序猿麦小七1 小时前
今天给在家介绍一篇基于jsp的旅游网站设计与实现
java·源码·旅游·景区·酒店
Dontla2 小时前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust