我想知道二叉树T2是否是二叉树T1的子树。我读到可以使用预订和有序遍历为T2和T1构建字符串表示,如果T2字符串是T1字符串的子字符串,则T2是T1的子树。
我对这种方法感到有点困惑,不确定它的正确性。
来自wiki:“树的子树T是由T中的节点及其在T中的所有后代组成的树。”
在以下示例中:
T2:
1
/ \
2 3
T1:
1
/ \
2 3
\
4
如果我们为T2和T1构建字符串:
预购T2:“1,2,3”
预购T1:“1,2,3,4”
T2:“2,1,3”
inorder T1:“2,1,3,4”
T2字符串是T1的子字符串,因此使用上述子字符串匹配方法,我们应该得出结论T2是T1的子树。
但是,根据定义,T2不应该是T1的子树,因为它没有T1根节点的所有后代。
有一个相关的讨论here,似乎总结出这个方法是正确的。
我在这里错过了什么吗?
答案 0 :(得分:8)
非常有趣的问题。你似乎是正确的。我想你提到的问题是由math (graph theory)和计算机科学中不同的子树定义引起的。在图论中,T2是T1的适当子树。
答案 1 :(得分:7)
假设你从“Cracking the Coding Interview”这本书中得到了这一点,作者还提到要区分具有相同值的节点,还应该打印出空值。
这也可以解决你对子树定义的问题(正如书中所描述的那样正确)
预购T2:“1,2,null,null,3,null,null” 预订T1:“1,2,null,null,3,null,4,null,null” inorder T2:“null,2,null,1,null,3,null” inorder T1:“null,2,null,1,null,3,null,4,null”
如您所见,T2不是T1的子树
答案 2 :(得分:0)
树的子树的定义应该与字符串的子串的定义相同。可以这样想: 1.子字符串具有开始,包含和结束。 2.树也应该具有相同的定义,但是通用化以适应树数据结构。 3.泛化是从1维的字符串到2维的树。
答案 3 :(得分:0)
我正在阅读同一本书,并对其解决方案表示怀疑。我提出了另一个反例,它不属于用户icepack提到的潜在解释(不一定需要所有后代的子树)。
考虑以下树
T2:
B
/ \
A C
T1:
C
/ \
B C
/
A
预购T2:'BAC'
预购T1:'CBAC'
T2:'ABC'
inorder T1:'ABCC'
同样,T2字符串是其T1对应物的子串,但T2绝不是T1的子树。也许在作者已经排除重复,并特别提到他对子树的定义它可能是正确的,但遗漏这些信息使我们别无选择,只能认为它是一个不正确的解决方案。
答案 4 :(得分:0)
问题也出在书<Cracking the coding interview 6th>
的{{1}}部分| IX Interview Questions
| 4. Trees and graphs
。
(这是Google的一位软件工程师Question 4.10
写的一本好书,他采访了很多人。)
(A)搜索根和匹配子树算法。
复杂度:
时间
Gayle Laakmann McDowell
O(n + m*k)
O(n2 + m2*k2)
(来自其他算法),但要视情况而定。空格:O(n + m)
(主要由递归调用的方法堆栈使用。)
位置:
O(lg(m) + lg(n))
n
m
k
n2
之间。[1, n]
m2
(B)将两棵树分别遍历到列表,并找到子列表。
复杂度:
时间:k2
空格:O(n + m)
位置:
O(n + m))
n
提示:
m
遍历,还需要将null节点作为特殊值放入列表中。pre-order
遍历不起作用。比较2种算法:
(以下是in-order
中的一种实现,其中包含两者算法和测试用例。)
CheckSubtree.java
Java
CheckSubtreeTest.java
(单元测试,通过import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Given 2 binary tree T1 & T2, they are very large, and T1 is much larger than T2.
* <p>Check whether T2 is a subtree of T1,
*
* @author eric
* @date 2/9/19 1:48 PM
*/
public class CheckSubtree {
/**
* Check whether small tree is subtree of large tree, via searching root & match.
* <p>Return on first match.
*
* @param largeTree large tree,
* @param smallTree small tree,
* @param <T>
* @return true if small tree is subtree of large tree, or small tree is empty,
*/
public static <T extends Comparable> boolean checkViaRootAndMatch(BST<T> largeTree, BST<T> smallTree) {
if (smallTree.size() == 0) return true; // small tree is empty,
BST.BSTNode<T> matchNode = searchAndMatch(largeTree.getRoot(), smallTree); // search root & try match,
return matchNode != null;
}
/**
* Search root, and check whether the subtree there match small tree.
*
* @param largeCurrent subtree of large tree,
* @param smallTree small tree,
* @param <T>
* @return node from large tree that match small tree, or null if not found,
*/
protected static <T extends Comparable> BST.BSTNode<T> searchAndMatch(BST.BSTNode<T> largeCurrent, BST<T> smallTree) {
if (largeCurrent == null) return null; // subtree of large is empty,
T smallRoot = smallTree.getRoot().getValue(); // value of small tree's root,
if (largeCurrent.getValue().compareTo(smallRoot) == 0) { // found root of small tree,
if (match(largeCurrent, smallTree)) return largeCurrent; // also match the whole small tree,
}
BST.BSTNode<T> leftFoundNode = searchAndMatch(largeCurrent.getLeft(), smallTree); // search in left subtree,
if (leftFoundNode != null) return leftFoundNode; // match in left subtree of current,
return searchAndMatch(largeCurrent.getRight(), smallTree); // search in right subtree,
}
/**
* Check whether small tree match at given subtree of large tree.
*
* @param largeCurrent subtree of large tree,
* @param smallTree small tree,
* @param <T>
* @return
*/
protected static <T extends Comparable> boolean match(BST.BSTNode<T> largeCurrent, BST<T> smallTree) {
return match(largeCurrent, smallTree.getRoot());
}
/**
* Check whether subtree of small tree match at given subtree of large tree.
*
* @param largeCurrent subtree of large tree,
* @param smallCurrent subtree of small tree,
* @param <T>
* @return true if subtree of small is subtree of large, or subtree of small is empty,
*/
protected static <T extends Comparable> boolean match(BST.BSTNode<T> largeCurrent, BST.BSTNode<T> smallCurrent) {
if (smallCurrent == null) return true; // smaller reach leaf,
if (largeCurrent == null) return false; // larger is empty, while smaller is not,
if (largeCurrent.getValue().compareTo(smallCurrent.getValue()) != 0)
return false; // current value is different,
if (!match(largeCurrent.getLeft(), smallCurrent.getLeft())) return false; // check whether left subtree match,
return match(largeCurrent.getRight(), smallCurrent.getRight()); // check whether right subtree match,
}
// traverse both tree and generate a list representation, then check whether the small list is sub list of large list,
/**
* Check whether small tree is subtree of large tree, via traverse tree to list & find sublist. Use given null value.
* <p>Return on first match.
*
* @param largeTree
* @param smallTree
* @param <T>
* @return
*/
public static <T extends Comparable> boolean checkViaTraverseAndSublist(BST<T> largeTree, BST<T> smallTree) {
return checkViaTraverseAndSublist(largeTree, smallTree, null);
}
/**
* Check whether small tree is subtree of large tree, via traverse tree to list & find sublist. Use given special value.
* <p>Return on first match.
*
* @param largeTree
* @param smallTree
* @param special special value to represent value of null node in tree,
* @param <T>
* @return
*/
public static <T extends Comparable> boolean checkViaTraverseAndSublist(BST<T> largeTree, BST<T> smallTree, T special) {
if (smallTree.size() == 0) return true; // small tree is empty,
// tree to list,
List<T> largeList = treeToList(largeTree, special);
List<T> smallList = treeToList(smallTree, special);
// System.out.printf("large list: %s\n", largeList);
// System.out.printf("small list: %s\n", smallList);
int idx = Collections.lastIndexOfSubList(largeList, smallList); // find sublist,
return idx >= 0;
}
/**
* Traverse tree and add nodes to list, with pre-order, use special value to represent null node.
*
* @param tree
* @param special special value to represent value of null node in tree,
* @param <T>
* @return
*/
protected static <T extends Comparable> List<T> treeToList(BST<T> tree, T special) {
List<T> list = new LinkedList<>();
treeToList(tree.getRoot(), special, list);
return list;
}
/**
* Traverse subtree and add nodes to list, with pre-order, use special value to represent null node.
*
* @param current
* @param special special value to represent value of null node in tree,
* @param list
* @param <T>
*/
protected static <T extends Comparable> void treeToList(BST.BSTNode<T> current, T special, List<T> list) {
if (current == null) {
list.add(special); // represent null with special value,
return;
}
list.add(current.getValue()); // current,
treeToList(current.getLeft(), special, list); // left subtree,
treeToList(current.getRight(), special, list); // right subtree,
}
}
)
TestNG
所有测试用例都会通过。
提示:
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/**
* CheckSubtree test.
*
* @author eric
* @date 2/9/19 4:18 PM
*/
public class CheckSubtreeTest {
private int n = 10;
// trees, via minimal BST,
private BST<Integer> largeTree; // large tree,
private BST<Integer> smallTree; // small tree, subtree of large tree,
private BST<Integer> smallTree2; // small tree, not subtree of large tree,
private BST<Integer> emptyTree; // empty tree,
@BeforeMethod
public void init() {
// init - large tree,
largeTree = CreateMinimalBST.createRangeNum(0, n);
// init - small tree,
smallTree = CreateMinimalBST.createRangeNum(8, 10);
smallTree2 = CreateMinimalBST.createRangeNum(2, 5);
// init empty BST,
emptyTree = new BST<>();
}
// checkViaRootAndMatch(),
@Test
public void testViaRootAndMatch() {
Assert.assertTrue(CheckSubtree.checkViaRootAndMatch(largeTree, smallTree)); // subtree,
Assert.assertFalse(CheckSubtree.checkViaRootAndMatch(largeTree, smallTree2)); // not subtree,
Assert.assertFalse(CheckSubtree.checkViaRootAndMatch(smallTree, largeTree)); // not subtree,
// empty tree,
Assert.assertTrue(CheckSubtree.checkViaRootAndMatch(largeTree, emptyTree));
Assert.assertTrue(CheckSubtree.checkViaRootAndMatch(smallTree, emptyTree));
Assert.assertTrue(CheckSubtree.checkViaRootAndMatch(emptyTree, emptyTree));
}
// checkViaTraverseAndSublist(),
@Test
public void testViaTraverseAndSublist() {
// Integer special = null;
// Integer special = Integer.MAX_VALUE;
Assert.assertTrue(CheckSubtree.checkViaTraverseAndSublist(largeTree, smallTree)); // subtree,
Assert.assertFalse(CheckSubtree.checkViaTraverseAndSublist(largeTree, smallTree2)); // not subtree,
Assert.assertFalse(CheckSubtree.checkViaTraverseAndSublist(smallTree, largeTree)); // not subtree,
// empty tree,
Assert.assertTrue(CheckSubtree.checkViaTraverseAndSublist(largeTree, emptyTree));
Assert.assertTrue(CheckSubtree.checkViaTraverseAndSublist(smallTree, emptyTree));
Assert.assertTrue(CheckSubtree.checkViaTraverseAndSublist(emptyTree, emptyTree));
}
}
是一个简单的二叉树实现。BST
是BST.BSTNode
的节点。BST
是一个工具,可帮助构建最小高度的二进制搜索树。答案 5 :(得分:-2)
Na ......这种方法不正确。因为不同的树可以有相同的遍历。例如,在给定的示例中,树是
26
/ \
10 3
/ \ \
4 6 3
\
30
候选子树
10
/ \
4 6
\
30
和
30
/ \
4 10
\
6
具有与4,30,10,6相同的顺序遍历 但第二个不是子树