算法刷题 - 二叉树(对称专题合集)

56 阅读5分钟

题目01 - 判断二叉树是否对称相等


原题链接

LeetCode 101 - 对称二叉树

题目描述

你一个二叉树的根节点 root , 检查它是否轴对称

输入示例

这里写图片替代文字 这里写图片替代文字

思路 01 - 递归

1. 比较的节点

  • 比较的是两个子树的里侧和外侧的元素是否相等
  • 假设当前比较的节点为 node。其左右子节点已经对称相等 node.left.val = node.right.val
  1. 第一组: 比较里侧为 node.left.left && node.right.right
  2. 第二组: 比较外侧为 node.left.right && node.right.left

2. 递归终止条件:

  • 左不为空,右为空,不对称返回 false
  • 左右都为空,对称,返回 true
  • 左右都不为空,比较节点的数值,不相同就返回 false
  • 判断当前组节点是否对称相等的条件
    • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子
    • 比较内侧是否对称:传入左节点的右孩子和右节点的左孩子
    • 后续如果左右都对称就返回 true, 有一侧不对称就返回 false

代码实现 - 递归

class Solution {
    
    public boolean isSymmetric(TreeNode root) {
        // 如果根节点为空或者仅有一个节点
        if (root == null || (root.left == null && root.right == null))
            return true;
        return isSymmetricHelper(root.left, root.right);
    }

    //递归判断是否对称
    public boolean isSymmetricHelper(TreeNode left, TreeNode right) {
        if (left == null && right == null) {
            return true;
        }
        if (left == null || right == null) {
            return false;

        }
        if (left.val != right.val) {
            return false;
        }
        // 比较外侧里侧是否对称
        return isSymmetricHelper(left.left, right.right) && isSymmetricHelper(left.right, right.left);
    }
}

执行结果

  • 时间复杂度:O(N),其中 n 是二叉树的节点数。
  • 空间复杂度:O(H),其中 h 是二叉树的高度。最坏情况下为 O(n),最佳情况下为 O(log n)。

思路 02 - 迭代

  • 参考层序遍历的思想;借助队列遍历
  • 循环判断条件,只要队内不为空
  • 但是入队顺序不同
    • 首轮将当前节点的左右子节点入队
    • 每次出队需要连续将两个节点出队
    • 判断如果对称相等后按照如下如何入队:
      • 左子树左子节点 和 右子树右子节点 为一组 (外侧)
      • 左子树的右子节点 和 右子树的左子节点为一组 (里侧)
  • 判断是否对称的条件
    • 如果出队的两个节点都为 null; 则对称相等,继续出队 (continue)
    • 如果两个节点仅有一个为 null; 则不是对称相等,返回 false;
    • 如果两个节点都不为 null; 则判断两个节点值是否相等,相等则对称相等;否则不对称相等

示意图

  • 蓝色节点代表还未入队
  • 橙色节点表示已经入队
这里写图片替代文字 这里写图片替代文字

代码实现

class Solution {
    public boolean isSymmetric(TreeNode root) {
        // 检查输入
        if (null == root || root.left == null && root.right == null)
            return true;
        Deque<TreeNode> queue = new LinkedList<>();
        // 首轮入队
        queue.offer(root.left);
        queue.offer(root.right);
        // 循环
        while (!queue.isEmpty()) {
            TreeNode peek1 = queue.poll();
            TreeNode peek2 = queue.poll();
            // 都为空
            if (peek1 == null && peek2 == null) {
                continue;
            }
            // 其中一个为空
            if (peek1 == null || peek2 == null) {
                return false;
            }
            // 都不为空
            if (peek1.val != peek2.val) {
                return false;
            }
            // 将外侧组节点入队
            queue.offer(peek1.left);
            queue.offer(peek2.right);
            // 将里侧组节点入队
            queue.offer(peek1.right);
            queue.offer(peek2.left);
        }
        // 全部对称相等则满足
        return true;
    }
}

执行结果

  • 时间复杂度: O(N)
  • 空间复杂度: O(N)

题目 02 - 相同的树

原题链接

LeetCode 100 - 相同二叉树

题目描述

  • 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

  • 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

输入示例

转存失败,建议直接上传图片文件 转存失败,建议直接上传图片文件

思路

  • 递归返回条件
  1. 如果节点 p 和 q 都为空,返回 true
  2. 如果仅有其中一个节点为空,不相等,返回 false
  3. 如果都不为空但是 val 不相等,返回 false
  • 如果都不为空并且 val 一致,继续比较两个分组:
  1. p 的左子树和 q 的右子树
  2. p 的右子树和 q 的右子树
  • 如果后续比较存在一次不相等则判定不是相等二叉树

代码实现

class Solution {

    public boolean isSameTree(TreeNode p, TreeNode q) {
        //递归终止条件一:两个节点都为空,两个节点相等
        if (null == p && null == q) {
            return true;
        }
        //递归终止条件二: 仅有一个节点为空,两节点不相等
        if (p == null || q == null) {
            return false;
        }
        //递归终止条件三: 两个节点都不为空并且值相等
        if (p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}

执行结果

  • 时间复杂度: O(1)
  • 空间复杂度: O(1)

题目 03 - 另外一颗子树

原题链接

LeetCode 572 - 另外一颗子树

题目描述

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

输入示例

转存失败,建议直接上传图片文件 转存失败,建议直接上传图片文件

思路

  • 递归遍历root下的所有节点,与 subTree 比较;比较二叉树是否相等的逻辑与 LeetCode 100 - 判断二叉树是否相等 一致
  • 递归返回条件一:如果 root 为空,返回 false;说明沿着当前树枝往下不存在相同子树
  • 递归返回条件二: 如果 rootsubTree 相等,返回 true;当前 root 节点下的子树与 subTree 一致
  • 递归查找 root.left 子树是否存在和 subTree 一致的子树 || root.right 子树是否存在和 subTree 一致的子树

代码实现

    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        // 检查输入
        if (root == null) {
            return false;
        }
        // 递归遍历root各个节点是子树根节点
        if (isSameTree(root, subRoot)) {
            return true;
        }
        // 递归左、右子节点
        return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
    }

    public boolean isSameTree(TreeNode root, TreeNode subTree) {
        // 节点都为空
        if (root == null && subTree == null) {
            return true;
        }
        // 仅有一个节点为空
        if (root == null || subTree == null) {
            return false;
        }
        // 节点元素不相等
        if (root.val != subTree.val) {
            return false;
        }
        return isSameTree(root.left, subTree.left) && isSameTree(root.right, subTree.right);
    }

执行结果

  • 时间复杂度: O(N)
  • 空间复杂度: O(H)

更多二叉树专题请参考个人专栏