一天一大 lee(二叉树的中序遍历)难度:中等-Day20200914

时间:2022-07-25
本文章向大家介绍一天一大 lee(二叉树的中序遍历)难度:中等-Day20200914,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目:

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    
     2
    /
   3

输出: [1,3,2]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

抛砖引玉

抛砖引玉

思路

二叉树的中序遍历:

左子树——>根节点——>右子树

**注意:**这个遍历顺序不仅仅是遍历二叉树整体的顺序也是遍历所有子树的顺序

递归

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var inorderTraversal = function(root) {
  let _result = []
  function helper(node) {
    if (!node) {
      return
    }
    // 先深层遍历左子树
    helper(node.left)
    // 再遍历左子树的根节点
    _result.push(node.val)
    // 最后深层遍历当前根节点的右子树
    helper(node.right)
  }
  helper(root)
  return _result
}

递归方法采用了深度优先遍历的形式,一般可以采用深度优先遍历就可以采用广度优先遍历。

  • 从根节点开始这个查找其左节点(先入后出)
  • 遍历完左节点,逐个取出栈内节点
  • 当前节点不仅仅是单个二叉树节点,也可能是二叉子树,如果其是二叉子树同样遍历其左节点入栈
var inorderTraversal = function(root) {
  let _result = [],
    stack = []
  while (root || stack.length) {
    // 遍历节点左子树,入栈
    while (root) {
      stack.push(root)
      root = root.left
    }
    // 节点(子树)逐个出栈,存放到结果数字
    root = stack.pop()
    _result.push(root.val)
    // 如果出栈的是子树则同第一个有节点开始重复上面的逻辑
    root = root.right
  }
  return _result
}

Morris 中序遍历

20200808: 恢复二叉搜索树 (难度:困难)中,还遇到了Morris 中序遍历的方法

假设当前遍历到的节点为 node:

  • 如果 node 无左孩子,先将 node 的值加入答案数组,再访问 node的右孩子,即 node = node.right
  • 如果 node 有左孩子,则找到 node 左子树上最右的节点(即左子树中序遍历的最后一个节点,node 在中序遍历中的前驱节点),记为predecessor。根据predecessor 的右孩子是否为空,进行如下操作:
    • 如果predecessor 的右孩子为空,则将其右孩子指向 node,然后访问 node 的左孩子,即 node = node.left
    • 如果predecessor 的右孩子不为空,则此时其右孩子指向 node,说明我们已经遍历完 node 的左子树,将predecessor 的右孩子置空,将 node 的值加入答案数组,然后访问 node 的右孩子,即 node = node.right

简要的讲就是,在中序遍历是在左子树走到尽头是,需要回溯到之前出现的某个节点的右节点上,就使用predecessor记录这个回溯的位置,是不存在关系的节点在遍历时连续起来。

var inorderTraversal = function(root) {
  let _result = [],
      predecessor = null

  while (root) {
    if (root.left) {
      // predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
      predecessor = root.left
      while (predecessor.right && predecessor.right !== root) {
        predecessor = predecessor.right
      }

      // 让 predecessor 的右指针指向 root,继续遍历左子树
      if (!predecessor.right) {
        predecessor.right = root
        root = root.left
      }
      // 说明左子树已经访问完了,我们需要断开链接
      else {
        _result.push(root.val)
        predecessor.right = null
        root = root.right
      }
    }
    // 如果没有左孩子,则直接访问右孩子
    else {
      _result.push(root.val)
      root = root.right
    }
  }

  return _result
}