劍指offer面試18 樹的子結構

頭像是我偶像發表於2017-07-07

一、題目

輸入兩棵二叉樹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));
    }
}

相關文章