#

# 理解树结构

树

树的关键特性和重点概念

  • 树的层次计算规则:根结点所在的那一层记为第一层,其子结点所在的就是第二层,以此类推。
  • “度”的概念:一个结点开叉出去多少个子树,被记为结点的“度”。比如我们上图中,根结点的“度”就是 3。
  • “叶子结点”:叶子结点就是度为 0 的结点。在上图中,最后一层的结点的度全部为 0,所以这一层的结点都是叶子结点。
  • 结点和树的“高度”计算规则:叶子结点高度记为 1,每向上一层高度就加 1,逐层向上累加至目标结点时,所得到的的值就是目标结点的高度。树中结点的最大高度,称为“树的高度”。

# 理解二叉树结构

二叉树是指满足以下要求的树

  • 它可以没有根结点,作为一棵空树存在
  • 如果它不是空树,那么必须由根结点、左子树和右子树组成,且左右子树都是二叉树

二叉树的编码实现

function TreeNode(val) {
  this.val = val //数据域
  this.left = null //左树的引用
  this.right = null // 右树的引用
}

# 二叉树的遍历

以一定的顺序规则,逐个访问二叉树的所有结点,这个过程就是二叉树的遍历

  • 递归遍历
    • 先序遍历
    • 中序遍历
    • 后序遍历
  • 迭代遍历
    • 层次遍历

# 递归遍历

编程语言中,函数 Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。

  • 根结点 -> 左子树 -> 右子树
  • 左子树 -> 根结点 -> 右子树
  • 左子树 -> 右子树 -> 根结点

所谓的“先序”、“中序”和“后序”,“先”、“中”、“后”其实就是指根结点的遍历时机

const root = {
  val: 'A',
  left: {
    val: 'B',
    left: {
      val: 'D'
    },
    right: {
      val: 'E'
    }
  },
  right: {
    val: 'C',
    right: {
      val: 'F'
    }
  }
}
// 所有遍历函数的入参都是树的根结点对象
function preorder(root) {
  // 递归边界,root 为空
  if (!root) {
    return
  }

  // 输出当前遍历的结点值
  //console.log('当前遍历的结点值是:', root.val) 先续
  // 递归遍历左子树
  preorder(root.left)
  // 输出当前遍历的结点值
  //console.log('当前遍历的结点值是:', root.val) 中序
  // 递归遍历右子树
  preorder(root.right)
  // 输出当前遍历的结点值
  //console.log('当前遍历的结点值是:', root.val)  后序
}

每个节点过了 2 次 等到没有左右子节点才根据先中后在第二次打印对应当前节点

编写一个递归函数之前,大家首先要明确两样东西

  • 递归式
  • 递归边界

TIP

前中后序是遍历二叉树过程中处理每一个节点的三个特殊时间点

TIP

二叉树的所有问题,就是让你在前中后序位置注入巧妙的代码逻辑,去达到自己的目的

TIP

二叉树题目的递归解法可以分两类思路,第一类是遍历一遍二叉树得出答案,第二类是通过分解问题计算出答案,这两类思路分别对应着 回溯算法核心框架 和 动态规划核心框架。

TIP

但这里面大有玄妙,意味着前序位置的代码只能从函数参数中获取父节点传递来的数据,而后序位置的代码不仅可以获取参数数据,还可以获取到子树通过函数返回值传递回来的数据。

上次更新: 3/8/2022, 8:13:19 PM