1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Linq ;
4
+ using System . Text ;
5
+ using System . Threading . Tasks ;
6
+
7
+ namespace LowestCommonAncestorBTree
8
+ {
9
+ /// <summary>
10
+ /// code review on July 9, 2018
11
+ /// Leetcode 236
12
+ /// https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/
13
+ /// </summary>
14
+ class TreeNode
15
+ {
16
+ public int val ;
17
+ public TreeNode left ;
18
+ public TreeNode right ;
19
+
20
+ public TreeNode ( int x )
21
+ {
22
+ left = null ;
23
+ right = null ;
24
+ val = x ;
25
+ }
26
+ } ;
27
+
28
+ class LowestCommonAncestorB
29
+ {
30
+ /*
31
+ * Leetcode:
32
+ * Lowest common ancestor in binary tree
33
+ *
34
+ * Try a few of implementations - 4 different ones:
35
+ * 1. Method A:
36
+ * Top down method, using counting method to help
37
+ * worst case time O(N^2)
38
+ *
39
+ * 2. Method B:
40
+ * A Bottom-up Approach (Worst case O(n) ):
41
+ Using a bottom-up approach, we can improve over the top-down approach
42
+ by avoiding traversing the same nodes over and over again.
43
+ */
44
+ static void Main ( string [ ] args )
45
+ {
46
+ var root = new TreeNode ( 1 ) ;
47
+ root . left = new TreeNode ( 2 ) ;
48
+ root . right = new TreeNode ( 3 ) ;
49
+
50
+ root . left . left = new TreeNode ( 4 ) ;
51
+ root . left . right = new TreeNode ( 5 ) ;
52
+
53
+ root . right . left = new TreeNode ( 6 ) ;
54
+ root . right . right = new TreeNode ( 7 ) ;
55
+
56
+ var ancestor = LowestCommonAncestor ( root , root . right . left , root . left . right ) ;
57
+ }
58
+
59
+ /*
60
+ * source code from blog:
61
+ * http://articles.leetcode.com/2011/07/lowest-common-ancestor-of-a-binary-tree-part-i.html
62
+ *
63
+ * A Top-Down Approach (Worst case O(n^2) ):
64
+ *
65
+ * julia's comment:
66
+ 1. check left subtree count of matching p or q:
67
+ 0 - no match in left sub tree, so go to check right subtree
68
+ 1 - found it, the root is the node
69
+ 2 - continue to left, check
70
+ * 2. put the code in the certain order: root, left, right
71
+ * so, making it easy to follow.
72
+ * two orders:
73
+ * 1. check countingProblem return value, from 0 to 1 to 2, increasing order
74
+ * 2. using countingProblem calculation take action, "found it", "go left",
75
+ * "go right" three choice.
76
+ *
77
+ * Keep the code in the above order, this way, easy to memorize, explain, and discuss.
78
+ *
79
+ * 3. Using a easy counting problem to help solve lowest common ancestor (LCA),
80
+ * What is the thinking process?
81
+ * Here is the scripts Julia makes out:
82
+ * Go to visit root first, check it value, null or one of two nodes,
83
+ * and then, decide to find LCA, or go left to find it or go right to find it.
84
+ *
85
+ * But wait, how to decide to find it, or go left/ right, so calculate the left sub tree
86
+ * matching count first, use its value to determine.
87
+ *
88
+ * 4. online judge: (lowest common ancestor in binary search tree, not in binary tree)
89
+ * 27 / 27 test cases passed.
90
+ Status: Accepted
91
+ Runtime: 180 ms
92
+ *
93
+ */
94
+ public static TreeNode LowestCommonAncestor ( TreeNode root , TreeNode p , TreeNode q )
95
+ {
96
+ if ( root == null || p == null || q == null )
97
+ return null ;
98
+
99
+ if ( root == p || root == q )
100
+ return root ;
101
+
102
+ // go left
103
+ int leftTreeMatched = findMatches ( root . left , p , q ) ;
104
+
105
+ bool zeroFound = leftTreeMatched == 0 ;
106
+ bool oneFound = leftTreeMatched == 1 ;
107
+ bool twoFound = leftTreeMatched == 2 ;
108
+
109
+ if ( oneFound )
110
+ {
111
+ return root ;
112
+ }
113
+ else if ( twoFound )
114
+ {
115
+ return LowestCommonAncestor ( root . left , p , q ) ;
116
+ }
117
+ else if ( zeroFound )
118
+ {
119
+ return LowestCommonAncestor ( root . right , p , q ) ;
120
+ }
121
+
122
+ return null ;
123
+ }
124
+
125
+ /*
126
+ * source code from blog:
127
+ * http://articles.leetcode.com/2011/07/lowest-common-ancestor-of-a-binary-tree-part-i.html
128
+ * comment from blog:
129
+ * A Top-Down Approach (Worst case O(n^2) ):
130
+
131
+ Let’s try the top-down approach where we traverse the nodes from the top to the bottom.
132
+ * First, if the current node is one of the two nodes, it must be the LCA of the two nodes.
133
+ * If not, we count the number of nodes that matches either p or q in the left subtree (which
134
+ * we call totalMatches). If totalMatches equals 1, then we know the right subtree will contain
135
+ * the other node. Therefore, the current node must be the LCA. If totalMatches equals 2, we know
136
+ * that both nodes are contained in the left subtree, so we traverse to its left child. Similar
137
+ * with the case where totalMatches equals 0 where we traverse to its right child.
138
+ *
139
+ * Return #nodes that matches P or Q in the subtree.
140
+ *
141
+ * julia's comment:
142
+ * 1. Convert C++ code to C#
143
+ * 2. Solve a problem first: counting matches in the tree
144
+ * Counting always helps to make decision. Using number to make a decision
145
+ * 3. think about the question:
146
+ * Can this function only uses two line code?
147
+ *
148
+ *
149
+ */
150
+ private static int findMatches ( TreeNode root , TreeNode p , TreeNode q )
151
+ {
152
+ if ( root == null ) return 0 ;
153
+
154
+ int matches = findMatches ( root . left , p , q ) + findMatches ( root . right , p , q ) ;
155
+
156
+ if ( root == p || root == q )
157
+ {
158
+ return 1 + matches ;
159
+ }
160
+ else
161
+ {
162
+ return matches ;
163
+ }
164
+ }
165
+ }
166
+ }
0 commit comments