題目分析:ide
假設1:這個二叉樹是二叉排序樹(O(N))spa
若是題目中的二叉樹是二叉排序樹,那麼這道題目變的相對簡單不少,咱們只須要從根節點開始遍歷,有如下三種狀況發生:指針
(1)若是題目給的兩個節點的值都大於當前節點,那麼繼續遍歷當前節點的右子樹排序
(2)若是題目給的兩個節點的值都小於當前節點,那麼繼續遍歷當前節點的左子樹遞歸
(3)若是當前節點大於其中一個節點而小於其中另一個節點,那麼則返回該節點,該節點即是兩個節點的公共 祖先節點。二叉樹
假設2:這個二叉樹是普通的二叉樹,可是存在指向父節點的指針遍歷
因爲每一個節點存在指向父節點的指針,因此若是給定任意一個節點,即可以找到從該節點到根節點的路徑,所以咱們能夠找到題目中給予的兩個節點分別到根節點的路徑,所以該題目便變成了求兩個單向鏈表的第一個公共節點。方法
假設2延伸:求兩個單向鏈表第一個公共節點有兩種方法:im
(1)第一種方法是:兩個鏈表若是有 公共節點,那麼從第一個公共節點開始日後的全部節點都相同,咱們首先遍歷兩個鏈表,求出兩個鏈表的長度。而後設定兩個指針分別指向這兩個單向鏈表的頭結 點,首先讓鏈表較長的指針先移動,直至移動的剩餘長度與鏈表較短的那個鏈表長度相同爲止,這個時候兩個指針開始同時移動,直到兩個指針指向的節點相同,這 個節點即是這兩個單向鏈表的第一個公共節點。鏈表
(2)第二種方法是:能夠設置兩個棧,首先分別把兩個鏈表中的節點依次入棧,接下來從兩個棧中同時一個一個的將節點彈出,遇到第一對彈出的節點不一樣時,那麼前一次彈出相同的節點即是這兩個鏈表的第一個公共節點。
假設3:這個二叉樹是普通的二叉樹,而且不存在指向父節點的指針
<方法1>:判斷子樹中是否存在某節點(時間複雜度O(N^2))
首先創建一個方法判斷一棵樹中是否存在某個節點,這個比較簡單,只須要遍歷整棵樹,若是存在返回true,若是不存在則返回false。
接下來的判斷過程就至關於二叉排序數的判斷過程了,只不過二叉排序樹判斷一個節點是否在左子樹或者右子樹中只須要和父節點比較,而普通的樹須要遍歷整個左子樹或者右子樹才能實現,
(1)若是題目給的兩個節點都在右子樹,那麼繼續遍歷當前節點的右子樹
(2)若是題目給的兩個節點都在左子樹,那麼繼續遍歷當前節點的左子樹
(3)若是給定的兩個節點一個在左子樹,一個在右子樹,則返回當前節點。
<方法2>:尋找從根節點到子節點的路徑
若是找到從根節點到兩個子節點的路徑,那麼題目就迎刃而解了,能夠用棧遍歷二叉樹,最後找到從根節點到相應節點的路徑,相似二叉樹的先序非遞歸遍歷同樣,先遍歷根節點,接着遍歷左子樹,接着遍歷右子樹,直到遍歷到相應節點爲止,這時棧中的元素即是路徑元素。
<方法3>:遞歸遍歷
設根節點爲root,兩個節點分別爲p,q,用前序遍歷方法遞歸遍歷整個二叉樹,若是隻找到p則返回p,若是隻找到q則返回q,若是一個節點的左右子樹分別找到p,q,則返回該節點,其餘狀況返回NULL。
/*
*若p爲root的子孫,則返回true
*/
boolean covers(TreeNode root,TreeNode p)
{
if(root==null) return false;
if(root== p) return true;
return covers(root.left,p)||covers(root.right,p);
}
TreeNode commonAncestorHelper(TreeNode root,TreeNode p,TreeNode q)
{
if(root==null) return null;
if(root == p || root == q) return root;
boolean is_p_on_left=covers(root.left,p);
boolean is_q_on_left=covers(root.left,q);
/*
* 若p和q不在同一邊,則返回root
*/
if(is_p_on_left!=is_q_on_left) return root;
/*
* 不然就在同一邊,遍訪那一邊
*/
TreeNode child_side=is_p_on_left?root.left:root.right;
return commonAncestorHelper(child_side,p,q);
}
TreeNode commonAncestor(TreeNode root,TreeNode p,TreeNode q)
{
if(!covers(root,p)||!covers(root,q))//錯誤檢查
{
return false;
}
return commonAncestorHelper(root,p,q);
}