【算法15天:Day15】第六章二叉树 LeetCode 对称二叉树(101)

75 阅读4分钟

题目三:

image.png

解法一:(递归)

解题思路:参考代码随想录,整体就相当于左右子树对称镜像位置一个一个比较。

递归三部曲

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

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

返回值自然是bool类型。

代码如下:

const compare = function (left, right){}
  1. 确定终止条件

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

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

  • 左节点为空,右节点不为空,不对称,return false
  • 左不为空,右为空,不对称 return false
  • 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

  • 左右都不为空,比较节点数值,不相同就return false

此时左右节点不为空,且数值也不相同的情况我们也处理了。

代码如下:

if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false; // 注意这里我没有使用else

注意上面最后一种情况,我没有使用else,而是elseif, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。

  1. 确定单层递归的逻辑

此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  • 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。

代码如下:

bool outside = compare(left.left, right.right);   // 左子树:左、 右子树:右
bool inside = compare(left.right, right.left);    // 左子树:右、 右子树:左
bool isSame = outside && inside;                    // 左子树:中、 右子树:中(逻辑处理)
return isSame;

如上代码中,我们可以看出使用的遍历方式,左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)

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);
};

// 或者

const isSymmetric = (root) => {
  
    const check = (left, right) => {
        if (left == null && right == null) { // 两个子树都为null,是对称的
           return true;
        }
        if (left && right) { // 两个子树都存在,则需要:root值相同,且他们的子树也满足镜像
            return left.val == right.val && check(left.left, right.right) && check(left.right, right.left);
        }
        return false;        // 一个子树存在一个不存在,肯定不对称
    };

    if (root == null) {     // 如果传入的root就是null,对称
        return true;
    }           
    return check(root.left, root.right); // 否则,判断它的左右子树是否满足对称
}

解法二:(队列)

队列实现迭代判断是否为对称二叉树:

  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
      }
      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
  }

解法三:(栈)

栈实现迭代判断是否为对称二叉树:

var isSymmetric = function(root) {
  // 迭代方法判断是否是对称二叉树
  // 首先判断root是否为空
  if(root === null){
      return true;
  }
  let stack=[];
  stack.push(root.left);
  stack.push(root.right);
  while (stack.length){
      let rightNode = stack.pop();//右节点
      let leftNode = stack.pop();//左节点
      if(leftNode === null && rightNode === null){
          continue;
      }
      if(leftNode === null || rightNode === null || leftNode.val !== rightNode.val){
          return false;
      }
      stack.push(leftNode.left);//左节点左孩子入队
      stack.push(rightNode.right);//右节点右孩子入队
      stack.push(leftNode.right);//左节点右孩子入队
      stack.push(rightNode.left);//右节点左孩子入队
  }
  return true;
};

解法四:(中序遍历+层数)

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function (root) {
    if (root == null) {
        return true;
    }
    //左中右顺序递归二叉树
    var nums = [];
    search(nums, root, 1);
    //判断是否对称
    var i = 0, j = nums.length - 1;
    while (i < j) {
        if (nums[i] != nums[j]) {
            return false;
        }
        i++;
        j--;
    }
    return true;
};
/**
 * 按照左中右顺序递归二叉树,输出至数组nums中
 * @param nums      输出
 * @param n         节点
 * @param k         层次
 */
function search(nums, n, k) {
    //左边
    if (n.left != null) {
        search(nums, n.left, k + 1);
    }
    //节点值,层次
    nums.push(n.val + ',' + k);
    //右边
    if (n.right != null) {
        search(nums, n.right, k + 1);
    }
}

我最开始考虑的就是中序遍历,最后判断数组是否对称,但是对于特殊null的情况并没有想到如何考虑,参考别人的想法,可以加上层数,很不错。