一、題目
輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
二、分析
要查找樹A中是否存在和樹B結構一樣的子樹,我們可以分成兩步:
1. 在樹A中找到和樹B的根結點的值一樣的結點R;
2. 再判斷樹A中以R爲根結點的子樹是否包含和樹B一樣的結構。
2.1 第一步
在樹A中查找與樹B根結點的值一樣的結點,實際上就是樹的遍歷。我們採用遞歸來實現,遞歸的終止條件是樹A遍歷結點。
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
boolean result = false;
if (root1 == null || root2 == null) {
return result;
}
if (root1.val == root2.val)
result = isTheSame(root1, root2);
if (!result)
result = HasSubtree(root1.left, root2);
if (!result)
result = HasSubtree(root1.right, root2);
return result;
/*
* 上面三個if可以用一行代碼表示: return isTheSame(root1, root2) ||
* HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
*/
}
2.2 第二步
判斷樹A中以R爲根結點的子樹是不是和樹B具有相同的結構。同樣,我們也可以用遞歸的思路來考慮:如果結點R的值和樹B的根結點不相同,則以R爲根結點的子樹和樹B肯定不具有相同的結點;如果它們的值相同,則遞歸地判斷它們各自的左右結點的值是否相同。遞歸的終止條件是到達了樹A或樹B的葉結點。
public boolean isTheSame(TreeNode root1, TreeNode root2) {
if (root2 == null) {//如果遞歸到達樹B的葉結點,則表示存在這樣的子樹
return true;
}
if (root1 == null) {//如果遞歸到達樹A的葉結點,則表示不存在這樣的子樹
return false;
}
if (root1.val != root2.val) {
return false;
}
return isTheSame(root1.left, root2.left) && isTheSame(root1.right, root2.right);
}
三、注意事項
- 一定要注意邊界條件的檢查,即檢查空指針。
- 設置遞歸調用的退出條件。
四、整體代碼
/*
* 題目
* 輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
*/
public class Test18 {
/*
* 先將二叉樹序列化,然後再比較子序列化字符串是否存在於root1表示的序列化字符串中 但是這種方法似乎不夠嚴謹,會出現錯誤
*/
public boolean HasSubtree2(TreeNode root1, TreeNode root2) {
String s1 = serialByPre(root1);
String s2 = serialByPre(root2);
return false;
}
// 用先序遍歷序列化二叉樹
public String serialByPre(TreeNode root) {
String result = null;
if (root == null) {
return "#!";
}
result = root.val + "!";
result += serialByPre(root.left);
result += serialByPre(root.right);
return result;
}
/*
* 1、先序遍歷二叉樹,比較遍歷節點也子二叉樹根節點;
* 2、如果相等,則分別比較二叉樹與子二叉樹的左右子結點是否相等,如果遇到不相等的子結點,則返回到第3步,如果都相等,則返回true
* 3、繼續遍歷二叉樹,直到遍歷完,如果還沒找到就返回。
*/
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
boolean result = false;
if (root1 == null || root2 == null) {
return result;
}
if (root1.val == root2.val)
result = isTheSame(root1, root2);
if (!result)
result = HasSubtree(root1.left, root2);
if (!result)
result = HasSubtree(root1.right, root2);
return result;
/*
* 上面三個if可以用一行代碼表示: return isTheSame(root1, root2) ||
* HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
*/
}
public boolean isTheSame(TreeNode root1, TreeNode root2) {
if (root2 == null) {//如果遞歸到達樹B的葉結點,則表示存在這樣的子樹
return true;
}
if (root1 == null) {//如果遞歸到達樹A的葉結點,則表示不存在這樣的子樹
return false;
}
if (root1.val != root2.val) {
return false;
}
return isTheSame(root1.left, root2.left) && isTheSame(root1.right, root2.right);
}
/*
* 測試用例
* 樹A: 1
/ \
2 3
/ \
4 5
樹B: 1
/ \
2 3
*/
public static void main(String[] args) {
// 二叉樹
TreeNode root1 = new TreeNode(1);
TreeNode temp = root1;
TreeNode n1 = new TreeNode(2);
TreeNode n2 = new TreeNode(3);
temp.left = n1;
temp.right = n2;
temp = temp.left;
n1 = new TreeNode(4);
n2 = new TreeNode(5);
temp.left = n1;
temp.right = n2;
// 子二叉樹
TreeNode root2 = new TreeNode(2);
temp = root2;
n1 = new TreeNode(4);
n2 = new TreeNode(5);
Test18 t = new Test18();
System.out.println(t.HasSubtree(root1, root2));
}
}