二叉樹基礎題(六):樹的子結構&二叉搜索樹的判斷&鏡像二叉樹


(尊重勞動成果,轉載請註明出處:https://yangwenqiang.blog.csdn.net/article/details/105928523
冷血之心的博客)

這段時間完成了職業生涯第一次跳槽,對算法題目有了一個更深的認識和理解,這裏將二叉樹常見的面試題目以及解法補充完善。

二叉樹基礎題(一):先序遍歷&中序遍歷&後序遍歷

二叉樹基礎題(二):分層遍歷&二叉樹深度&是否相同的樹

二叉樹基礎題(三):完全二叉樹的判斷&平衡二叉樹的判斷

二叉樹基礎題(四):對稱二叉樹的判斷&之字形分層遍歷二叉樹

二叉樹基礎題(五):二叉樹的下一個節點&最低公共祖先節點

二叉樹基礎題(六):樹的子結構&二叉搜索樹的判斷&鏡像二叉樹


今天我們再來看三道二叉樹相關的基礎題目吧,如下所示:

  • 樹的子結構
  • 二叉搜索樹
  • 鏡像二叉樹

題目一:樹的子結構

輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)

思路:
1、首先設置標誌位result = false,因爲一旦匹配成功result就設爲true,剩下的代碼不會執行,如果匹配不成功,默認返回false
2、遞歸思想,如果根節點相同則遞歸調用DoesTree1HaveTree2(),如果根節點不相同,則判斷tree1的左子樹和tree2是否相同,再判斷右子樹和tree2是否相同
3、注意null的條件,HasSubTree中,如果兩棵樹都不爲空才進行判斷,DoesTree1HasTree2中,如果Tree2爲空,則說明第二棵樹遍歷完了,即匹配成功,tree1爲空有兩種情況:

  • 如果tree1爲空&&tree2不爲空說明不匹配,
  • 如果tree1爲空,tree2爲空,說明匹配。

代碼實現如下:

public class Main {
    
    public boolean HashSubtree(TreeNode root1, TreeNode root2){
        boolean result = false;
         //當Tree1和Tree2都不爲null的時候,才進行比較。否則直接返回false
        if(root1!=null&&root2!=null){
            //如果找到了對應Tree2的根節點的點
            if(root1.val==root2.val){
                 //以這個根節點爲爲起點判斷是否包含Tree2
                result = DoesTree1haveTree2(root1,root2);
            }
             //如果找不到,那麼就再去root的左兒子當作起點,去判斷時候包含Tree2
            if(!result)
                result = HashSubtree(root1.left, root2);
            //如果還找不到,那麼就再去root的右兒子當作起點,去判斷時候包含Tree2
            if(!result)
                result = HashSubtree(root1.right, root2);
        }
        return result;
    }
    private boolean DoesTree1haveTree2(TreeNode root1, TreeNode root2) {
        //如果Tree2已經遍歷完了都能對應的上,返回true
        if(root2==null)
            return true;
        //如果Tree2還沒有遍歷完,Tree1卻遍歷完了。返回false
        if(root1==null&&root2!=null)
            return false;
        //如果其中有一個點沒有對應上,返回false
        if(root1.val!=root2.val)
            return false;
        //如果根節點對應的上,那麼就分別去子節點裏面匹配
        return DoesTree1haveTree2(root1.left, root2.left)
                &&DoesTree1haveTree2(root1.right, root2.right);
    }
}
class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}

題目二:是否是二叉搜索樹

給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。

二叉搜索樹的定義如下:

  • 節點的左子樹只包含小於當前節點的數。
  • 節點的右子樹只包含大於當前節點的數。
  • 所有左子樹和右子樹自身必須也是二叉搜索樹。

看到這裏,也許大家很easy就可以寫出如下的錯誤代碼

public boolean isValidBST(TreeNode root) {
    if (root == null) 
    	return true;
    if (root.left != null && root.val <= root.left.val) 
    	return false;
    if (root.right != null && root.val >= root.right.val) 
    return false;

    return isValidBST(root.left) && isValidBST(root.right);
}

這個代碼有問題嗎?有嗎?當然有問題了。我們的當前節點的值要大於左邊子樹的所有節點,而不僅僅是其一個左子樹節點,右邊也是同樣的道理。比如下邊所示並不是一個合法的二叉搜索樹,但是我們的算法會返回true
在這裏插入圖片描述

怎麼辦呢?

這個時候需要使用二叉樹操作中經常需要使用的方法了,重載當前的函數,傳入更多的參數,使得我們可以擁有更多的信息,最後纔是遞歸調用!

正確的代碼實現如下:

/**
* Definition for a binary tree node.
* public class TreeNode {
*     int val;
*     TreeNode left;
*     TreeNode right;
*     TreeNode(int x) { val = x; }
* }
*/
class Solution {
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root,null,null);
    }
    // 使用輔助函數,增加函數參數列表,在參數中攜帶額外信息
    private boolean isValidBST(TreeNode root, TreeNode min,TreeNode max){
        if(root==null)
            return true;
        if(min!=null&&root.val<=min.val)
            return false;
        if(max!=null&&root.val>=max.val)
            return false;
            
        return isValidBST(root.left,min,root)&&isValidBST(root.right,root,max);
    }
}

題目三:鏡像二叉樹(二叉樹的翻轉)

二叉樹的鏡像:操作給定的二叉樹,將其變換爲原二叉樹的鏡像。

思路:

這道題目還算比較簡單吧,其實也挺難的,Homebrew 的作者Max Howell面試被 Google
拒啦,因爲他不會翻轉二叉樹。是不是很悲劇?
在這裏插入圖片描述
這道題目可以給出兩個解決方法:

  • 原地交換當前節點的左右節點
  • 新建一顆二叉樹
class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}

public class Solution {
    // Mirror求出的即是當前二叉樹的鏡像
    public void Mirror(TreeNode root) {
        if (root == null)
            return;
        // 交換當前節點的左右節點
        TreeNode tmpNode = root.left;
        root.left = root.right;
        root.right = tmpNode;

        Mirror(root.left);
        Mirror(root.right);
    }

    // 獲取鏡像二叉樹(不改變原二叉樹,重新生成了一個其鏡像二叉樹)
    private  TreeNode getMirror(TreeNode pRoot) {
        if (pRoot == null) {
            return null;
        }
        TreeNode root = new TreeNode(pRoot.val);
        root.right = getMirror(pRoot.left);
        root.left = getMirror(pRoot.right);
        return root;
    }
}

總結:

二叉樹相關的題目的一個技巧就是重載一個函數用來遞歸,可以傳入額外的參數,獲取更多的信息。另外就是二叉樹的遞歸解法中,我們只要定義一個遞歸函數,並且處理好當前節點要處理的事情即可,別的都交給遞歸函數,認爲其已經給我們解決了問題,切莫在腦子裏進行壓棧和出棧的操作,因爲我們的腦子都不夠用哈~

就比如說二叉樹的鏡像算法中,我們只需要確定下遞歸結束條件,處理好當前節點的事情(交換),然後再分別遞歸左右子樹即可。

後續我會繼續更新二叉樹相關基礎題目,感興趣的同學可以持續關注交流~


注意啦,注意啦~

歡迎大家關注我的牛客專欄《Java開發崗面試題全解析》 ,點擊圖片查看詳情。

Java開發崗高頻面試題全解析,專欄共計32節,已經全部更新完畢。

專欄分9個模塊來對Java崗位面試中的知識點進行解析,包括通用面試技能,Java基礎,Java進階,網絡協議,常見框架以及算法,設計模式等。

專欄串點成面的解析每個面試題背後的技術原理,由淺入深,循序漸進,力爭讓大家掌握面試題目的背後的技術原理,摒棄背題模式的陋習。


如果對你有幫助,記得點贊哈,歡迎大家關注我的博客,關注公衆號(文強的技術小屋),學習更多技術知識,一起遨遊知識海洋~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章