emmm,不刷樹的題目還真以爲自己有多瞭解樹。。。題目一做,方紙上得來終覺淺啊,才知道自己的淺薄和學海的浩瀚。
那,我們來看看這次遇到些什麼問題
前情概要
在做樹相關題目時,我覺得基礎還是要先牢靠一點的:
二叉樹的前中後序遍歷
題目(1):探尋樹的深度
給定一個二叉樹,找出其最大深度。
二叉樹的深度爲根節點到最遠葉子節點的最長路徑上的節點數。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定二叉樹 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
題目(2):驗證二叉搜索樹
給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。
假設一個二叉搜索樹具有如下特徵:
節點的左子樹只包含小於當前節點的數。
節點的右子樹只包含大於當前節點的數。
所有左子樹和右子樹自身必須也是二叉搜索樹。
示例 1:
輸入:
2
/ \
1 3
輸出: true
示例 2:
輸入:
5
/ \
1 4
/ \
3 6
輸出: false
解釋: 輸入爲: [5,1,4,null,null,3,6]。
根節點的值爲 5 ,但是其右子節點值爲 4 。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/validate-binary-search-tree
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
題目(3):驗證對稱樹
給定一個二叉樹,檢查它是否是鏡像對稱的。
例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的:
1
/ \
2 2
\ \
3 3
進階:
你可以運用遞歸和迭代兩種方法解決這個問題嗎?
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/symmetric-tree
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
我的題解(1)
這題實在簡單的不想說什麼了,連流程圖我都不想畫,直接上代碼吧。
int maxDepth(TreeNode* root) {
if(root == NULL)
return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
簡單吧,三行代碼的事情。。。
或者說,一行代碼的事情而已。。。
我的題解(2)
這題真的打破了我對樹的幻想。我一直以爲二叉搜索樹我還挺熟的,沒想到,居然處處碰壁。。。
剛開始我採用層序遍歷的方式,一層層比對,(當前節點小於當前右節點,大於左節點),但是很快就發現問題了。
如果覺得上面的那個方式沒錯,那麼看完這張圖就不會這麼想了吧。
當時看到這個情況,我也是整個人都不好了,不僅僅要和子節點比對,孫子節點還要防着。。。
經過一早上的掙扎,我放棄了前序遍歷,果然是我基礎不牢,這個模型其實可以用中序遍歷來弄,可惜我就浪費了幾個小時沒想到。
對一顆正常的二叉搜索樹,用中序遍歷打印出來的節點順序是嚴格從一端到另一端的。
當時也想着直接取值比對,但終究是遞歸也不太熟,最後只好把值都放到數組裏在遍歷數組比對了。
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
bool MidOrderTraverse(TreeNode* T, vector<int>& temp)
{
if (T == NULL)
return true;
MidOrderTraverse(T->left,temp);
temp.push_back(T->val);
cout << T->val << " ";
MidOrderTraverse(T->right,temp);
for (int i = 0; i < temp.size()-1; i++)
{
int j = i + 1;
if (temp[i] >= temp[j])
return false;
}
return true;
}
bool isValidBST(TreeNode* T)
{
vector<int> temp;
int k = MidOrderTraverse(T, temp);
return k;
}
最後就做成這個樣子了。
我的題解(3)
經過第二題,我對遞歸又深入瞭解了一點,知道該把一些希望固定住的值固定住,可以用參數形式,也可以用static.
那這題其實就很簡單了,只要把樹從根節點左右對半開,然後比較左右兩顆子樹就好,比較子樹的話,前中後序都可以,不過需要做一點小改變:
我用的是正反前序遍歷同步的方法
int Pre(TreeNode* Tl, TreeNode* Tr,int flag) //flag爲標誌退出循環
{
if (flag == 0)
return false;
if (Tl == NULL)
{
if(Tr == NULL)
return true;
return false;
}
if (Tr == NULL)
return false;
if (Tl->val != Tr->val)
{
flag = 0;
return false;
}
flag = Pre(Tl->left,Tr->right,flag);
if (flag == 0)
return false;
flag = Pre(Tl->right,Tr->left,flag);
if (flag == 0)
return false;
return true;
}
bool isSymmetric(TreeNode* root)
{
//先排除極端情況
if (root == NULL)
return true;
if (root->left == NULL)
{
if (root->right != NULL)
return false;
return true;
}
if (root->right == NULL)
return false;
if (root->left->val != root->right->val)
return false;
int ret = Pre(root->left, root->right,1);
return ret;
}
看着挺多的,其實四分之三的篇幅都用在了對特殊情況的剔除上。真正匹配的代碼是這裏:
int Pre(TreeNode* Tl, TreeNode* Tr,int flag) //flag爲標誌退出循環
{
if (flag == 0)
return false;
flag = Pre(Tl->left,Tr->right,flag);
if (flag == 0)
return false;
flag = Pre(Tl->right,Tr->left,flag);
if (flag == 0)
return false;
return true;
}
官方題解(1)略
官方題解(2)
方法一: 遞歸
上述思路可以用遞歸法實現。首先將結點的值與上界和下界(如果有)比較。然後,對左子樹和右子樹遞歸進行該過程。
class Solution {
public boolean helper(TreeNode node, Integer lower, Integer upper) {
if (node == null) return true;
int val = node.val;
if (lower != null && val <= lower) return false;
if (upper != null && val >= upper) return false;
if (! helper(node.right, val, upper)) return false;
if (! helper(node.left, lower, val)) return false;
return true;
}
public boolean isValidBST(TreeNode root) {
return helper(root, null, null);
}
}
> 作者:LeetCode
> 鏈接:https://leetcode-cn.com/problems/validate-binary-search-tree/solution/yan-zheng-er-cha-sou-suo-shu-by-leetcode/ 來源:力扣(LeetCode) 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
複雜度分析
時間複雜度 : O(N)。每個結點訪問一次。
空間複雜度 : O(N)。我們跟進了整棵樹。
官方題解(3)
和我的差不多,迭代的就不說了。
總結
樹!!!
我也不多說,心照不宣