劍指OFFER思路總結與代碼分享——樹篇(Java實現)

注:順序是先篩選分類再按LeeCode上的通過率排的,每題最後的總結代碼都是在LeeCode上跑過的,應該沒啥問題。但是思路中的代碼都是直接在CSDN編輯器裏徒手敲的,若有筆誤還煩請告知,蟹蟹~

55-1 二叉樹的深度

在這裏插入圖片描述
思路是使用遞歸,說到遞歸,立馬寫下如下思路:

  1. 方法想要幹什麼?
  2. 方法的終止條件是什麼?以此確定終止條件;
  3. 方法的等價關係是什麼?以此確定返回值。

立馬寫下如下三個詞:

  • 終止條件
  • 遞推關係
  • 返回值

然後開始分析:

  • 首先這個方法想要計算以入參root爲根節點的樹的深度,那麼方法的返回值即爲此樹的深度
  • 其次這個方法的終止條件是當傳入的root爲空時,返回深度爲0;
  • 最後這個方法的等價關係是root節點的深度等於:root左節點的深度與root右節點的深度相較而言更大的那個深度加上1。

分析結束,將思路轉爲代碼段在草稿紙上寫:


終止條件

if(root == null){
	return 0;
}

遞推關係

depth = Math.max(maxDepth(root.left).maxDepth(root.right)) + 1;

返回值

return dpeth;

整理成代碼:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
    }
}

27 二叉樹的鏡像

在這裏插入圖片描述
開始分析:

  • 首先這個方法要將以入參root爲根節點的樹鏡像變換;
  • 其次當root爲空的時候停止遍歷,返回null
  • 最後遞歸關係是將root左節點的鏡像放在右節點上,將root右節點的鏡像放在左節點上,即可完成root的鏡像。

終止條件

if(root == null){
	return null;
}

遞推關係

TreeNode tmp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(tmp);

返回值

return root;

整理成代碼:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode tmp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(tmp);

        return root;
    }
}

54 二叉搜索樹的第K大節點

在這裏插入圖片描述
開始分析:

  • 首先這個方法是求第K大個節點對應的值;
  • 其次當傳入節點爲null時返回,或者找到了第K大節點後返回;
  • 最後遞歸關係是使用中序遍歷的方法遍歷這顆二叉搜索樹,很關鍵的一點是看到二叉搜索樹首先想到中序遍歷,因爲二叉搜索樹的中序遍歷結果是從小到大有序的數組,具體說來就是先看右子樹有沒有第K大,沒有的話看自己是不是第K大,最後再看左子樹有沒有第K大。

知識點:看到二叉搜索樹想到中序遍歷


終止條件

if(root == null){
	return;
}
if(count == 0){
	return;
}

遞推關係

helper(root.right);
if(--count == 0){
	res = root.val;
}
helper(root.left);

返回值

//res直接寫在類內上,所有方法共用,不用返回

整理成代碼:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    private int count = 0,res = 0;
    public int kthLargest(TreeNode root, int k) {
        count = k;
        helper(root);
        return res;
    }

    private void helper(TreeNode root){
        if(root == null){
            return;
        }
        helper(root.right);
        if(count == 0){
            return;
        }
        if(--count == 0){
            res = root.val;
        }
        helper(root.left);
    }
}

32-II 從上到下打印二叉樹

在這裏插入圖片描述
開始分析:
此題需要用到層次遍歷,層次遍歷也可以被稱爲二叉樹的廣度優先遍歷BFS,說道廣度優先遍歷,就要想到我們需要維護一個隊列,通過控制節點在隊列的進出來控制遍歷的節奏。
二叉樹BFS的大致框架,更多細節在具體實現的代碼註釋中有講到:

//初始化一個隊列並把首節點放進隊列裏
Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
//當隊列不爲空的時候循環
while(!queue.isEmpty()){
	//一層層算賬
	for(int i = queue.size(); i > 0; i--){
		//先把本層節點給poll出來
		TreeNode node = queue.poll();
		//再把與本層相連的下層左右節點給塞進去
		if(node.left != null){
			queue.add(node.left);
		}
		if(node.right != null){
			queue.add(node.right);
		}
	}
}

知識點:廣度優先,先建隊列,每層poll出,下層add進。

因爲這題打印結果呈現每層單獨一個ArrayList的結果,所以要在每層的循環中開一個ArrayList接一下即可。總體框架還是能看出來的。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        //判斷特殊情況
        if(root == null){
            return new ArrayList<>();
        }

        //一個ArrayList承載最後的結果
        List<List<Integer>> res = new ArrayList<>();
        //一個Queue->LinkedList承載每一層的節點,初始化爲首層的root
        Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
        
        //若queue爲空,則說明所有的節點都處理完畢,可以輸出結果了
        //這裏用queue.isEmpty(),不能用queue==null
        while(!queue.isEmpty()){
            //緩存每層的數據,這裏用List<Integer>是爲了每層直接接上最後的結果
            List<Integer> tmp = new ArrayList<>();
            //一個for,從queue.size()開始,這很關鍵,
            //用for(int i = 0; i < queue.size(); i++)的話,則會報錯
            //因爲隊列的長度是在變化的,應該首先就指定好,這樣才能保證每次循環只出一層的數據
            for(int i = queue.size();i > 0; i--){
                //隊列先進先出,進是add出是poll
                TreeNode father = queue.poll();
                //記得先判斷是否爲空再添加進隊列中,不然會報空指針異常
                if(father.left != null){
                    //把左孩子加入進去,等待其在下一層被poll出來
                    queue.add(father.left);
                }
                //右孩子同理
                if(father.right != null){
                    queue.add(father.right);
                }
                //把poll出的節點的值存進ArrayList中
                tmp.add(father.val);
            }
            //把一層的ArrayList值存進最後的結果中
            res.add(tmp);
        }

        return res;
    }
}

07 重建二叉樹

在這裏插入圖片描述
先序遍歷:根左右
中序遍歷:左根右
在這裏插入圖片描述
1:前序遍歷的首節點爲root

int rootVal = preorder[0];

2:根據拿到的root去中序遍歷裏尋找對應的root

int rootIndex = 0;
for(int i = 0; i < inorder.length; i++){
	if(rootVal == inorder[i]){
		rootIndex = i;
		break;
	}
}

3-4:在中序遍歷中找到root之後,它的左右即爲當前root節點的左子樹和右子樹

//inorder的左邊:
Arrays.copyOfRange(inorder, 0, rootIndex);
//inorder的右邊:
Arrays.copyOfRange(inorder, rootIndex + 1, inorder.length);

注意copyOfRange的取值範圍是左閉右開,可以畫圖觀察,Java裏很多這種取值的操作都是左閉右開的,想想str.substring(閉,開);

5-6:根據我們得到的左右子樹,去前序遍歷找左右子樹的段落

//preorder的左邊:
Arrays.copyOfRange(preorder, 1, 1 + rootIndex);
//preorder的右邊:
Arrays.copyOfRange(preorder, 1 + rootIndex, preorder.length);

7-8:根據前序遍歷的特點,我們所找的段落的首節點就是直接連着root的左孩子與右孩子

root.left = buildTree(preorder的左邊,inorder的左邊);
root.right = buildTree(preorder的右邊,inorder的右邊);

至此我們就可以進行遞歸操作了。

有沒有發現樹的遞歸操作很多時候都是在root.left和root.right的時候用等於接上的,這種格式可以多多考慮:
root.left = 遞歸左子樹;
root.right = 遞歸右子樹;


整理成代碼:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        if(n == 0){
            return null;
        }
        int rootVal = preorder[0],rootIndex = 0;
        for(int i = 0; i < n; i++){
            if(rootVal == inorder[i]){
                rootIndex = i;
                break;
            }
        }
        TreeNode root = new TreeNode(rootVal);
        root.left = buildTree(Arrays.copyOfRange(preorder,1,1 + rootIndex),Arrays.copyOfRange(inorder,0,rootIndex));
        root.right = buildTree(Arrays.copyOfRange(preorder,1+rootIndex,n),Arrays.copyOfRange(inorder,rootIndex+1,n));

        return root;
    }
}

68-I 二叉搜索樹的最近公共祖先

在這裏插入圖片描述

  • 方法的作用是用來找root節點爲根節點的樹裏指定兩個節點的公共祖先;
  • 當傳入爲null時返回;
  • 根據二叉搜索樹的性質,若是p與q一個比root大一個比root小,那麼就說明root在兩個節點之間,否則就在根據root與p、q的大小來判斷左邊或者右邊。

終止條件

if(root == null){
	return null;
}

遞推關係

if(root.val > p.val && root.val > q.val){
	return lowestCommonAncestor(root.left, p, q);
}else if(root.val < p.val && root.val < q.val){
	return lowestCommonAncestor(root.right, p, q);
}

返回值

return root;

整理成代碼:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;
        }
        if(root.val > p.val && root.val > q.val){
            return lowestCommonAncestor(root.left, p, q);
        }else if(root.val < p.val && root.val < q.val){
            return lowestCommonAncestor(root.right, p, q);
        }
        return root;

    }
}

68-II 二叉樹的最近公共祖先

在這裏插入圖片描述

  • 方法的作用是看p或q在不在,若在則返回p或q,若不在則返回null
  • root == null的時候返回nullroot == p的時候返回proot == q的時候返回q
  • 當左子樹爲null說明兩個都在右邊,當右子樹爲null說明兩個都在左邊,只有左右子樹都不爲null的時候該root纔是公共祖先,返回這個root,層層上傳。

終止條件

if(root == null){
	return null;
}
if(root == p || root == q){
	return root;
}

遞推關係

TreeNode leftNode = lowestCommonAncestor(root.left,p,q);
TreeNode rightNode = lowestCommonAncestor(root.right,p,q);
if(leftNode == null){
	return rightNode;
}
if(rightNode == null){
	return leftNode;
}

返回值

return root;

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;
        }
        if(root == p || root == q){
            return root;
        }
        
        TreeNode leftNode = lowestCommonAncestor(root.left,p,q);
        TreeNode rightNode =  lowestCommonAncestor(root.right,p,q);

        if(leftNode == null){
            return rightNode;
        }
        if(rightNode == null){
            return leftNode;
        }
        return root;
    }
}

32-I 從上到下打印二叉樹

在這裏插入圖片描述
這題爲啥會比32-II做出來的人少…我蒙了,就是廣度優先遍歷的板子就完事了。


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] levelOrder(TreeNode root) {
    	//特殊邊界要提前考慮到
        if(root == null){
            return new int[0];
        }
        LinkedList<TreeNode> queue = new LinkedList<>(){{add(root);}};
        ArrayList<Integer> res = new ArrayList<>();
        while(!queue.isEmpty()){
            for(int i = queue.size(); i > 0; i--){
                TreeNode tmp = queue.poll();
                res.add(tmp.val);
                if(tmp.left != null){
                    queue.add(tmp.left);
                }
                if(tmp.right != null){
                    queue.add(tmp.right);
                }
            }
        }
        //這裏返回值是int[],再遍歷一遍傳過去吧,卑微
        int[] n = new int[res.size()];
        for(int j = 0; j < res.size(); j++){
            n[j] = res.get(j);
        }
        return n; 
    }
}

32-III 從上到下下打印二叉樹

在這裏插入圖片描述
你要是一路看下來的話這玩意是第三個BFS題了,加個reverse就完事了。這題我們來嘮嘮返回值的事,這個方法讓我們返回的是List<List<Integer>>這麼個玩意,當root == null的時候,我們應該怎麼看返回個啥呢?答案是看它最外層是啥,它最外層是個List<>,那我們就

if(root == null){
	return ArrayList<>();
	//return LinkedList<>();一樣也行
}

這玩意說難也不難,就是有時候會一下鑽牛角尖裏,不造返回啥了,寫出來防止忘記。
還有一個好玩的操作是判斷奇偶數,這麼寫其實也不會快(因爲聽說你用取餘的時候,JAVA底層已經優化成這種寫法了),但是秀呀哈哈。

//奇數
if((i & 1) == 1){
	...
}
//偶數
if((i & 1) == 0){
	...
}

最後上個代碼:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root == null){
            return new ArrayList<>();
        }
        LinkedList<List<Integer>> res = new LinkedList<>();
        LinkedList<TreeNode> queue = new LinkedList<>(){{add(root);}};//設置個層數
        int floor = 0;
        while(!queue.isEmpty()){
            ArrayList<Integer> tmp = new ArrayList<>();
            for(int i = queue.size(); i > 0; i--){
                TreeNode node = queue.poll();
                tmp.add(node.val);
                if(node.left != null){
                    queue.add(node.left);
                }
                if(node.right!= null){
                    queue.add(node.right);
                }
            }
            //當奇數層時需要翻轉
            if((floor & 1) == 1){
                Collections.reverse(tmp);
            }
            res.add(tmp);
            //層數++
            floor++;
        }
        return res;
    }
}

55-II 平衡二叉樹

在這裏插入圖片描述
emmm這題寫的有點醜,雖然最後蜜汁跑出了99.96%和100%的成績…
思路就是遞歸,感覺不用之前那麼一板一眼的分析了,有點感覺了,就直接說我怎麼想的吧。首先拿到左邊和右邊的高度,兩個一減取個絕對值,要是這個值大於1呢就說明剛好在你正在處理的root節點完球了,返回false就完事了,不然呢,就返回左子樹有沒有完球 && 右子樹有沒有完球。還有一個問題是我們怎麼知道左邊和右邊的高度是多少呢?再寫個函數,像“55-1 二叉樹的深度”那題那樣遞歸一下就完事了。
所以我寫了兩個遞歸咋跑到99.96%的?我看大佬們寫的比我簡潔多了。


我好像發現了做遞歸題的idea:
你:算法之神,你只要告訴我前一次_______,我就能給你整出我這個________。
算法之神:小夥子牛逼哦,那我給你前一次__________的結果,你算吧。
你:現在只要把前一次_________xxxxx,然後xxxxx,最後返回的_______就是我這個________了,牛逼不。
算法之神:牛逼哦~


你:算法之神,你只要告訴我前一個節點是不是平衡二叉樹,我就能給你整出我這個節點是不是平衡二叉樹。
算法之神:小夥子牛逼哦,那我給你前一個節點是不是平衡二叉樹的結果,你算吧。
你:現在只要看左邊是不是平衡二叉樹,再看右邊是不是平衡二叉樹,要是兩邊都平衡,那我這個節點就是平衡二叉樹,要是有一邊不是,那我這個節點就不是平衡二叉樹。牛逼不。
算法之神:牛逼哦~


順便記一下絕對值方法:

Math.abs(i);

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
    	//空樹肯定平衡
        if(root == null){
            return true;
        }
        //拿左右子樹高度
        int leftNode = treeHeight(root.left);
        int rightNode = treeHeight(root.right);
        //瞅瞅自己這個節點上兩個差有沒有大於1,大於了就麻溜的false
        if(Math.abs((leftNode - rightNode)) > 1){
            return false;
        }
        //瞅瞅左邊,瞅瞅右邊
        return isBalanced(root.left) && isBalanced(root.right);
    }

    public int treeHeight(TreeNode root){
   		//空了高度就是0了
        if(root == null){
            return 0;
        }
        //左邊高度和右邊高度拿到
        int leftHeight = treeHeight(root.left);
        int rightHeight = treeHeight(root.right);
    	//現在的高度就是左右高度高的那嘎達加1
        return Math.max(leftHeight,rightHeight) + 1;
    }
}

28 對稱的二叉樹

在這裏插入圖片描述
在這裏插入圖片描述
鏡像就是左邊的左邊等於右邊的右邊,左邊的右邊等於右邊的左邊。
我們造一個遞歸方法,表示該樹是否鏡像,只要告訴我左邊的左邊和右邊的右邊是不是鏡像,左邊的右邊和右邊的左邊是不是鏡像,我就能告訴你當前節點是不是鏡像,我只要再判斷當前節點的左右是一樣的即可。
判斷左右節點一樣之前要保證他們都不是null或者都是null,不然會報空指針,用這樣的模板即可:

//先判斷左右是不是都是null
if(left == null && right == null){
	return true;
}
//已經排除左右都是null的情況下,任意一邊爲null都說明left和right不相等
if(left == null || right == null){
	return false;
}

代碼如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        }
        return helper(root.left,root.right);
    }

    private boolean helper(TreeNode left,TreeNode right){
        if(left == null && right == null){
            return true;
        }
        if(left == null || right == null){
            return false;
        }
        //除去null,還有一種false是左右不相等
        if(left.val != right.val){
            return false;
        }
        //算法之神告訴了我左左右右和左右右左是不是鏡像
        //如果左右相等,那本節點的結果就是 左左右右 && 左右右左 的結果
        return helper(left.left,right.right) && helper(left.right,right.left);
    }
}

34 二叉樹中和爲某一值的路徑

在這裏插入圖片描述
這題是傳說中的回溯(麼?我也不知道),我死在淺拷貝還是深拷貝上了,絕望調試了半天…這地方是個深坑,沒踩過十有八九是會忽略的,這題就來嘮嘮這玩意。
先說整體思路吧,一個List<List<Integer>>來接最後的結果,一個List<Integer>來接(這裏插一句,看我頂上一開始申明的LinkedList<Integer>就知道我想拿棧來做這玩意了,結果發現棧做出來結果是倒序的它不認,加個Collections.reverse()直接給我幹掉2ms,拉倒吧還是用隊列做吧。),然後就是一個遞歸函數。
嘮嘮這個遞歸函數,首先碰到null返回,其次在本節點中的任務是先把本節點的值加在統計本條線路的值中,然後把本節點塞進隊列裏,再去查看本節點是不是葉子,如果是葉子的話就看和我們想要的值是否相等,相等就深拷貝一下我們記錄的這條線路,然後把本節點的值減掉,把本節點吐出來;要不是葉子節點就向左向右遞歸,注意遞歸一路彈回來到這之後還是要把本節點吐出來的,所以不如直接把吐出來放在最後,少打一句話舒坦三十秒。
提一嘴這個函數,用隊列的時候可以用上:

LinkedList<Integer> linkedlist = new LinkedList<>();
linkedlist.removeLast();	//刪除隊列的最後一個元素

先看代碼再說坑,代碼如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    List<List<Integer>> res;
    LinkedList<Integer> ways;
    int sum = 0;
    
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        if(root == null){
            return new ArrayList<>();
        }
        //前期準備
        res = new ArrayList<>();
        ways = new LinkedList<>();
        this.sum = sum;
        //調用遞歸方法
        helper(root,0);
        return res;        
    }

    private void helper(TreeNode root,int waySum){
        if(root == null){
            return;
        }
        //本節點到了,先給他把值加上,塞隊裏
        waySum += root.val;
        ways.add(root.val);
        //瞅瞅本節點是不是葉子節點啊,是葉子的話總值對不對啊
        if(waySum == sum && root.left == null && root.right == null){
        	//深拷貝的牌面還是要有的,把當前路徑深拷貝到結果隊列裏
            res.add(new LinkedList<>(ways));
        }else{
        	//不是葉子,下面還有節點,藥不能停,向左向右繼續
            helper(root.left,waySum);
            helper(root.right,waySum);
        }
        //把最後一位移除收尾,對於葉子來說是把葉子移出;
        //對於非葉子來說是遞歸結束的時候彈到本節點時把本節點移出
        ways.removeLast();
        
    }
}

然後就要說拷貝這事了,我一開始在葉子節點那是這麼寫的:

if(waySum == sum && root.left == null && root.right == null){
    res.add(ways);
}

然後就麻了,得到的結果是這樣的:

[[],[]]

我尋思找出來路徑的個數也對了啊,怎麼不顯示路徑呢,我也add(ways)了啊。最後發現我這樣寫是淺拷貝,拷貝的只是指向ways的一個指針,所以ways一會add(root.val)的一會removeLast()的,我想輸出的值也在跟着變了。

淺拷貝(Shallow Copy):
①對於數據類型是基本數據類型的成員變量,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的對象。因爲是兩份不同的數據,所以對其中一個對象的該成員變量值進行修改,不會影響另一個對象拷貝得到的數據。
②對於數據類型是引用數據類型的成員變量,比如說成員變量是某個數組、某個類的對象等,那麼淺拷貝會進行引用傳遞,也就是隻是將該成員變量的引用值(內存地址)複製一份給新的對象。因爲實際上兩個對象的該成員變量都指向同一個實例。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值。

所以這時候要深拷貝一下,直接新建一個LinkedList<>()然後把ways裏的值拷貝過去就完事了。

if(waySum == sum && root.left == null && root.right == null){
    res.add(new LinkedList<>(ways));
}

深拷貝(Deep Copy):
相對於淺拷貝而言,對於引用類型的修改,並不會影響到對應的copy對象的值。
每一層的每個對象都進行淺拷貝=深拷貝。

來做個總結:

以後出現結果個數正確、內容錯誤或消失的時候,可以考慮是否是淺拷貝導致的。

37 序列化二叉樹

在這裏插入圖片描述
思路是用BFS做序列化和反序列化,主要還是細心,很多小細節調了不少次,還有就是Sting和Integer的轉換注意下,最後注意一下String判相等還是用equals()吧,怎麼着都方便點。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null){
            return "";
        }
        Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        while(!queue.isEmpty()){
            for(int i = queue.size(); i > 0; --i){
                TreeNode tmp = queue.poll();
                if(tmp != null){
                    sb.append(tmp.val + ",");
                    queue.add(tmp.left);
                    queue.add(tmp.right);
                }else{
                    sb.append("null,");
                }
            }
        }
        sb.append("]"); 
        return sb.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data == "" ){
            return null;
        }
        data = data.substring(1,data.length() - 1);
        String[] strs = data.split(",");
        int index = 0;
        TreeNode root = new TreeNode(Integer.parseInt(strs[0]));
        Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
        index++;
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(!strs[index].equals("null")){
                node.left = new TreeNode(Integer.parseInt(strs[index]));
                queue.add(node.left);
            }
            index++;
            if(!strs[index].equals("null")){
                node.right = new TreeNode(Integer.parseInt(strs[index]));
                queue.add(node.right);
            }
            index++;
        }
        return root;
    }

}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

26 樹的子結構

在這裏插入圖片描述
首先需要遞歸的是從本層節點開始,是否有B樹的子結構,這個需要一個對比是否是子結構的遞歸,然後我們還需要一個遞歸是從本層節點的左右節點開始,是否有B樹的子結構,所以這題是雙重遞歸。


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null){
            return false;
        }
        //遞歸A的節點
        return helper(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
    }
	
	//遞歸A某節點向下的樹與B樹是否一致
    private boolean helper(TreeNode A, TreeNode B){
        if(B == null){
            return true;
        }
        if(A == null){
            return false;
        }
        return A.val == B.val && helper(A.left,B.left) && helper(A.right,B.right);
    }
}

到此爲止樹的部分就結束啦~求個關注啵啵啵(●´З`●)

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