(搜索)二叉树这种数据结构,操作和判断特别多,这篇文章归纳和整理一下它的常见操作。一个有趣的现象是,关于二叉树,有广度优先和深度优先两种搜索办法。如果是广度优先,常规做法是使用一个辅助队列 queue。如果是深度优先,常规做法是使用递归。
验证二叉搜索树 对称二叉树 二叉树的前中后序遍历 二叉树的层次遍历/锯形层次遍历 二叉树最近公共祖先 二叉树最大/最小深度 删除二叉树中的节点 从中序遍历和后序遍历构造二叉树 验证二叉搜索树 98 验证一棵树是否为二叉搜索树,有两种方法。都是基于二叉搜索树的特征。
基于二叉搜索树的中序遍历结果是升序序列。中序遍历整个二叉搜索树,遍历的过程中查验是否为升序。  基于节点的左子树中所有的节点的值,均小于当前节点的值;节点的右子树中所有节点的值,均大于当前节点的值。根节点的值的范围为(-Infinity,Infinity)。其左子节点的值应该小于 root.val, 所以范围应该为(-Infinity,root.val)。同理,右子节点的范围应该为(root.val,Infinity)。树上的每个节点都应该满足上面的条件。  递归代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function  helper (root: TreeNode | null , min: number , max: number  ): boolean  {  if  (!root) return  true ;      if  (root.val  <= min || root.val  >= max) {     return  false ;   }      return  helper (root.left , min, root.val ) && helper (root.right , root.val , max); } function  isValidBST (root: TreeNode | null  ): boolean  {     return  helper (root, -Infinity , Infinity ); } 
对称二叉树 剑指 offer28 验证一个树是否为对称二叉树,优雅的方案还是递归,毕竟要一层一层的比较,而且每次比较的模式都是一样的。
递归代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function  helper (left: TreeNode | null , right: TreeNode | null  ): boolean  {  if  (!left && !right) {          return  true ;   } else  if  (left && right) {     if  (left.val  === right.val ) {              return  helper (left.left , right.right ) && helper (left.right , right.left );     } else  {              return  false ;     }   } else  {          return  false ;   } } function  isSymmetric (root: TreeNode | null  ): boolean  {  return  helper (root, root); } 
二叉树的前中后序遍历 94 这里以二叉树的中序遍历为例子。使用递归,中序遍历的实现很容易实现(代码简单)。比较难(或者说不容易记住和理解)的是使用迭代的方式来遍历。
递归代码 1 2 3 4 5 6 7 8 9 10 11 12 13 function  helper (root: TreeNode | null , res: number [] ) {  if  (!root) return ;   helper (root.left , res);   res.push (root.val );   helper (root.right , res); } function  inorderTraversal (root: TreeNode | null  ): number [] {  const  res : number [] = [];   helper (root, res);   return  res; } 
迭代代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function  inorderTraversal (root: TreeNode | null  ): number [] {  if  (!root) return  [];   const  res = [];      const  stack = [];   while  (stack.length  || root) {     while  (root) {       stack.push (root);       root = root.left ;     }               const  node = stack.pop ();     res.push (node.val );          root = node.right ;   }   return  res; } 
二叉树的层次遍历 使用递归,也就是 dfs 深度优先搜索。 可以使用一个队列来保存被访问的节点,也就是广度优先搜索。 递归代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  helper (root: TreeNode | null , index: number , res: number [][] ) {  if  (!root) return ;   res[index] ? res[index].push (root.val ) : (res[index] = [root.val ]);   helper (root.left , index + 1 , res);   helper (root.right , index + 1 , res); } function  levelOrder (root: TreeNode | null  ): number [][] {  if  (!root) return  [];   const  res = [];   helper (root, 0 , res);   return  res; } 
迭代代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function  levelOrder (root: TreeNode | null  ): number [][] {  if  (!root) return  [];   const  queue = [root];   const  res = [];   while  (queue.length ) {     const  length = queue.length ;     const  temp = [];     for  (let  i = 0 ; i < length; i++) {       const  node = queue.shift ();       temp.push (node.val );       if  (node.left ) queue.push (node.left );       if  (node.right ) queue.push (node.right );     }     res.push (temp);   }   return  res; } 
二叉树最近公共祖先 前提条件是 p 和 q 都是树中的节点。
递归代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function  lowestCommonAncestor (  root: TreeNode | null ,   p: TreeNode | null ,   q: TreeNode | null  TreeNode  | null  {  if  (!root) return  root;   if  (root === p || root === q) return  root;   const  left = lowestCommonAncestor (root.left , p, q);   const  right = lowestCommonAncestor (root.right , p, q);   if  (left && right) {     return  root;   } else  if  (left) {     return  left;   } else  {     return  right;   } } 
二叉树的最大深度 递归求出左右子树深度的最大值,然后加 1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function  maxDepth (root: TreeNode | null  ): number  {  if  (!root) return  0 ;   return  Math .max (maxDepth (root.left ), maxDepth (root.right )) + 1 ; } function  minDepth (root: TreeNode | null  ): number  {  if  (!root) return  0 ;   if  (!root.left  && !root.right ) return  1 ;   let  min = Infinity ;   if  (root.left ) {     min = Math .min (minDepth (root.left ), min);   }   if  (root.right ) {     min = Math .min (minDepth (root.right ), min);   }   return  min + 1 ; }