一,問題描述
構建一棵二叉樹(不一定是二叉查找樹),求出該二叉樹中某兩個結點的最低公共父結點。借用一張圖如下:
結點8 和 結點5 的最低公共父結點為 結點2
二,二叉樹的構建
與 求二叉樹中第K層結點的個數 文章中的第二點:二叉樹構建相同
三,求解最低公共父結點的算法實現
有兩種思路,一種是通過中序遍歷和后序遍歷。由於中序遍歷是先左子樹中的結點,再訪問根,再訪問右子樹中結點,因此這兩個結點的公共父結點一定處於這兩個結點之間。
如:中序遍歷:8, 4, 9, 2, 5, 1, 6, 3, 7 結點2處於結點8 和 結點5 之間,也就是說:結點8 和 結點5 的最低公共父結點在 [8~5]之間的候選結點,這里為{4,9,2}中取
后序遍歷是先訪問左右子樹中的結點,最后再訪問根。故這兩個結點的最低公共父結點一定處於 結點8 和 結點5 之后的結點,且是第一個出現在{4,9,2}中的那個結點。
后序遍歷:8, 9, 4, 5, 2, 6, 7, 3, 1 8->9->4->5 這之后的結點,才可能是 結點8 和 結點5 的父結點。
另一種方法則是:遞歸,首先從樹根開始考慮:
①結點A 和 結點B 要么都在樹根的左子樹中;②要么都在樹根的右子樹中;③要么一個在左子樹中,一個在右子樹中。
這是一個分治算法,對於情況①和②,可以繼續遞歸分解。對於情況③屬於代碼第10行判斷,復雜度為O(1)
遞歸表達式可表示為:T(N)=2T(N/2)+O(1),解得T(N)=O(N)
對於③,最低公共父結點為樹根。
對於①,可以進一步判斷,從樹根的左孩子結點考慮:
1)結點A 和 結點B 要么都在樹根的左子孩子 的 左子樹中;2)要么都在樹根的左孩子 的 右子樹中;3) 要么一個在樹根的左孩子的 左子樹中,一個在樹根的左孩子 的 右子樹中。
對於②,可以進一步判斷,從樹根的右孩子的結點考慮:
1)結點A 和 結點B 要么都在樹根的右子孩子 的 左子樹中;2)要么都在樹根的右孩子 的 右子樹中;3) 要么一個在樹根的右孩子的 左子樹中,一個在樹根的右孩子 的 右子樹中。
下面代碼實現遞歸求解最低公共父結點。
四,代碼實現(node1 node2 都是二叉樹中的結點)
1 /** 2 * 求解node1 和 node2 的最低公共父結點 3 * @param node1 4 * @param node2 5 * @return 最低公共父結點 6 */ 7 public BinaryNode<T> commonNode(BinaryNode<T> node1, BinaryNode<T> node2, BinaryNode<T> root){ 8 if(root == null) 9 return null; 10 if(node1.element == root.element || node2.element == root.element) 11 return root; 12 /* 13 * 若 left==null, node1,node2 都不在 root.left子樹中 14 * 若right==null,node1,node2 都不在root.right子樹中 15 */ 16 BinaryNode<T> left = commonNode(node1, node2, root.left); 17 BinaryNode<T> right = commonNode(node1, node2, root.right); 18 19 if(left != null && right != null) 20 return root; 21 return left == null ? right : left; 22 }
根據程序中的第8行和第10行的if語句,可知:
1)若 left==null, 則說明 node1,node2 都不在 root.left子樹中
2)若right==null,則說明 node1,node2 都不在root.right子樹中
3)當對於某個結點,當執行了第16,17行的遞歸后 ,left 和 right 都不為空,說明node1 在 該結點.left子樹中,node2 在 該結點.right子樹中
故第19-20行,返回 該結點 作為公共父結點
例如,求結點8 和 結點5 的最低公共父結點:遞歸調用過程如下:
a)commNode(8,5,1)==2
b1) commNode(8,5,2)==2
c1) commNode(8,5,4)==8
d1) commNode(8,5,8)==8
d2) commNode(8,5,9)==null
c2) commNode(8,5,5)==5
b2) commNode(8,5,3)==null
a)生成了 b1) b2) 兩個遞歸調用,其中 b2)為空,因為結點8,結點5 不在以3為根的子樹中
b1)生成了 c1) c2)兩個遞歸調用,其中 c2) 是commNode(8,5,5),根據程序第10行if,返回5,而c1)又生成了 d1) d2)兩個遞歸調用
其中,d1) 返回8,d2)返回null, 故執行到第21行語句,return 不空的那個left/right,也就是 d1) 返回的結點8
由於 c1)返回了5, c2)返回了8,都不為空,執行到第19-20行,返回它們的root,即,結點4和結點5的公共父結點:結點2
由於 b1) 生成了 c1) c2) 故 b1) 的值是結點2 又因為 b2) 為null
故 a) 最終是 b1) 的值,即為結點2
可把該方法放到求二叉樹中第K層結點的個數 中 的完整代碼給出的程序中進行測試。
五,參考資料
How to find the lowest common ancestor of two nodes in any binary tree?
附:求解二叉查找樹的最低公共祖先結點:https://www.cnblogs.com/hapjin/p/5770596.html