【牛客 - 剑指offer】JZ28 对称的二叉树 两种方案 Java实现

本文介绍了如何使用Java解决剑指Offer中的对称二叉树问题,提供了两种解决方案:一种基于栈的迭代方法,另一种是递归方法。在迭代方法中,通过两个栈分别从左右两侧进行对比;递归方法则利用前序遍历的特性,对树的左右子节点进行互换比较。
摘要由CSDN通过智能技术生成


剑指offer题解汇总 Java实现

https://blog.csdn.net/guliguliguliguli/article/details/126089434

本题链接

知识分类篇 - 树 - JZ28 对称的二叉树

题目

在这里插入图片描述
题目的主要信息是:判断一棵二叉树是否是镜像,即判断二叉树是否是轴对称图形

方案一 stack(我的解决思路)

用两个栈来存储节点

  • stack1是从左边开始存储

  • stack2是从右边开始存储

首先,在stack1和stack2中都存入根节点

进入循环,stack1,stack2,都弹出栈顶元素,比较弹出的栈顶元素的值

  • 如果相等,则可以继续比较其左子节点和右子节点的情况

    • 由于第一次两个栈的栈顶元素都是在进入循环之前,加进去的根节点,所以,弹出的两个节点的值肯定是相同的
  • 如果不等,则该棵二叉树不是对称的二叉树,return false

比较stack1当前节点的左子节点和stack2当前节点的右子节点

  • 返回false的两种情况

    • 如果stack1当前节点的左子节点为空,stack2的当前节点的右子节点不为空,或者,stack1当前节点的左子节点不为空,stack2的当前右子节点为空,那么该棵二叉树不是对称的二叉树

    • 如果stack1当前节点的右子节点为空,stack2的当前节点的左子节点不为空,或者,stack1当前节点的右子节点不为空,stack2的当前左子节点为空,那么该棵二叉树不是对称的二叉树

  • 添加新的节点到stack中的两种情况

    • 如果stack1的当前节点的左子节点和stack2的当前节点的右子节点都不为空,则向stack1中添加stack1当前节点的左子节点,向stack2中添加stack2当前节点的右子节点

    • 如果stack1的当前节点的右子节点和stack2的当前节点的左子节点都不为空,则向stack1中添加stack1当前节点的右子节点,向stack2中添加stack2当前节点的左子节点

  • 不予考虑的情况

    • 如果stack1当前节点的左子节点和stack2当前节点的右子节点都为空,则不做任何处理
import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    boolean isSymmetrical(TreeNode pRoot) {

        if (pRoot == null) {
            return true;
        }

        Stack<TreeNode> s1 = new Stack<>();
        Stack<TreeNode> s2 = new Stack<>();

        s1.push(pRoot);
        s2.push(pRoot);

        while (!s1.isEmpty() && !s2.isEmpty()) {
            TreeNode n1 = s1.pop();
            TreeNode n2 = s2.pop();

            if (n1.val != n2.val) {
                return false;
            }

            if (n1.left != null && n2.right != null) {
                s1.push(n1.left);
                s2.push(n2.right);
            }

            if (n1.right != null && n2.left != null) {
                s1.push(n1.right);
                s2.push(n2.left);
            }

            if ((n1.left != null && n2.right == null) || (n1.left == null && n2.right != null)) {
                return false;
            }

            if ((n1.right != null && n2.left == null) || (n1.right == null && n2.left != null)) {
                return false;
            }
        }
        return true;
    }

}

稍加修改,思路一致

import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    boolean isSymmetrical(TreeNode pRoot) {

        if (pRoot == null) {
            return true;
        }

        Stack<TreeNode> s1 = new Stack<>();
        Stack<TreeNode> s2 = new Stack<>();

        s1.push(pRoot);
        s2.push(pRoot);

        while (!s1.isEmpty() && !s2.isEmpty()) {
            TreeNode n1 = s1.pop();
            TreeNode n2 = s2.pop();

            if (n1 == null && n2 == null) {
                continue;
            }

            if (n1 == null || n2 == null || n1.val != n2.val) {
                return false;
            }

            s1.push(n1.left);
            s1.push(n1.right);

            s2.push(n2.right);
            s2.push(n2.left);

        }
        return true;
    }

}

方案二 递归(推荐使用)

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能将原本的问题分解为更小的子问题,这是使用递归的关键。

而二叉树的递归,则是将某个节点的左子树、右子树看成一棵完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数,不断进入子树。

前序遍历的时候,采用的是“根左右”的遍历次序,如果这棵二叉树是对称的,即相应的左右节点交换位置完全没问题,那就可以尝试“根右左”遍历,按照轴对称图像的性质,这两种次序的遍历结果应该是一样的

注意
轴对称图像“根左右”,“根右左”的遍历顺序是一样的
但是,“根左右”,“根右左”的遍历顺序一样,不代表就是轴对称图形
例如
下面这幅图的“根左右”顺序是1 2 3 2 3,“根右左”顺序也是1 2 3 2 3
在这里插入图片描述

不同的方式遍历两次,将结果拿出来比较是一种可行的方法,但是,这种方式比较麻烦。

那么,可以直接在遍历的过程中比较。遍历的方式使用前序遍历的递归形式:

  • 终止条件:当进入子问题的两个节点都为空,说明到了叶子节点,且是同步的,因此结束本次子问题,返回true;当进入子问题的两个节点只有一个为空,或者元素值不相等,说明这里的对称不匹配,同样结束本次子问题,返回false

  • 返回值:每一级将子问题是否匹配的结果往上传递

  • 本级任务:每个子问题,按照上述思路。“根左右”走左边的时候“根右左”走右边,“根左右”走右边的时候“根右左”走左边,一起进入子问题,需要两边都是匹配才能对称

import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    boolean recursion(TreeNode root1, TreeNode root2) {
        //可以两个都为空
        if (root1 == null && root2 == null)
            return true;
        //只有一个为空或者节点值不同,必定不对称
        if (root1 == null || root2 == null || root1.val != root2.val)
            return false;
        //每层对应的节点进入递归比较
        return recursion(root1.left, root2.right) && recursion(root1.right, root2.left);
    }

    boolean isSymmetrical(TreeNode pRoot) {
        return recursion(pRoot, pRoot);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值