力扣刷题记录-对称二叉树

  主要介绍力扣 101. 对称二叉树一题的递归和迭代解法。并且通过这题的思想还可以练习100. 相同的树与力扣 572. 另一棵树的子树

目录

    • 对称二叉树
      • 力扣 101. 对称二叉树
        • 递归解法
        • 迭代法
      • 力扣 100. 相同的树
      • 力扣 572. 另一棵树的子树
      • 剑指 Offer 26. 树的子结构

对称二叉树

力扣 101. 对称二叉树

原题链接
力扣刷题记录-对称二叉树_第1张图片

递归解法

1.递归参数和返回值
因为需要比较是否对称,因此每次递归传入的参数因当是一对对称位置上的结点,返回值应当是bool类型

boolean compare(TreeNode leftNode,TreeNode rightNode)

2.终止条件和递归过程
比较两个结点:
①首先应该先判断是否为空,都为空则对称,返回true;
②能经过①走到②这一步,则两个结点不是全为空,有三种情况,只有左为空,只有右为空,或均不为空;前两种情况说明不对称,返回false,均不为空情况下,如果结点值不同,也不对称,返回false;

//首先判断空节点的三种情况
if(leftNode==null&&rightNode==null)return true;
else if(leftNode!=null&&rightNode==null)return false;
else if(leftNode==null&&rightNode!=null)return false;
//判断空节点之后判断值是否相同
else if(leftNode.val!=rightNode.val)return false;

③能走到这一步,说明两个结点对称了,需要进一步判断下一层的结点的对称情况,也就是下一层的外侧和内侧的两对结点(需要同时满足才是对称,这也决定了迭代法里面每次都是比较一对结点,循环末尾需要内外侧两对结点成对入队)

//排除空节点和值之后,肯定leftNode.val=rightNode.val,递归判断下一层的情况
else return compare(leftNode.left,rightNode.right)&&compare(leftNode.right,rightNode.left);//分别判断下一层外侧和内侧结点是否对称

整体代码如下:

//递归方法
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left,root.right);//这题结点树大于等于1,不用判断空节点
    }
    boolean compare(TreeNode leftNode,TreeNode rightNode){
        //首先判断空节点的三种情况
        if(leftNode==null&&rightNode==null)return true;
        else if(leftNode!=null&&rightNode==null)return false;
        else if(leftNode==null&&rightNode!=null)return false;
        //判断空节点之后判断值是否相同
        else if(leftNode.val!=rightNode.val)return false;
        //排除空节点和值之后,肯定leftNode.val=rightNode.val,递归判断下一层的情况
        else return compare(leftNode.left,rightNode.right)&&compare(leftNode.right,rightNode.left);//分别判断下一层外侧和内侧结点是否对称
        
    }
}
迭代法

每次都是从队中取出一对结点结点进行比较,一层层比较下去,具体过程见代码注释。

//迭代方法
class Solution {
    public boolean isSymmetric(TreeNode root) {
        Queue<TreeNode>que=new LinkedList<>();//用队列暂存一对对结点
        que.offer(root.left);//入队出队一定都是成双成对的
        que.offer(root.right);
        TreeNode leftNode,rightNode;//暂存出队的一对结点,用于比较
        while(!que.isEmpty()){
            leftNode=que.poll();
            rightNode=que.poll();
            //首先判断结点是否为空,当两个结点都为空,说明对称,进入下一次循环
            if(leftNode==null&&rightNode==null)continue;
            //能走到这里,说明两个结点不是全空,可能是其中一个为空,也可能是都不空(那就要判断值是不是相等)
            if(leftNode==null||rightNode==null||leftNode.val!=rightNode.val)return false;
            //能走到这里,说明这两个点对称了,要判断下一对结点(入队)
            que.offer(leftNode.left);
            que.offer(rightNode.right);
            que.offer(leftNode.right);
            que.offer(rightNode.left);
        }
        return true;//如果循环结束都没返回false,则说明全过程都是对称
    }
}

理解了这题的做法,可以对另外三题进行练习:

力扣 100. 相同的树

原题链接

//递归(深度优先遍历) 
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p==null&&q==null)return true;
        else if(p==null||q==null||p.val!=q.val)return false;
        //分别递归判断左子树和右子树
        else return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);//注意这里是比较对应位置,而不是像上题一样的对称位置
    }
}


//迭代法(广度优先遍历),用一个队列即可,每次出入队都成对
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        Queue<TreeNode>que=new LinkedList<>();
        que.offer(p);que.offer(q);
        while(!que.isEmpty()){
            p=que.poll();q=que.poll();
            if(p==null&&q==null)continue;
            if(p==null||q==null||p.val!=q.val)return false;
            que.offer(p.left);
            que.offer(q.left);
            que.offer(p.right);
            que.offer(q.right);
        }
        return true;
    }
}

力扣 572. 另一棵树的子树

原题链接

递归解法:需要理解100题的递归方法,这题递归的思路就会很清晰。

//递归解法
class Solution {
    //100题的递归函数,直接使用,p对应母树root的点,q对应子树subRoot的点
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p==null&&q==null)return true;
        else if(p==null||q==null||p.val!=q.val)return false;
        //分别递归判断母树和子树的左子树和右子树
        else return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
    }
    //主函数
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root==null)return false;//母树遍历完都没有返回true,说明都没匹配上
        //每层递归里面调用判断两树相等函数,判断是否相等,再调用主函数递归判断母树中当前结点的左右子树;
        return isSameTree(root,subRoot)||isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);
    }
}


//哈希+深度优先遍历解法
class Solution {
    //随便两个素数P、Q,用在哈希函数里面计算结点哈希值
    int P=7,Q=11,mod=100007;//mod用于防止哈希计算过程数值溢出

    boolean ans=false;//ans标记子树是否在母树中出现过

    int T=-99;//用于记录子树的哈希值,一开始设置为负数是为了避免碰巧等于子树哈希值,这样就可以让主函数里的T=dfs(subRoot)语句,给T赋正确的哈希值

    //深度优先遍历,并且计算每个结点对应子树的哈希值
    public int dfs(TreeNode root){

        //根节点如果是空的,随便返回一个值(注意不能返回0,因为结点值为0也可能返回0,这样就没办法区分结点值为0和结点为空的情况,结果会出错)
        if(root==null)return 1234;

        //root.val%mod+mod先把当前遍历到的点值映射成非负,最后%mod防止溢出
        int x=(root.val%mod+mod)%mod;

        int L=dfs(root.left);//求左子树的哈希值
        int R=dfs(root.right);//右子树的哈希值
        
        //若在深度优先遍历母树过程中,发现母树某结点对应子树的哈希值与待匹配子树相同,说明子树在母树中出现了,ans标记置true
        if(T==L||T==R)ans=true;

        //返回当前root结点(这一层递归传入的结点)对应子树的哈希值
        return (x+L*P+R*Q)%mod;
    }
    //主函数
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        
        T=dfs(subRoot);//求子树的哈希值

        //判断子树哈希值和母树根节点哈希值相同,若相同则标记,不相同则在深度优先遍历母树过程中继续判断
        if(T==dfs(root))ans=true;

        return ans;//最终返回标记
    }
}

剑指 Offer 26. 树的子结构

原题链接

这题要注意与力扣 572. 另一棵树的子树区分,这题的子树只要结构可以对应就可以,没有要求从根节点到叶子结点完全对应。

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        //约定空树不是任意一个树的子结构
        if(A == null || B == null) return false; 
        // dfs(A, B) 当前节点B是否是A的子树,若不是,则同理判断当前节点的孩子节点
        return dfs(A, B)||isSubStructure(A.left,B)||isSubStructure(A.right, B);
    }
    public boolean dfs(TreeNode A, TreeNode B){
        //这里如果B为空,说明B已经访问完了,确定是A的子结构
        if(B == null) return true;
        //如果B不为空A为空,或者这两个节点值不同,说明B树不是A的子结构,直接返回false
        if(A == null|| A.val != B.val ) return false;
        // 若两个节点的值不同 则B不可能是A的子树 相同则比较两个节点的孩子节点是否相同
        return dfs(A.left, B.left)&& dfs(A.right, B.right);
    }
}

你可能感兴趣的:(力扣刷题记录,java,力扣,数据结构)