Skip to content

Commit 3aa0008

Browse files
committed
Add new problem notes and update some problem notes
1 parent c6ea2d9 commit 3aa0008

File tree

53 files changed

+1033
-236
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1033
-236
lines changed

Algorithms.code-workspace

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
"folders": [
33
{
44
"path": "."
5-
},
6-
{
7-
"name": ".leetcode",
8-
"path": "../../../.leetcode"
95
}
106
],
117
"settings": {}

leetcode/100.same-tree.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,33 @@ fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean {
5050
}
5151
```
5252

53-
* **Time Complexity**: `O(min(P, Q)`, where `P` and `Q` are the number of tree nodes of `p` and `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))`
55+
56+
## Iterative
57+
```kotlin
58+
fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean {
59+
val pQueue = ArrayDeque<TreeNode?>()
60+
val qQueue = ArrayDeque<TreeNode?>()
61+
pQueue.addLast(p)
62+
qQueue.addLast(q)
63+
while (pQueue.isNotEmpty() && qQueue.isNotEmpty()) {
64+
val pp = pQueue.removeFirst()
65+
val qq = qQueue.removeFirst()
66+
67+
if (pp == null && qq == null) continue
68+
if (pp == null || qq == null) return false
69+
if (pp.`val` != qq.`val`) return false
70+
pQueue.addLast(pp.left)
71+
qQueue.addLast(qq.left)
72+
73+
pQueue.addLast(pp.right)
74+
qQueue.addLast(qq.right)
75+
}
76+
return pQueue.isEmpty() && qQueue.isEmpty()
77+
}
78+
```
79+
* **Time Complexity**: `O(min(P, Q))`, where `P` and `Q` are the number of tree nodes of `p` and `q`.
5480
* **Space Complexity**: `O(min(P, Q))`
5581

5682
## Traversal

leetcode/101.symmetric-tree.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ fun check(left: TreeNode?, right: TreeNode?): Boolean {
4646
}
4747
```
4848

49+
* **Time Complexity**: `O(n)`, where `n` is the number of tree nodes.
50+
* **Space Complexity**: `O(h)`, where `h` is the height of the tree.
51+
4952
## Iterative
5053
```kotlin
5154
fun isSymmetric(root: TreeNode?): Boolean {
@@ -74,4 +77,6 @@ fun isSymmetric(root: TreeNode?): Boolean {
7477

7578
return leftQueue.isEmpty() && rightQueue.isEmpty()
7679
}
77-
```
80+
```
81+
* **Time Complexity**: `O(n)`, where `n` is the number of tree nodes.
82+
* **Space Complexity**: `O(n)`, where `n` is the number of tree nodes.

leetcode/110.balanced-binary-tree.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private fun getHeight(node: TreeNode?): Int {
8484
### Bottom-Up Solution (Optimal)
8585
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.
8686
* 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.
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.
8888

8989
![](../media/110.balanced-binary-tree.png)
9090
> Source: [Solution](https://leetcode.cn/problems/balanced-binary-tree/solution/ping-heng-er-cha-shu-by-leetcode-solution/)

leetcode/111.minimum-depth-of-binary-tree.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ fun minDepth(root: TreeNode?): Int {
9090
else return 1 + minOf(minDepth(root.left), minDepth(root.right))
9191
}
9292
```
93+
* **Time Complexity**: `O(n)`, iterate all nodes.
94+
* **Space Complexity**: `O(n)`, the worst case is the tree is a linked list, so the recursion stack will store all nodes.
9395

9496
## BFS
9597
We traversal level by level, and search the first leaf node, then return the depth.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# [1110. Delete Nodes And Return Forest](https://leetcode.com/problems/delete-nodes-and-return-forest/)
2+
3+
## Clarification Questions
4+
* Does the `to_delete` array contain the node that does not exist in the tree?
5+
6+
## Test Cases
7+
### Normal Cases
8+
```
9+
Input:
10+
1
11+
/ \
12+
3 5
13+
/ \ / \
14+
8 9 7 0
15+
/ /
16+
2 6
17+
delete = [5]
18+
Output:
19+
1
20+
/
21+
3
22+
/ \
23+
8 9 7 0
24+
/ /
25+
2 6
26+
```
27+
### Edge / Corner Cases
28+
* The nodes to delete are in the same subtree.
29+
```
30+
Input:
31+
1
32+
/ \
33+
3 5
34+
/ \ / \
35+
8 9 7 0
36+
/ /
37+
2 6
38+
delete = [1,5]
39+
Output:
40+
3
41+
/ \
42+
8 9 7 0
43+
/ /
44+
2 6
45+
```
46+
47+
## Postorder
48+
**Idea!!** We can traverse the tree, if the current node is in the delete list, we can add its children to the forest.
49+
50+
```js
51+
1 del(1)
52+
/ \
53+
2 3
54+
... ...
55+
56+
// After delete `1`, the subtrees `2` and `3` become the forest.
57+
/ \
58+
2 3
59+
... ...
60+
```
61+
We use postorder to delete, because we need to delete the nodes from the bottom to the top. Here is one key point to note, if the original root is not in the delete list, we should add it to the forest first.
62+
```kotlin
63+
fun delNodes(root: TreeNode?, to_delete: IntArray): List<TreeNode?> {
64+
val set = HashSet<Int>()
65+
for (d in to_delete) set.add(d)
66+
val forest = mutableListOf<TreeNode?>()
67+
// If the root is not in the delete list, add it to the forest first.
68+
if (!set.contains(root?.`val`)) forest.add(root)
69+
delete(root, set, forest)
70+
return forest
71+
}
72+
73+
private fun delete(root: TreeNode?, deleteSet: HashSet<Int>, forest: MutableList<TreeNode?>): TreeNode? {
74+
if (root == null) return null
75+
76+
root.left = delete(root.left, deleteSet, forest)
77+
root.right = delete(root.right, deleteSet, forest)
78+
if (deleteSet.contains(root.`val`)) {
79+
if (root.left != null) forest.add(root.left)
80+
if (root.right != null) forest.add(root.right)
81+
return null
82+
}
83+
return root
84+
}
85+
```
86+
87+
* **Time Complexity:** `O(n)`
88+
* **Space Complexity:** `O(n)`

leetcode/114.flatten-binary-tree-to-linked-list.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ private fun subtreeLast(root: TreeNode): TreeNode {
139139
* **Time Complexity**: `O(n)`, where `n` is the node of tree.
140140
* **Space Complexity**: `O(h)`, where `h` is the height of tree.
141141

142-
## DFS
142+
## Postorder (DFS)
143143
We can use postorder traversal to flatten the tree. The idea is to flatten the left subtree and right subtree first, then we relink the right subtree after the right most node of left subtree.
144144

145145
![](../media/114.flatten-binary-tree-to-linked-list.png)
@@ -170,7 +170,7 @@ private fun subtreeLast(node: TreeNode?): TreeNode? {
170170
}
171171
```
172172

173-
## DFS with Pointers (Constant Space Complexity)
173+
## Postorder with Pointers (Space Optimization)
174174
Idea is the same, but we traversal with pointers only (removing stack or recursion).
175175

176176
```kotlin

leetcode/121.best-time-to-buy-and-sell-stock.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ We're going to find minimium price to buy and calculate max profit for every day
55

66
```kotlin
77
fun maxProfit(prices: IntArray): Int {
8-
var lowestPrice = prices[0]
8+
var lowestPrice = Int.MAX_VALUE
99
var maxProfit = 0
10-
for (i in 1 until prices.size) {
11-
lowestPrice = min(lowestPrice, prices[i])
12-
maxProfit = max(maxProfit, prices[i] - lowestPrice)
10+
for (i in 0 until prices.size) {
11+
lowestPrice = minOf(lowestPrice, prices[i])
12+
maxProfit = maxOf(maxProfit, prices[i] - lowestPrice)
1313
}
1414
return maxProfit
1515
}

leetcode/133.clone-graph.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# [133. Clone Graph](https://leetcode.com/problems/clone-graph/description/)
2+
3+
## DFS + Hash Table
4+
We can use DFS or BFS to traverse from starting node to clone the node, then store the old and new node mapping in hash table, and update the neighbors of the new node by traversing the neighbors of the old node.
5+
6+
```kotlin
7+
/**
8+
* Definition for a Node.
9+
* class Node(var `val`: Int) {
10+
* var neighbors: ArrayList<Node?> = ArrayList<Node?>()
11+
* }
12+
*/
13+
14+
class Solution {
15+
16+
// 1: 1'
17+
// 2: 2'
18+
private val oldNewMapping = HashMap<Node, Node>()
19+
20+
fun cloneGraph(node: Node?): Node? {
21+
cloneNode(node)
22+
updateNeighbors(node, HashSet<Node>())
23+
return oldNewMapping[node]
24+
}
25+
26+
private fun cloneNode(node: Node?) {
27+
if (node == null || oldNewMapping.containsKey(node)) return
28+
val newNode = Node(node.`val`)
29+
oldNewMapping[node] = newNode
30+
node.neighbors.forEach { adj ->
31+
cloneNode(adj)
32+
}
33+
}
34+
35+
// 1 -> 1'
36+
// 1: [2, 3] -> 1': [2', 3']
37+
private fun updateNeighbors(node: Node?, updated: HashSet<Node>) {
38+
if (node == null || updated.contains(node)) return
39+
val newNode = oldNewMapping[node]
40+
if (newNode != null) {
41+
node.neighbors.forEach { adj ->
42+
val newAdj = oldNewMapping[adj]
43+
newNode.neighbors.add(newAdj)
44+
}
45+
}
46+
updated.add(node)
47+
node.neighbors.forEach { adj ->
48+
updateNeighbors(adj, updated)
49+
}
50+
}
51+
}
52+
53+
// Or equivalently, we can update the neighbors during the DFS traversal.
54+
55+
fun cloneGraph(node: Node?): Node? {
56+
return clone(node)
57+
}
58+
59+
private fun clone(node: Node?): Node? {
60+
if (node == null) return null
61+
if (oldNewMapping.containsKey(node)) return oldNewMapping[node]!!
62+
val newNode = Node(node.`val`)
63+
oldNewMapping[node] = newNode
64+
node.neighbors.forEach { adj ->
65+
newNode.neighbors.add(clone(adj))
66+
}
67+
return newNode
68+
}
69+
```
70+
* **Time Complexity**: `O(n)` for DFS traversal.
71+
* **Space Complexity**: `O(n)` for hash table and recursion stack.
72+
73+
## BFS + Hash Table
74+
In BFS, we still need old and new node mapping to prevent duplicate visited. We have to update the neighbors of current node when we traverse the neighbors of the old node.
75+
76+
```kotlin
77+
fun bfs(node: Node?): Node? {
78+
if (node == null) return null
79+
val queue = ArrayDeque<Node>()
80+
queue.addLast(node)
81+
val newNode = Node(node.`val`)
82+
oldNewMapping[node] = newNode
83+
84+
while (queue.isNotEmpty()) {
85+
val old = queue.removeFirst()
86+
87+
// We don't clone here, we clone and update the neighbors when we visit the adjacent nodes.
88+
89+
old.neighbors.forEach { adj ->
90+
if (adj != null) {
91+
if (!oldNewMapping.containsKey(adj)) {
92+
val newNode = Node(adj.`val`)
93+
oldNewMapping[adj] = newNode
94+
oldNewMapping[old]!!.neighbors.add(newNode)
95+
queue.addLast(adj)
96+
} else {
97+
// We have cloned the node, we just update the neighbors.
98+
oldNewMapping[old]!!.neighbors.add(oldNewMapping[adj]!!)
99+
}
100+
}
101+
}
102+
}
103+
return newNode
104+
}
105+
```
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# [1382. Balance a Binary Search Tree](https://leetcode.com/problems/balance-a-binary-search-tree/description/)
2+
3+
### Inorder
4+
We can build the inorder traversal of the tree and then build a new tree from the inorder traversal.
5+
6+
```kotlin
7+
private val list = mutableListOf<TreeNode>()
8+
9+
fun balanceBST(root: TreeNode?): TreeNode? {
10+
if (root == null) return null
11+
inorder(root)
12+
return build(0, list.size - 1)
13+
}
14+
15+
private fun inorder(root: TreeNode?) {
16+
if (root == null) return
17+
inorder(root.left)
18+
list.add(root)
19+
inorder(root.right)
20+
}
21+
22+
private fun build(start: Int, end: Int): TreeNode? {
23+
if (start > end) return null
24+
val middle = start + (end - start) / 2
25+
val newRoot = list[middle]
26+
newRoot.left = build(start, middle - 1)
27+
newRoot.right = build(middle + 1, end)
28+
return newRoot
29+
}
30+
```
31+
32+
* Time Complexity: `O(n)`
33+
* Space Complexity: `O(n)`

0 commit comments

Comments
 (0)