Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node's descendants. The tree s could also be considered as a subtree of itself.
Example 1:
Given tree s:
3 / \ 4 5 / \ 1 2Given tree t:
4 / \ 1 2Return true , because t has the same structure and node values with a subtree of s.
Example 2:
Given tree s:
3 / \ 4 5 / \ 1 2 / 0Given tree t:
4 / \ 1 2Return false .
--------------------------------------------------------------------------------------------------------------------------------
题意
求一个二叉树t是否是另一个二叉树s的子树,一个s子树由s中的一个节点以及这个节点的所有的后代组成。
思路
我的思路是:从s中节点值等于t的根节点值开始,遍历判断s的节点值与t的节点值是否相同,判断t的左或右孩子为空时,
s的左或右孩子情况,判断s的左或右孩子为空时,t的左右孩子情况。最终,测试用例只通过154个,并未全部通过。
其实,有另一种更简单、清晰的思路,就是遍历二叉树s和t,然后将他们的值拼接起来,最后再判断字符串是否包含t即可,
需要注意,这种方法需要在节点为空的时候添加具有标识的特殊字符,以便在具有相同树遍历方法的情况下,确定结构是否相同。
代码:
public class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
String spreorder = generatepreorderString(s);
String tpreorder = generatepreorderString(t);
return spreorder.contains(tpreorder) ;
}
public String generatepreorderString(TreeNode s){
StringBuilder sb = new StringBuilder();
Stack<TreeNode> stacktree = new Stack();
stacktree.push(s);
while(!stacktree.isEmpty()){
TreeNode popelem = stacktree.pop();
if(popelem==null)
sb.append(",#"); // Appending # inorder to handle same values but not subtree cases
else
sb.append(","+popelem.val);
if(popelem!=null){
stacktree.push(popelem.right);
stacktree.push(popelem.left);
}
}
return sb.toString();
}
}
这种方法号是比较长。
还有一种耗时比较短的方法:
private static boolean isSubtree(TreeNode s, TreeNode t, boolean isRoot) {
//如果节点s和节点t都为空,则返回true
if (s == null && t == null) return true;
//如果节点s和t,有一个为空,另一个不为空,则返回false;
else if (s == null || t == null) return false;
//前两个if判断是根据树的结构进行判断,下面是在结构相同的前提下,判断节点的值是否相同
else {
//如果相同
if (s.val == t.val) {
//则递归继续进行判断,如果左子树和右子树都判断完了,并且都返回的true,则返回true。
if (isSubtree(s.left, t.left, true) && isSubtree(s.right, t.right, true)) return true;
} else {
//如果节点的值不相同,如果已经开始判断是否为子树,则返回false
if (isRoot) return false;
}
//如果s的左右孩子节点值与t的根节点值不同,则继续用s的左右孩子与t的根节点值比较。
//isRoot是用来判断是否开始对其子树进行判断。
为什么用或,因为t子树有可能在s的左子树或右子树。
return isSubtree(s.left, t, isRoot) || isSubtree(s.right, t, isRoot);
}
}
其实这个方法的思路和我的思路差不多,区别就是该方法的实现更合理。
首先,该方法有递归进行左右子树判断,而我是用非递归前序的遍历方法,而且我的方法用了三个stack,一个用于保存s,一个用于保存t,另一个用于保存当s的节点值等于t的根节点值时s的节点。而上面那个方法用一个标志位isRoot替代了我第三个stack的作用。其他对结果情况判断方面,我的方法和上面的方法是一致的。