14_对称二叉树

简介: 14_对称二叉树

对称二叉树

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

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

【思路】

对于二叉树是否对称,要比较的是根节点的左子树右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。比较的是两个子树的里侧和外侧的元素是否相等。

那么遍历的顺序应该是什么样的呢?

本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。

正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。

但都可以理解算是后序遍历,尽管已经不是严格上在一个树上进行遍历的后序遍历了。

其实后序也可以理解为是一种回溯。

递归法

递归三部曲

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

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

返回值自然是bool类型

2、确定终止条件

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

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

  • 左节点为空,右节点不为空,不对称,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,而是else if, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。

3、确定单层递归的逻辑

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

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子
  • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子
  • 如果左右都对称就返回true,有一侧不对称就返回false
bool outside = compare(left.left, right.right);
bool inside = compare(left.right, right.left);
bool isSame = outside && inside;
return isSame;

完整代码如下所示:

import java.util.LinkedList;
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        return compare(root.left, root.right);
    }
    public boolean compare(TreeNode left, TreeNode right) {
        //确定递归终止条件
        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;
        }
        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        boolean inSide = compare(left.right, right.left);
        boolean outside = compare(left.left, right.right);
        return inSide && outside;
    }
}

方法二:迭代法

使用队列

通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等。

/
**  迭代法
**  使用双端队列,相当于两个栈
/
public boolean isSymmetric(TreeNode root) {
  Deque<TreeNode> deque = new LinkedList<>();
    deque.offerFirst(root.left);
    deque.offerLast(root.right);
    while (!deque.isEmpty()) {
        TreeNode leftNode = deque.pollFirst();
        TreeNode rightNode = deque.pollLast();
        if (leftNode == null && rightNode == null) {
            continue;
        }
        // 以上三个判断条件合并
        if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
            return false;
        }
        deque.offerFirst(leftNode.left);
        deque.offerFirst(leftNode.right);
        deque.offerLast(rightNode.right);
        deque.offerLast(rightNode.left); 
    }
    return true;
}

方法三:迭代法

使用普通队列

public boolean isSymmetric(TreeNode root) {
    Queue<TreeNode> deque = new LinkedList<>();
    deque.offer(root.left);
    deque.offer(root.right);
    while (!queue.isEmpty()) {
      TreeNode leftNode = deque.poll();
            TreeNode rightNode = deque.poll();
            if (leftNode == null && rightNode == null) {
                continue;
            }
            //以上三个条件合并
            if (leftNode == null || rightNode == null || leftNode.val != right.val)
                return false;
            //这里顺序与使用Deque不同
            deque.offer(leftNode.left);
            deque.offer(rightNode.right);
            deque.offer(leftNode.right);
            deque.offer(rightNode.left);
    }
    return true;
}


相关文章
|
XML 数据管理 测试技术
深入探索软件自动化测试框架的设计与实现
【4月更文挑战第26天】 随着软件开发周期不断缩短,传统的手动测试方法已难以满足快速迭代的需求。本文聚焦于自动化测试框架的构建与优化,旨在提供一种高效、可维护且可扩展的软件测试解决方案。文章从自动化测试的必要性出发,详细阐述了自动化测试框架设计的核心要素,包括模块化设计、数据驱动测试以及关键词驱动测试等概念。同时,结合实例分析了如何利用流行的测试工具进行框架搭建,并提出了针对常见问题的创新解决方法。最后,通过案例研究展示了该框架在实际项目中的应用效果和潜在改进空间。
|
消息中间件 负载均衡 Java
SpringCloud系列--(二)服务的注册于发现
SpringCloud系列--(二)服务的注册于发现
75 1
SpringCloud系列--(二)服务的注册于发现
|
算法
大话数据结构--迪杰斯特拉(Dijkstra)算法
大话数据结构--迪杰斯特拉(Dijkstra)算法
305 0
|
算法
「leetCode」114-二叉树展开为链表⚡️
「leetCode」114-二叉树展开为链表⚡️
157 0
「leetCode」114-二叉树展开为链表⚡️
|
物联网 机器人
物联网平台IOT Studio业务逻辑实现报警功能
通过IOT studio的业务逻辑,实现设备端报警连接钉钉提醒的功能
1728 1
物联网平台IOT Studio业务逻辑实现报警功能
|
SQL JSON Java
[Hive]JsonSerde使用指南
注意: 重要的是每行必须是一个完整的JSON,一个JSON不能跨越多行,也就是说,serde不会对多行的Json有效。 因为这是由Hadoop处理文件的工作方式决定,文件必须是可拆分的,例如,Hadoop将在行尾分割文本文件。
2936 0
|
10天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
9天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
404 130
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话