1、235. 二叉搜索樹的最近公共祖先
給定一個二叉搜索樹, 找到該樹中兩個指定節點的最近公共祖先。
最近公共祖先的定義為:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示為一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度盡可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉搜索樹: root = [6,2,8,0,4,7,9,null,null,3,5]
_______6______ / \ ___2__ ___8__ / \ / \ 0 _4 7 9 / \ 3 5
示例 1:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 輸出: 6 解釋: 節點2
和節點8
的最近公共祖先是6。
示例 2:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 輸出: 2 解釋: 節點2
和節點4
的最近公共祖先是2
, 因為根據定義最近公共祖先節點可以為節點本身。
這道題我們可以用遞歸來求解,我們首先來看題目中給的例子,由於二叉搜索樹的特點是左<根<右,所以根節點的值一直都是中間值,大於左子樹的所有節點值,小於右子樹的所有節點值,那么我們可以做如下的判斷,如果根節點的值大於p和q之間的較大值,說明p和q都在左子樹中,那么此時我們就進入根節點的左子節點繼續遞歸,如果根節點小於p和q之間的較小值,說明p和q都在右子樹中,那么此時我們就進入根節點的右子節點繼續遞歸,如果都不是,則說明當前根節點就是最小共同父節點,直接返回即可。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(!root) return nullptr; if(root->val > max(p->val, q->val)) return lowestCommonAncestor(root->left, p, q); else if(root->val < min(p->val, q->val)) return lowestCommonAncestor(root->right, p, q); else return root; } };
2、236. 二叉樹的最近公共祖先
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義為:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示為一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度盡可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉樹: root = [3,5,1,6,2,0,8,null,null,7,4]
_______3______ / \ ___5__ ___1__ / \ / \ 6 _2 0 8 / \ 7 4
示例 1:
輸入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 輸出: 3 解釋: 節點5
和節點1
的最近公共祖先是節點3。
示例 2:
輸入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 輸出: 5 解釋: 節點5
和節點4
的最近公共祖先是節點5。
因為根據定義最近公共祖先節點可以為節點本身。
所以我們只能在二叉樹中來搜索p和q,然后從路徑中找到最后一個相同的節點即為父節點,我們可以用遞歸來實現,在遞歸函數中,我們首先看當前結點是否為空,若為空則直接返回空,若為p或q中的任意一個,也直接返回當前結點。否則的話就對齊左右子結點分別調用遞歸函數,由於這道題限制了p和q一定都在二叉樹中存在,那么如果當前結點不等於p或q,那么p和q要么分別位於左右子樹中,要么同時位於左子樹,或者同時位於右子樹,那么我們分別來討論:
若p和q要么分別位於左右子樹中,那么對左右子結點調用遞歸函數,會分別返回p和q結點的位置,而當前結點正好就是p和q的最小共同父結點,直接返回當前結點即可,這就是題目中的例子1的情況。
若p和q同時位於左子樹,這里有兩種情況,一種情況是left會返回p和q中較高的那個位置,而right會返回空,所以我們最終返回非空的left即可,這就是題目中的例子2的情況。還有一種情況是會返回p和q的最小父結點,就是說當前結點的左子樹中的某個結點才是p和q的最小父結點,會被返回。
若p和q同時位於右子樹,同樣這里有兩種情況,一種情況是right會返回p和q中較高的那個位置,而left會返回空,所以我們最終返回非空的right即可,還有一種情況是會返回p和q的最小父結點,就是說當前結點的右子樹中的某個結點才是p和q的最小父結點,會被返回,寫法很簡潔,
上述代碼可以進行優化一下,如果當前結點不為空,且既不是p也不是q,那么根據上面的分析,p和q的位置就有三種情況,p和q要么分別位於左右子樹中,要么同時位於左子樹,或者同時位於右子樹。我們需要優化的情況就是當p和q同時為於左子樹或右子樹中,而且返回的結點並不是p或q,那么就是p和q的最小父結點了,已經求出來了,就不用再對右結點調用遞歸函數了,同樣,對返回的right也做同樣的優化處理。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(!root || p==root || q==root) return root; TreeNode* left = lowestCommonAncestor(root->left, p, q);
//優化添加的 if(left && left != p && left != q) return left;
TreeNode* right = lowestCommonAncestor(root->right, p, q);
//優化添加的 if(right && right != p && right != q) return right;
if(left && right) return root; return left ? left : right; } };