【温故知新】谷歌面试:`226. 翻转二叉树` 广度优先、深度优先 、前序、后序递归4种解法

524 阅读3分钟

题目描述

翻转一棵二叉树。

示例:

输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9
输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

备注: 这个问题是受到 Max Howell 的 原问题 启发的 :

谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/in… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

一位大佬吐槽自己面试谷歌因为没写出翻转二叉树被拒了 image.png

知识回顾:

前序、中序、后序遍历概念

以根节点作为参考:

  • DLR--前序遍历(根在前,从左往右,一棵树的根永远在左子树前面,左子树又永远在右子树前面 )

  • LDR--中序遍历(根在中,从左往右,一棵树的左子树永远在根前面,根永远在右子树前面)

  • LRD--后序遍历(根在后,从左往右,一棵树的左子树永远在右子树前面,右子树永远在根前面)

解题思路

思路1:广度优先遍历 BFS

  • 利用队列的先进先出的原则,进行元素互换;
  • 初始化队列:queue=[root]
  • 取出最先入队的元素;
    • 如果当前节点为null,则进行下一个节点的遍历;
    • 否则进行左右子树的交换;
    • 交换结束,从左往右继续进行入队;
  • 广度优先遍历完成,则当前树完成所有的交互翻转;

代码

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function invertTree(root: TreeNode | null): TreeNode | null {
    // 思路1:广度优先遍历:利用队列的先进先出的原则,进行元素互换;
    let queue=[root]; // 初始化队列;
     while(queue.length>0){
         // 取出最先入队的元素;
         let current =queue.shift();
          if(current===null){
              continue;
          }
          // 当前节点存在,此时进行左右子树互换;  
          [current.left,current.right]=[current.right,current.left]
          //互换结束,从左往右继续进行入队;
          queue.push(current.left);
          queue.push(current.right)
     }// end of queue 直到所有的元素广度优先遍历完毕;
    return root;

};

思路2:利用栈stack的特性

  • 同理如上
    • 可以利用栈stack数据结构的先进后出的原则;
function invertTree(root: TreeNode | null): TreeNode | null {
    // 同理可以利用栈stack数据结构的先进后出的原则;
     let stack=[root]
     while(stack.length>0){
         let current=stack.pop();
         if(current===null){
             continue;
         }
         //交换左右子树节点;
         [current.left,current.right]=[current.right,current.left]
         // 栈stack先进出的原则,
         stack.push(current.right);
         stack.push(current.left);
     }// end of stack
    return root;
};

思路3:深度优先遍历 DFS

递归:前序遍历

  • 前序遍历:
    • 先处理根元素,然后再处理左子树,然后再处理右子树;
function invertTree(root: TreeNode | null): TreeNode | null {
  // 递归进行前序遍历 :先处理根元素,然后再处理左子树,然后再处理右子树;
  if(root===null){
      return root;
  }
  // 进行跟元素交互操作
  [root.left,root.right]=[root.right,root.left]
  // 处理左子树;
   invertTree(root.left);
   // 处理右子树
   invertTree(root.right);
   return root;
  
};

  • 后序遍历:
    • 先再处理左子树,然后再处理右子树;最后处理根元素,
function invertTree(root: TreeNode | null): TreeNode | null {
  // 递归进行前序遍历 :先处理根元素,然后再处理左子树,然后再处理右子树;
  if(root===null){
      return root;
  }

   // 处理左子树;
   invertTree(root.left);
   // 处理右子树
   invertTree(root.right);

  // 进行跟元素交互操作
  [root.left,root.right]=[root.right,root.left]
   return root;
  
};

二叉树相关回顾

【温故知新】101. 对称二叉树 利用队列、递归实现二叉树的判断

【温故知新】102. 二叉树的层序遍历 广度优先遍历、队列先进先出的原则

【温故知新】104. 二叉树的最大深度 广度优先遍历、递归实现

【温故知新】1367. 二叉树中的列表 递归分解问题求解