I want to find out whether binary tree T2 is a subtree of of binary tree T1. I read that one could build string representations for T2 and T1 using pre-order and in-
The problem is also from book
, at section: IX Interview Questions
| 4. Trees and graphs
| Question 4.10
.
(It's a great book by Gayle Laakmann McDowell
, a software engineer from Google, who has interviewed a lot people. )
(A) Search root & match subtree algorithm.
Complexity:
Time
O(n + m*k)
O(n2 + m2*k2)
O(n + m)
(from the other algorithm), but depends.Space: O(lg(m) + lg(n))
(Mainly taken by method stacks of recursive calls.)
Where:
n
m
k
n2
[1, n]
.m2
k2
(B) Traverse both trees to lists respectively, and find sub list.
Complexity:
Time: O(n + m)
Space: O(n + m))
Where:
n
m
Tips:
pre-order
traverse, and also need to put null node as a special value to list.in-order
traverse won't work.Compare 2 algorithms:
(Following is an implementation in Java
containing both algorithms, with test case.)
CheckSubtree.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.
* 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.
*
Return on first match.
*
* @param largeTree large tree,
* @param smallTree small tree,
* @param
* @return true if small tree is subtree of large tree, or small tree is empty,
*/
public static boolean checkViaRootAndMatch(BST largeTree, BST smallTree) {
if (smallTree.size() == 0) return true; // small tree is empty,
BST.BSTNode 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
* @return node from large tree that match small tree, or null if not found,
*/
protected static BST.BSTNode searchAndMatch(BST.BSTNode largeCurrent, BST 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 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
* @return
*/
protected static boolean match(BST.BSTNode largeCurrent, BST 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
* @return true if subtree of small is subtree of large, or subtree of small is empty,
*/
protected static boolean match(BST.BSTNode largeCurrent, BST.BSTNode 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.
* Return on first match.
*
* @param largeTree
* @param smallTree
* @param
* @return
*/
public static boolean checkViaTraverseAndSublist(BST largeTree, BST 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.
* Return on first match.
*
* @param largeTree
* @param smallTree
* @param special special value to represent value of null node in tree,
* @param
* @return
*/
public static boolean checkViaTraverseAndSublist(BST largeTree, BST smallTree, T special) {
if (smallTree.size() == 0) return true; // small tree is empty,
// tree to list,
List largeList = treeToList(largeTree, special);
List 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
* @return
*/
protected static List treeToList(BST tree, T special) {
List 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
*/
protected static void treeToList(BST.BSTNode current, T special, List 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,
}
}
CheckSubtreeTest.java
(unit test, via 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 largeTree; // large tree,
private BST smallTree; // small tree, subtree of large tree,
private BST smallTree2; // small tree, not subtree of large tree,
private BST 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));
}
}
All test cases would pass.
Tips:
BST
is a simple binary tree impl.BST.BSTNode
is BST
's node.CreateMinimalBST
is a tool that help to build a binary search tree of minimal height.