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

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

对称二叉树

力扣 101. 对称二叉树

原题链接
在这里插入图片描述

递归解法

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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问题。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问题的关键。需要仔细分析题目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值