【代码随想录 | day17】(JavaScript) 二叉树系列:559.n叉树的最大深度、226.翻转二叉树、101.对称二叉树

140 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情

  • 559.n叉树的最大深度
  • 226.翻转二叉树
  • 101.对称二叉树

559.n叉树的最大深度

题目链接:N 叉树的最大深度

给定一个 N 叉树,找到其最大深度。最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。

思路:就是N叉树的层序遍历写法(用到了 for (let item of node.children) { }),再加上二叉树的最大深度写法。

 var maxDepth = function (root) {
   // 最大的深度就是二叉树的层数
   if (root === null) return 0;
   let queue = [root];
   let height = 0;
   while (queue.length) {
     let n = queue.length;
     height++;
     for (let i = 0; i < n; i++) {
       let node = queue.shift();
       for (let item of node.children) {
         item && queue.push(item);
       }
     }
   }
   return height;
 };

226.翻转二叉树

题目链接:翻转二叉树

翻转二叉树.png

递归法

思路以及代码

首先要注意,想要返回的数据类型是节点,并不是数组!所以我一开始的思路(遍历每一层节点值,然后翻转数据压入数组,最后进行一个 res 二维数组的扁平化)是行不通的!!

这一题关键在于,选择前中后序哪一种遍历方式? 使用前序遍历和后序遍历都可以,然后把每一个节点的左右孩子反转一下。

  1. 确定递归函数的参数和返回值
  2. 确定终止条件:当前节点为空的时候,就返回
  3. 确定单层递归的逻辑:因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。
 var invertTree = function(root) {
     // 终止条件
     if (!root) {
         return null;
     }
     // 交换左右节点
     const rightNode = root.right;
     root.right = invertTree(root.left);
     root.left = invertTree(rightNode);
     return root;
 };

层序遍历法

思路以及代码

使用层序遍历,就要先定义好节点交换函数

 var invertTree = function (root) {
   // 节点交换函数
   const invertNode = function (root, left, right) {
     let temp = left;
     left = right;
     right = temp;
     root.left = left;
     root.right = right;
   }
 ​
   if (root === null) return root;
   let queue = [];
   queue.push(root);
   while (queue.length) {
     // 记录当前层数的节点数
     let n = queue.length;
 ​
     while (n--) {
       let node = queue.shift();
       invertNode(node, node.left, node.right);
       node.left && queue.push(node.left);
       node.right && queue.push(node.right);
     }
   }
   // 二维数组扁平化处理
   return root
 };
  • let temp = left; 那么这个 temp 装的就是当前的 左节点,记住类型是节点。
  • 接下来一步的root.left = left;就是将当前的root的左孩子指向交换后的左节点。如果没有这一步,root还是原来的那个root。

101.对称二叉树

题目链接:对称二叉树

比较的是根节点的两个子树是否是相互翻转的

对称二叉树.jpg

递归法

因为需要不断收集左右子树的信息,然后返回给上一层,所以这里要使用后序遍历(左右根)

  1. 确定递归函数的参数和返回值

    1. 比较根节点下面的两个树,参数自然也是左子树节点和右子树节点。返回值自然是bool类型。
  2. 确定终止条件

    1. 要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

    2. 节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点

      • 左节点为空,右节点不为空,不对称,return false
      • 左不为空,右为空,不对称 return false
      • 左右都为空,对称,返回true
    3. 此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

      • 左右都不为空,比较节点数值,不相同就return false
    4. 此时左右节点不为空,且数值也不相同的情况我们也处理了。

  3. 确定单层递归的逻辑 处理 左右节点都不为空,且数值相同的情况。

    • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
    • 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
    • 如果左右都对称就返回true ,有一侧不对称就返回false 。
 var isSymmetric = function (root) {
   //使用递归遍历左右子树 递归三部曲
   // 1. 确定递归的参数 root.left root.right和返回值true false 
   const compareNode = function (left, right) {
     //2. 确定终止条件 空的情况
     if (left === null && right !== null || left !== null && right === null) {
       return false;
     } else if (left === null && right === null) {
       return true;
     } else if (left.val !== right.val) {
       return false;
     }
     //3. 确定单层递归逻辑
     let outSide = compareNode(left.left, right.right);
     let inSide = compareNode(left.right, right.left);
     return outSide && inSide;
   }
   if (root === null) {
     return true;
   }
   return compareNode(root.left, root.right);
 };

迭代法

使用队列来比较两个树(根节点的左右子树)是否相互翻转。

注意这不是层序遍历,且由于题目需求,不能使用平常的前中后序遍历了。已经不是所谓二叉树遍历的前中后序的关系了。

101.对称二叉树

 var isSymmetric = function (root) {
   //迭代方法判断是否是对称二叉树
   //首先判断root是否为空
   if (root === null) {
     return true;
   }
   let queue = [];
   queue.push(root.left);
   queue.push(root.right);
   while (queue.length) {
     let leftNode = queue.shift();//左节点
     let rightNode = queue.shift();//右节点
     // 左节点为空、右节点为空,此时说明是对称的
     if (leftNode === null && rightNode === null) {
       continue;
     }
     // 左右一个节点不为空,或者都不为空但数值不相同,返回false
     if (leftNode === null || rightNode === null || leftNode.val !== rightNode.val) {
       return false;
     }
     
     queue.push(leftNode.left);//左节点左孩子入队
     queue.push(rightNode.right);//右节点右孩子入队
     queue.push(leftNode.right);//左节点右孩子入队
     queue.push(rightNode.left);//右节点左孩子入队
   }
   return true;
 };

所以在这样不断while循环的过程中,就能判断相对应位置的节点是否相同!


参考文章