Skip to content

Commit 3ba35e5

Browse files
authored
Merge pull request #9 from Alonza0314/2025/04/11
2025/04/11
2 parents 8bd350a + 9512a6c commit 3ba35e5

10 files changed

+399
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 124. Binary Tree Maximum Path Sum
2+
3+
## Intuition
4+
5+
The maximum path sum in a binary tree can be found by considering all possible paths that pass through each node. For any given node, the maximum path sum could be:
6+
7+
1. The node's value itself
8+
2. The node's value plus the maximum path from its left subtree
9+
3. The node's value plus the maximum path from its right subtree
10+
4. The node's value plus both left and right maximum paths
11+
12+
## Approach
13+
14+
We use a post-order traversal approach to solve this problem:
15+
16+
1. For each node, we recursively calculate the maximum path sum from its left and right subtrees
17+
2. We update the global maximum by considering all possible combinations:
18+
- The current node's value alone
19+
- Current node + left path
20+
- Current node + right path
21+
- Current node + left path + right path
22+
3. We return the maximum path sum that can be extended from the current node to its parent (which can only include at most one of its children)
23+
24+
## Complexity
25+
26+
- Time complexity: O(n)
27+
- Space complexity: O(h)
28+
29+
## Keywords
30+
31+
- Binary Tree
32+
- Post-order Traversal
33+
- Recursion
34+
35+
## Code
36+
37+
```go
38+
/**
39+
* Definition for a binary tree node.
40+
* type TreeNode struct {
41+
* Val int
42+
* Left *TreeNode
43+
* Right *TreeNode
44+
* }
45+
*/
46+
func maxPathSum(root *TreeNode) int {
47+
ret := math.MinInt
48+
var postOrder func(node *TreeNode) int
49+
postOrder = func(node *TreeNode) int {
50+
if node == nil {
51+
return 0
52+
}
53+
left, right := postOrder(node.Left), postOrder(node.Right)
54+
ret = max(ret, left + right + node.Val, left + node.Val, right + node.Val, node.Val)
55+
return max(left + node.Val, right + node.Val, node.Val)
56+
}
57+
ret = max(ret, postOrder(root))
58+
return ret
59+
}
60+
```

Hard/834 Sum of Distances in Tree.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# 834. Sum of Distances in Tree
2+
3+
## Intuition
4+
5+
A key observation is that we can leverage the tree structure and parent-child relationships to compute these sums efficiently. Instead of calculating distances from scratch for each node, we can use the results from a parent node to derive the results for its children.
6+
7+
## Approach
8+
9+
The solution uses a two-pass DFS approach:
10+
11+
1. First DFS (dfs1):
12+
- Calculates the number of child nodes for each node
13+
- Computes the initial sum of distances for the root node (0)
14+
- This pass builds the foundation for the second pass
15+
2. Second DFS (dfs2):
16+
- Uses the results from the first pass to compute distances for all other nodes
17+
- For each child node, the sum of distances can be derived from its parent's sum using the formula:
18+
`ret[child] = ret[parent] + (n - 2 * childNodes[child])`
19+
- This formula works because moving from parent to child:
20+
- Increases distance by 1 for all nodes not in the child's subtree
21+
- Decreases distance by 1 for all nodes in the child's subtree
22+
23+
## Complexity
24+
25+
- Time complexity: O(n)
26+
- Space complexity: O(n)
27+
28+
## Keywords
29+
30+
- DFS
31+
- Distance Calculation
32+
33+
## Code
34+
35+
```go
36+
func sumOfDistancesInTree(n int, edges [][]int) []int {
37+
ret, childNodes, tree := make([]int, n), make([]int, n), make([][]int, n)
38+
for _, edge := range edges {
39+
a, b := edge[0], edge[1]
40+
tree[a], tree[b] = append(tree[a], b), append(tree[b], a)
41+
}
42+
43+
var dfs1 func(cur, prev int)
44+
dfs1 = func(cur, prev int) {
45+
childNodes[cur] = 1
46+
for _, neighbor := range tree[cur] {
47+
if neighbor == prev {
48+
continue
49+
}
50+
dfs1(neighbor, cur)
51+
childNodes[cur] += childNodes[neighbor]
52+
ret[cur] += ret[neighbor] + childNodes[neighbor]
53+
}
54+
}
55+
56+
var dfs2 func(cur, prev int)
57+
dfs2 = func(cur, prev int) {
58+
for _, neighbor := range tree[cur] {
59+
if neighbor == prev {
60+
continue
61+
}
62+
ret[neighbor] = ret[cur] + (n- 2 * childNodes[neighbor])
63+
dfs2(neighbor, cur)
64+
}
65+
}
66+
67+
dfs1(0, -1)
68+
dfs2(0, -1)
69+
70+
return ret
71+
}
72+
```
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# 236. Lowest Common Ancestor of a Binary Tree
2+
3+
## Intuition
4+
5+
We can use a post-order traversal approach where we first check the left and right subtrees, then process the current node.
6+
7+
## Approach
8+
9+
1. We use a recursive helper function `find` that takes the current node and the two target nodes p and q.
10+
2. The base cases are:
11+
- If the current node is nil, return nil
12+
- If the current node is either p or q, return the current node
13+
3. We recursively search in both left and right subtrees
14+
4. The logic for determining the LCA is:
15+
- If both left and right subtrees return nil, return nil (neither p nor q found)
16+
- If only one subtree returns a non-nil value, return that value (one of p or q found)
17+
- If both subtrees return non-nil values, the current node is the LCA
18+
19+
## Complexity
20+
21+
- Time complexity: O(n)
22+
- Space complexity: O(h)
23+
24+
## Keywords
25+
26+
- Binary Tree
27+
- Recursion
28+
- DFS
29+
- Post-order Traversal
30+
- Lowest Common Ancestor
31+
32+
## Code
33+
34+
```go
35+
/**
36+
* Definition for a binary tree node.
37+
* type TreeNode struct {
38+
* Val int
39+
* Left *TreeNode
40+
* Right *TreeNode
41+
* }
42+
*/
43+
func find(node, p, q *TreeNode) *TreeNode {
44+
if node == nil {
45+
return nil
46+
}
47+
if node == p || node == q {
48+
return node
49+
}
50+
left := find(node.Left, p, q)
51+
right := find(node.Right, p, q)
52+
if left == nil && right == nil {
53+
return nil
54+
}
55+
if left != nil && right == nil {
56+
return left
57+
}
58+
if left == nil && right != nil {
59+
return right
60+
}
61+
return node
62+
}
63+
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
64+
return find(root, p, q)
65+
}
66+
```

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
| 50 | Pow(x, n) | [go](/Medium/50%20Pow(x,%20n).md) | M |
1919
| 51 | N-Queens | [go](/Hard/51%20N-Queens.md) | H |
2020
| 105 | Construct Binary Tree from Preorder and Inorder Tranversal | [go](/Medium/105%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Tranversal.md) | M |
21+
| 124 | Binary Tree Maximum Path Sum | [go](Hard/124%20Binary%20Tree%20Maximum%20Path%20Sum.md) | H |
2122
| 135 | Candy | [go](/Hard/135%20Candy.md) | H |
2223
| 140 | Word Break II | [go](/Hard/140%20Word%20Break%20II.md) | H |
2324
| 146 | LRU Cache | [go](/Medium/146%20LRU%20Cache.md) | M |
25+
| 236 | Lowest Common Ancestor of a Binary Tree | [go](/Medium/236%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.md) | M |
2426
| 239 | Sliding Window Maximum | [go](/Hard/239%20Sliding%20Window%20Maximum.md) | H |
2527
| 322 | Coin Change | [go](/Medium/322%20Coin%20Change.md) | M |
2628
| 460 | LFU Cache | [go](/Hard/460%20LFU%20Cache.md) | H |
@@ -29,6 +31,7 @@
2931
| 757 | Set Intersection Size At Least Two | [go](/Hard/757%20Set%20Intersection%20Size%20At%20Least%20Two.md) | H |
3032
| 815 | Bus Routes | [go](/Hard/815%20Bus%20Routes.md) | H |
3133
| 765 | Couples Holding Hands | [go](/Hard/765%20Couples%20Holding%20Hands.md) | H |
34+
| 834 | Sum of Distances in Tree | [go](/Hard/834%20Sum%20of%20Distances%20in%20Tree.md) | H |
3235
| 840 | Magic Squares In Grid | [go](/Medium/840%20Magic%20Squares%20In%20Grid.md) | M |
3336
| 881 | Boats to Save People | [go](/Medium/881%20Boats%20to%20Save%20People.md) | M |
3437
| 912 | Sort an Array | [go](/Medium/912%20Sort%20an%20Array.md) | M |

Week8/124 AC.png

112 KB
Loading
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 124. Binary Tree Maximum Path Sum
2+
3+
## Intuition
4+
5+
The maximum path sum in a binary tree can be found by considering all possible paths that pass through each node. For any given node, the maximum path sum could be:
6+
7+
1. The node's value itself
8+
2. The node's value plus the maximum path from its left subtree
9+
3. The node's value plus the maximum path from its right subtree
10+
4. The node's value plus both left and right maximum paths
11+
12+
## Approach
13+
14+
We use a post-order traversal approach to solve this problem:
15+
16+
1. For each node, we recursively calculate the maximum path sum from its left and right subtrees
17+
2. We update the global maximum by considering all possible combinations:
18+
- The current node's value alone
19+
- Current node + left path
20+
- Current node + right path
21+
- Current node + left path + right path
22+
3. We return the maximum path sum that can be extended from the current node to its parent (which can only include at most one of its children)
23+
24+
## Complexity
25+
26+
- Time complexity: O(n)
27+
- Space complexity: O(h)
28+
29+
## Keywords
30+
31+
- Binary Tree
32+
- Post-order Traversal
33+
- Recursion
34+
35+
## Code
36+
37+
```go
38+
/**
39+
* Definition for a binary tree node.
40+
* type TreeNode struct {
41+
* Val int
42+
* Left *TreeNode
43+
* Right *TreeNode
44+
* }
45+
*/
46+
func maxPathSum(root *TreeNode) int {
47+
ret := math.MinInt
48+
var postOrder func(node *TreeNode) int
49+
postOrder = func(node *TreeNode) int {
50+
if node == nil {
51+
return 0
52+
}
53+
left, right := postOrder(node.Left), postOrder(node.Right)
54+
ret = max(ret, left + right + node.Val, left + node.Val, right + node.Val, node.Val)
55+
return max(left + node.Val, right + node.Val, node.Val)
56+
}
57+
ret = max(ret, postOrder(root))
58+
return ret
59+
}
60+
```

Week8/236 AC.png

118 KB
Loading
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# 236. Lowest Common Ancestor of a Binary Tree
2+
3+
## Intuition
4+
5+
We can use a post-order traversal approach where we first check the left and right subtrees, then process the current node.
6+
7+
## Approach
8+
9+
1. We use a recursive helper function `find` that takes the current node and the two target nodes p and q.
10+
2. The base cases are:
11+
- If the current node is nil, return nil
12+
- If the current node is either p or q, return the current node
13+
3. We recursively search in both left and right subtrees
14+
4. The logic for determining the LCA is:
15+
- If both left and right subtrees return nil, return nil (neither p nor q found)
16+
- If only one subtree returns a non-nil value, return that value (one of p or q found)
17+
- If both subtrees return non-nil values, the current node is the LCA
18+
19+
## Complexity
20+
21+
- Time complexity: O(n)
22+
- Space complexity: O(h)
23+
24+
## Keywords
25+
26+
- Binary Tree
27+
- Recursion
28+
- DFS
29+
- Post-order Traversal
30+
- Lowest Common Ancestor
31+
32+
## Code
33+
34+
```go
35+
/**
36+
* Definition for a binary tree node.
37+
* type TreeNode struct {
38+
* Val int
39+
* Left *TreeNode
40+
* Right *TreeNode
41+
* }
42+
*/
43+
func find(node, p, q *TreeNode) *TreeNode {
44+
if node == nil {
45+
return nil
46+
}
47+
if node == p || node == q {
48+
return node
49+
}
50+
left := find(node.Left, p, q)
51+
right := find(node.Right, p, q)
52+
if left == nil && right == nil {
53+
return nil
54+
}
55+
if left != nil && right == nil {
56+
return left
57+
}
58+
if left == nil && right != nil {
59+
return right
60+
}
61+
return node
62+
}
63+
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
64+
return find(root, p, q)
65+
}
66+
```

Week8/834 AC.png

40.1 KB
Loading

0 commit comments

Comments
 (0)