[leetcode日记]572.另一个子树

题目

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s
的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

示例 1: 给定的树 s:

   3
  / \    
 4   5   
/ \  
1   2 

给定的树 t:

   4   
  / \ 
 1   2 

返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。

示例 2: 给定的树 s:

    3
   / \    
  4   5  
 / \  
 1   2
     /    
    0 

给定的树 t:

4   
/ \  
1   2 

返回 false。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subtree-of-another-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析

这道题目虽然难度只有简单,但是如何达到一个比较理想的效率还是颇费一番功夫的。因为是要比较一棵树,需要保证左右子树都相等。读完题目,我初步有这么几个想法。

  1. 暴力搜索,只需要遍历树s中每一个节点,对每一个节点与t比较,就可以不落下地找到每一个满足条件的子树的根节点。这种方法肉眼可见得复杂,走投无路的时候才会尝试,不过我估计八成会超时,重复计算量有点大。有两个优化点:当某节点和t不相同的时候可以直接回溯,进行下一节点的比较(这个思想应该比较正常);当树的深度小于t的时候剩余部分可以直接剪枝(感觉需要标记层数,比较难以实现)。
  2. 利用前序遍历等方法,把树的结构扁平化。初步设想是将s和t转化成一个前序遍历的数组,然后比较两个数组。这种方法具有可用性,是因为一棵树的所有节点必然是相邻的;若存在相同和t相同的子树,那么s的数组内一定会有一个t数组。但是存在一个问题,前序遍历在s中的相邻的两个节点实际上却不一定相邻,就算数组s包含数组t也不能说明s中一定包含和t相同的子树。
  3. 递归。比较s和t的值,然后递归比较s左右子树和t的左右子树。也可以像暴力搜索那样剪枝。下面的第一个代码也是这个思路。

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

bool compare (struct TreeNode* p1,struct TreeNode* p2){
    if(!p1&&!p2) return true;		//两个节点都是空的返回true
    if(!p1||!p2) return false;		//一空一不空返回false
    if(p1->val!=p2->val) return false;
    return  compare(p1->left,p2->left)&&compare(p1->right,p2->right);
}

bool isSubtree(struct TreeNode* s, struct TreeNode* t){
	if(t) return true;
    if(!s) return false;
    return compare(s,t)||isSubtree(s->left,t)||isSubtree(s->right,t);
}

运行结果

运行结果
效率只能击败22.9%,看来还需要加把劲。

再分析&&代码

依照这个思路,我的代码不大幅度改动的话,应该怎么优化呢?我觉得isSubtree函数已经不能够更简单了,能减少compare函数里的语句吗?其实也不太行了,但是好像能把第二、三条if语句放进return里,具体如下:

bool compare (struct TreeNode* p1,struct TreeNode* p2)
{
    if(!p1&&!p2) return true;
    // if(!p1||!p2) return false;
    // if(p1->val!=p2->val) return false;
    return p1 && p2 && p1->val == p2->val && compare(p1->left,p2->left) && compare(p1->right,p2->right);
}

bool isSubtree(struct TreeNode* s, struct TreeNode* t){
    if(!s) return false;
    return compare(s,t)||isSubtree(s->left,t)||isSubtree(s->right,t);
}

再尝试运行结果

在这里插入图片描述
结果提升了不少,但是我感觉这可能已经是这种比较粗糙的递归方法的极限了。
为什么说粗糙呢?因为当节点p1的深度小于节点p2深度的时候,其实已经完全没有继续比较的必要了,可以直接return false。那应该怎么办呢?

……半个小时过去了,我啥也没有想出来。感觉额外增加的开销实在是太大了。
于是我果断转战评论区,试图找出大家的优秀代码,我一个一个试了过去,发现有一个代码跑出了比较好的结果……我直接把那位老哥的代码复制过来了:

一位老哥的代码

//老哥的代码
bool isSametree(struct TreeNode* s, struct TreeNode* t)
{
if((s == NULL)&&(t == NULL))
return true;
if((s == NULL)||(t == NULL))
return false;
if(s->val == t->val)
return isSametree(s->left, t->left) && isSametree(s->right, t->right);
else
return false;
}
bool isSubtree(struct TreeNode* s, struct TreeNode* t){
if(s == NULL)
return false;
else
return isSametree(s, t) || isSubtree(s->left, t) || isSubtree(s->right, t);
}

作者:p8salon
链接:https://leetcode-cn.com/problems/subtree-of-another-tree/solution/di-gui-pan-duan-shi-xiang-tong-de-shu-yi-ding-shi-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

没错,这位老哥的代码没有任何格式,没有写任何缩进……但是效率却达到了惊人的99.54%:

老哥运行结果

在这里插入图片描述

//整容代码(我真的嫌老哥的代码太丑,稍微缩进整容了一下)
bool isSametree(struct TreeNode* s, struct TreeNode* t)
{
    if((s == NULL)&&(t == NULL))
        return true;
    if((s == NULL)||(t == NULL))
        return false;
    if(s->val == t->val)
        return isSametree(s->left, t->left) && isSametree(s->right, t->right);
    else
        return false;
}
bool isSubtree(struct TreeNode* s, struct TreeNode* t){
    if(s == NULL)
        return false;
    else
        return isSametree(s, t) || isSubtree(s->left, t) || isSubtree(s->right, t);
}

仔细看看技术细节……貌似没有和我写的差很多啊……

这题虽然解出来了,但是总感觉差了点味道,我也不明白为什么我的代码效率会比这位老哥的要低。可能又是因为什么编译原因吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邵政道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值