Skip to content

Commit 23c8bcf

Browse files
committed
Update notes of tree problems and split tree problem out
1 parent 75afb64 commit 23c8bcf

18 files changed

+658
-244
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ This project offers a curated collection of notes and resources covering fundame
2222
- [Backtracking](./topics/backtracking.md)
2323

2424
## Problems & Solutions
25-
- [LeetCode Solutions](./topics/leetcode-solutions.md)
25+
- [LeetCode Problem Listing](./leetcode/README.md)
2626
- [Problem Solving Techniques](./topics/problem-solving.md)
2727

2828
## Roadmap

leetcode/100.same-tree.md

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## [100. Same Tree](https://leetcode.com/problems/same-tree/)
1+
# [100. Same Tree](https://leetcode.com/problems/same-tree/)
22

33
## Clarification Questions
44
* No, it's clear from problem description.
@@ -7,16 +7,41 @@
77
### Normal Cases
88
```
99
Input:
10-
Output:
10+
1
11+
/ \
12+
2 3
13+
14+
1
15+
/ \
16+
2 3
17+
Output: True
1118
```
1219
### Edge / Corner Cases
13-
*
20+
* Values are the same, but different structurely.
21+
```
22+
Input:
23+
1
24+
/
25+
2
26+
1
27+
\
28+
2
29+
Output = False
30+
```
31+
32+
* Same structurely, but different value.
1433
```
1534
Input:
16-
Output:
35+
1
36+
/
37+
2
38+
1
39+
/
40+
3
41+
Output = False
1742
```
1843

19-
### Recursive
44+
## Recursive
2045
```kotlin
2146
fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean {
2247
if (p == null && q == null) return true
@@ -25,15 +50,15 @@ fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean {
2550
}
2651
```
2752

28-
* **Time Complexity**: `O(min(|P|, |Q|))`, where `|P|` and `|Q|` are the number of tree nodes of `p` and `q`.
29-
* **Space Complexity**: `O(min(|P|, |Q|))`
53+
* **Time Complexity**: `O(min(P, Q)`, where `P` and `Q` are the number of tree nodes of `p` and `q`.
54+
* **Space Complexity**: `O(min(P, Q))`
3055

31-
### Traversal
56+
## Traversal
3257
```kotlin
3358
class Solution {
3459

35-
private val pNodes = mutableListOf<Int>()
36-
private val qNodes = mutableListOf<Int>()
60+
private val pNodes = mutableListOf<Int?>()
61+
private val qNodes = mutableListOf<Int?>()
3762

3863
fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean {
3964
inOrderTraversal(p, pNodes)
@@ -48,17 +73,14 @@ class Solution {
4873
return true
4974
}
5075

51-
private fun inOrderTraversal(node: TreeNode?, results: MutableList<Int>) {
52-
results.add(node?.`val` ?: Int.MAX_VALUE)
53-
if (node?.left != null) inOrderTraversal(node?.left, results) else results.add(Int.MAX_VALUE)
54-
if (node?.right != null) inOrderTraversal(node?.right, results) else results.add(Int.MAX_VALUE)
76+
private fun inOrderTraversal(node: TreeNode?, results: MutableList<Int?>) {
77+
results.add(node?.`val`)
78+
// We can't skip the null node, we have to add null value for it.
79+
if (node?.left != null) inOrderTraversal(node?.left, results) else results.add(null)
80+
if (node?.right != null) inOrderTraversal(node?.right, results) else results.add(null)
5581
}
5682
}
5783
```
5884

59-
### Failed Cases
60-
```js
61-
[1, 2]
62-
[1, null, 2]
63-
```
64-
I skip the null node, so two trees are content identical, not structurely. Can't skip the null node.
85+
* **Time Complexity**: `O(max(P, Q))`, where `|P|` and `|Q|` are the number of tree nodes of `p` and `q`.
86+
* **Space Complexity**: `O(P + Q)`

leetcode/101.symmetric-tree.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## [101. Symmetric Tree](https://leetcode.com/problems/symmetric-tree/)
1+
# [101. Symmetric Tree](https://leetcode.com/problems/symmetric-tree/)
22

33
## Clarification Questions
44
* Is the empty tree symmetric?
@@ -31,45 +31,45 @@ Input:
3131
3
3232
Output: False
3333
```
34-
### Recursive
34+
35+
## Recursive
3536
```kotlin
3637
fun isSymmetric(root: TreeNode?): Boolean {
3738
if (root == null) return true
3839
return check(root?.left, root?.right)
3940
}
4041

4142
fun check(left: TreeNode?, right: TreeNode?): Boolean {
42-
if (left == null || right == null) return left == right
43-
if (left?.`val` != right?.`val`) return false
44-
return check(left?.left, right?.right) && check(left?.right, right?.left)
43+
if (left == null && right == null) return true
44+
if (left == null || right == null) return false
45+
return (left?.`val` == right?.`val`) && check(left?.left, right?.right) && check(left?.right, right?.left)
4546
}
4647
```
4748

48-
### Iterative
49+
## Iterative
4950
```kotlin
50-
private val nullNode = TreeNode(200)
51-
5251
fun isSymmetric(root: TreeNode?): Boolean {
5352
if (root == null) return true
5453

55-
val leftQueue = ArrayDeque<TreeNode>()
56-
val rightQueue = ArrayDeque<TreeNode>()
54+
val leftQueue = ArrayDeque<TreeNode?>()
55+
val rightQueue = ArrayDeque<TreeNode?>()
5756

58-
leftQueue.addLast(root.left ?: nullNode)
59-
rightQueue.addLast(root.right ?: nullNode)
57+
leftQueue.addLast(root.left)
58+
rightQueue.addLast(root.right)
6059

6160
while (leftQueue.isNotEmpty() && rightQueue.isNotEmpty()) {
6261
val left = leftQueue.removeFirst()
6362
val right = rightQueue.removeFirst()
64-
if (left == nullNode && right == nullNode) continue
65-
if (left == nullNode || right == nullNode) return false
63+
if (left == null && right == null) continue
64+
if (left == null || right == null) return false
6665

6766
if (left.`val` != right.`val`) return false
6867

69-
leftQueue.addLast(if (left.left != null) left.left else nullNode)
70-
rightQueue.addLast(if (right.right != null) right.right else nullNode)
71-
leftQueue.addLast(if (left.right != null) left.right else nullNode)
72-
rightQueue.addLast(if (right.left != null) right.left else nullNode)
68+
leftQueue.addLast(left.left)
69+
rightQueue.addLast(right.right)
70+
71+
leftQueue.addLast(left.right)
72+
rightQueue.addLast(right.left)
7373
}
7474

7575
return leftQueue.isEmpty() && rightQueue.isEmpty()

leetcode/110.balanced-binary-tree.md

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,94 @@
1-
## [110. Balanced Binary Tree](https://leetcode.com/problems/balanced-binary-tree/)
1+
# [110. Balanced Binary Tree](https://leetcode.com/problems/balanced-binary-tree/)
2+
3+
A height-balanced binary tree is a binary tree in which **the depth of the two subtrees of every node never differs by more than one**.
4+
5+
## Clarification Questions
6+
* Is the empty tree balanced?
7+
8+
## Test Cases
9+
### Normal Cases
10+
```
11+
Input:
12+
1
13+
/ \
14+
2 2
15+
/ \
16+
3 4
17+
Output: True
18+
19+
Input:
20+
1
21+
/ \
22+
2 2
23+
\
24+
4
25+
Output: False
26+
```
27+
### Edge / Corner Cases
28+
```
29+
Input:
30+
1
31+
/ \
32+
2 2
33+
/ \
34+
5 4
35+
Output: True
36+
37+
Input:
38+
1
39+
/ \
40+
2 2
41+
/ \
42+
5 4
43+
/ \
44+
6 7
45+
Output: False
46+
47+
Input:
48+
1
49+
/ \
50+
2 2
51+
/ / \
52+
1 3 4
53+
/ /
54+
0 5
55+
/
56+
6
57+
Output: False
58+
```
259

360
### Top-Down Recursion
61+
We can iterate each node recursively to check if the height of their subtree differs <= 1. For each node, we calculate the height of subtree (one recursion), and check the current difference with the check of two subtrees recursively (another recursion).
462
```kotlin
563
fun isBalanced(root: TreeNode?): Boolean {
664
if (root == null) return true
7-
val leftHeight = height(root?.left)
8-
var rightHeight = height(root?.right)
65+
val leftHeight = getHeight(root?.left)
66+
var rightHeight = getHeight(root?.right)
967
val diff = leftHeight - rightHeight
1068
return diff * diff <= 1 && isBalanced(root?.left) && isBalanced(root?.right)
1169
}
1270

13-
private fun height(node: TreeNode?): Int {
71+
private fun getHeight(node: TreeNode?): Int {
1472
return if (node == null) 0
1573
else {
16-
1 + maxOf(height(node?.left), height(node?.right))
74+
1 + maxOf(getHeight(node?.left), getHeight(node?.right))
1775
}
1876
}
1977
```
2078

21-
* **Time Complexity**: Calculate the height = `O(lg n)`, and we have to calculate every node `n`, the average case (every node has children) is `O(n lg n)`, but the worst case is `O(n^2)` when the tree is skewed because the height becomes `O(n)`.
22-
* **Space Complexity**: `O(n)`.
79+
* **Time Complexity**: Calculate the height takes `O(h)`, and we have to calculate every node `n`, total takes `O(n * h)`, in the average case (every node has children) we have `O(n log n)`, but the worst case is `O(n^2)` when the tree is skewed because the height becomes `O(n)`.
80+
* **Space Complexity**: `O(n)` for recursive call stack.
2381

2482
> We can calculate the height and store it, then traversal to check. That would be `O(n)` time and space complexity.
2583
2684
### Bottom-Up Solution (Optimal)
27-
* We calculate the height (left and right) and check if it is balanced at the same function call, and if it's not balanced then return -1 to indicate unbalanced.
28-
* If it's not balanced from its child node, then it's not balanced as well for the current node.
85+
From previous solution, we invoke `isBalanced()` for each node recursively, and for each `isBalanced()` we have to calculate the height `getHeight()` recursively which leads to `O(n^2)` time complexity. Here we can optimize the solution by changing (merging) the purpose of `getHeight()`. We still use `getHeight()` to calculate the height, and use this function to indicate if the subtree is height-balanced at the same time.
86+
* If it's height-balanced, then return the height of current subtree.
87+
* Otherwise, just return -1 to indicate unbalanced. If it's not balanced from its child node, then the current node is not balanced as well.
2988

3089
![](../media/110.balanced-binary-tree.png)
31-
> Source: [Solution](https://leetcode.cn/problems/balanced-binary-tree/solution/ping-heng-er-cha-shu-by-leetcode-solution/), please take a very close look at it explanation.
90+
> Source: [Solution](https://leetcode.cn/problems/balanced-binary-tree/solution/ping-heng-er-cha-shu-by-leetcode-solution/)
91+
3292
```kotlin
3393
fun isBalanced(root: TreeNode?): Boolean {
3494
return getHeight(root) != -1
@@ -40,21 +100,10 @@ private fun getHeight(root: TreeNode?): Int {
40100
val leftHeight = getHeight(root.left)
41101
val rightHeight = getHeight(root.right)
42102

103+
// If one of the subtrees is not balanced, the it's not balanced overall
43104
if (leftHeight == -1 || rightHeight == -1 || abs(leftHeight - rightHeight) > 1) return -1
44105
else return 1 + max(leftHeight, rightHeight)
45106
}
46107
```
47108
* **Time Complexity**: It becomes `O(n)` since we calculate the height only once for every node.
48109
* **Space Complexity**: `O(n)`.
49-
50-
### Special Cases
51-
It's not balanced.
52-
```js
53-
1
54-
/ \
55-
2 3
56-
/ \
57-
4 5
58-
/ \
59-
5 7
60-
```

leetcode/112.path-sum.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## [112. Path Sum](https://leetcode.com/problems/path-sum/)
1+
# [112. Path Sum](https://leetcode.com/problems/path-sum/)
22

33
## Clarification Questions
44
* No, it's clear from problem description.
@@ -7,15 +7,29 @@
77
### Normal Cases
88
```
99
Input:
10-
Output:
10+
1
11+
/ \
12+
3 4
13+
targetSum = 4
14+
15+
Output: True
16+
17+
Input:
18+
1
19+
/ \
20+
3 4
21+
targetSum = 6
22+
23+
Output: False
1124
```
1225
### Edge / Corner Cases
13-
*
26+
* Empty tree
1427
```
15-
Input:
16-
Output:
28+
Input: []
29+
Output: False
1730
```
1831

32+
## DFS
1933
```kotlin
2034
fun hasPathSum(root: TreeNode?, targetSum: Int): Boolean {
2135
if (root == null) return false

0 commit comments

Comments
 (0)