Skip to content

Commit b2e1bb7

Browse files
committed
Add some solutions of new problems:
* 572. Subtree of Another Tree * 35. Search Insert Position * 143. Reorder List * 1155. Number of Dice Rolls With Target Sum
1 parent 9cb1b97 commit b2e1bb7

11 files changed

+223
-44
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## [1155. Number of Dice Rolls With Target Sum](https://leetcode.com/problems/number-of-dice-rolls-with-target-sum/)
2+
3+
```kotlin
4+
class Solution {
5+
6+
private val mod = 1000000000 + 7
7+
8+
fun numRollsToTarget(n: Int, k: Int, target: Int): Int {
9+
// val memo = hashMapOf<Pair<Int, Int>, Int>()
10+
// return topDownRecursion(n, k, target, memo)
11+
return bottomUpDp1D(n, k, target)
12+
}
13+
14+
private fun topDownRecursion(n: Int, k: Int, target: Int, memo: HashMap<Pair<Int, Int>, Int>): Int {
15+
if (n == 1) {
16+
if (target in 1..k) return 1
17+
else return 0
18+
}
19+
20+
// Or
21+
// if (n == 0 && target == 0) return 1
22+
// if (n == 0 || target == 0) return 0
23+
24+
if (memo.containsKey(n to target)) return memo[n to target]!!
25+
var ways = 0
26+
for (i in 1..k) {
27+
if (target - i >= 0)
28+
ways = (ways + topDownRecursion(n - 1, k, target - i, memo)) % mod
29+
}
30+
memo[n to target] = ways
31+
return ways
32+
}
33+
34+
private fun bottomUpDp(n: Int, k: Int, target: Int): Int {
35+
// dp[i][t] represents i dice and target is t
36+
val dp = Array(n + 1) { _ -> IntArray(target + 1) }
37+
for (i in 0..n) {
38+
for (t in 0..target) {
39+
dp[i][t] = if (i == 0 && t == 0) 1
40+
else if (i == 0 || t == 0) 0
41+
else {
42+
var ways = 0
43+
for (j in 1..k) {
44+
if (t >= j)
45+
ways = (ways + dp[i - 1][t - j]) % mod
46+
}
47+
ways
48+
}
49+
}
50+
}
51+
return dp[n][target]
52+
}
53+
54+
private fun bottomUpDp1D(n: Int, k: Int, target: Int): Int {
55+
val dp = IntArray(target + 1)
56+
for (i in 0..n) {
57+
for (t in target downTo 0) {
58+
dp[t] = if (i == 1) {
59+
if (t in 1..k) 1
60+
else 0
61+
} else {
62+
var ways = 0
63+
for (j in 1..k) {
64+
if (t >= j)
65+
ways = (ways + dp[t - j]) % mod
66+
}
67+
ways
68+
}
69+
}
70+
}
71+
return dp[target]
72+
}
73+
}
74+
```

leetcode/143.reorder-list.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,28 @@ fun reorderList(head: ListNode?): Unit {
55
if (head == null || head?.next == null) return
66
val middle = middle(head)
77
val reverseMiddleHead = reverse(middle)
8-
merge(head, reverseMiddleHead)
8+
9+
var node1: ListNode? = head
10+
var node2: ListNode? = reversedHead
11+
12+
while (node1 != null && node2 != null) {
13+
val next1 = node1.next
14+
var next2 = node2.next
15+
16+
node1.next = node2
17+
node2.next = next1
18+
19+
node1 = next1
20+
node2 = next2
21+
}
22+
23+
// Merge the remaining part
24+
if (node2 != null) node1?.next = node2
925
}
1026

27+
// This function actually finds the next node of middle node.
28+
// 1 -> 2 -> 3 -> 4, returns 3
29+
// 1 -> 2 -> 3 -> 4 -> 5, returns 4
1130
private fun middle(head: ListNode?): ListNode? {
1231
var previous: ListNode? = null
1332
var slow: ListNode? = head
@@ -39,6 +58,7 @@ private fun reverse(head: ListNode?): ListNode? {
3958
return previous
4059
}
4160

61+
// It works, but we have simpler way to merge.
4262
private fun merge(l1: ListNode?, l2: ListNode?) {
4363
var i = 2
4464
var result: ListNode? = l1

leetcode/235.lowest-common-acestor-of-a-binary-search-tree.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,16 @@ fun lowestCommonAncestor(root: TreeNode?, p: TreeNode?, q: TreeNode?): TreeNode?
4646
else break
4747
}
4848
return node
49-
}
49+
}
50+
```
51+
52+
### Recursive
53+
The same idea as *iterative once solution*.
54+
```kotlin
55+
fun lowestCommonAncestor(root: TreeNode?, p: TreeNode?, q: TreeNode?): TreeNode? {
56+
if (root == null || p == null || q == null) return null
57+
if (root.`val` < p.`val` && root.`val` < q.`val`) return lowestCommonAncestor(root.right, p, q)
58+
if (root.`val` > p.`val` && root.`val` > q.`val`) return lowestCommonAncestor(root.left, p, q)
59+
else return root
60+
}
61+
```

leetcode/33.search-in-rotated-sorted-array.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
We have to solve with `O(lg n)` time complexity, so we will start from binary search, but we have to decide the left or right part to search for next round, and since the array might be rotated, it won't be sorted for both left and right part, we have modify some logic here:
44

5-
In each round of binary search, we will split the `array[start..end]` into two parts: `[start..middle - 1]` and `[middle + 1..end]` and the key point here is that **one of the two parts will be sorted**, we have to find the sorted part (the starting value <= the ending value of that part) and we have to check if the target is in that sorted part, if yes, just search that part, otherwise, search another part.
5+
In each round of binary search, we will split the `array[start..end]` into two parts: `[start..middle - 1]` and `[middle + 1..end]` and the key point here is that **there is always a half part will be sorted, either left or right part**, we have to find the sorted part (the starting value <= the ending value of that part) and we have to check if the target is in that sorted part, if yes, just search that part, otherwise, search another part.
66

77
For example, nums = `[6, 7, 0, 1, 2, 5]`, target = 3:
88
* The `middle` = 1, left part = `[6, 7, 0]`, right part = `[2, 5]`.
@@ -31,4 +31,6 @@ fun search(nums: IntArray, target: Int): Int {
3131
}
3232
return -1
3333
}
34-
```
34+
```
35+
36+
Another way to implement the binary search

leetcode/35.search-insert-position.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
## [35. Search Insert Position](https://leetcode.com/problems/search-insert-position/)
2+
3+
```kotlin
4+
fun searchInsert(nums: IntArray, target: Int): Int {
5+
var left = 0
6+
var right = nums.size - 1
7+
8+
if (target < nums[left]) return 0
9+
else if (target > nums[right]) return nums.size
10+
11+
while (left <= right) {
12+
val middle = left + (right - left) / 2
13+
if (nums[middle] == target) return middle
14+
if (nums[middle] < target) {
15+
left = middle + 1
16+
} else {
17+
right = middle - 1
18+
}
19+
}
20+
return left
21+
}
22+
```

leetcode/373.find-k-pairs-with-smallest-sums.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ fun kSmallestPairs(nums1: IntArray, nums2: IntArray, k: Int): List<List<Int>> {
2929
(nums1[p1.first] + nums2[p1.second]) - (nums1[p2.first] + nums2[p2.second])
3030
}
3131

32-
// We iterate all index in nums1 and use 0 as second index.
32+
// We iterate all index in nums1 and use 0 as second index so that
33+
// we don't have to trace the duplicate pair.
34+
3335
// We use at most k items for optimization
3436
for (i in 0 until nums1.size.coerceAtMost(k))) {
3537
minHeap.add(i to 0)
3638
}
3739

3840
val results = mutableListOf<List<Int>>()
39-
var i = 0
4041
// We iterate at most k times
4142
while (results.size() < k && !minHeap.isEmpty()) {
4243
val pair = minHeap.poll()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## [572. Subtree of Another Tree](https://leetcode.com/problems/subtree-of-another-tree/)
2+
3+
```kotlin
4+
fun isSubtree(root: TreeNode?, subRoot: TreeNode?): Boolean {
5+
if (root == null && subRoot == null) return true
6+
if (root == null || subRoot == null) return false
7+
8+
return isSameTree(root, subRoot) || isSubtree(root?.left, subRoot) || isSubtree(root?.right, subRoot)
9+
}
10+
11+
private fun isSameTree(A: TreeNode?, B: TreeNode?): Boolean {
12+
if (A == null && B == null) return true
13+
if (A == null || B == null) return false
14+
15+
return A?.`val` == B?.`val` && isSameTree(A?.left, B?.left) && isSameTree(A?.right, B?.right)
16+
}
17+
```

leetcode/863.all-nodes-distance-k-in-binary-tree.md

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -57,61 +57,51 @@ class Solution {
5757
}
5858
```
5959

60-
### BFS
60+
### BFS (Level by Level)
6161
```kotlin
62-
private val parents = hashMapOf<TreeNode, TreeNode>()
63-
private val results = mutableListOf<Int>()
64-
6562
fun distanceK(root: TreeNode?, target: TreeNode?, k: Int): List<Int> {
63+
val results = mutableListOf<Int>()
6664
if (root == null || target == null) return results
67-
findParent(root, target)
68-
69-
var distance = 0
65+
66+
val parentMap = updateParents(root!!, target!!)
7067
val queue = ArrayDeque<TreeNode>()
7168
val visited = hashSetOf<TreeNode>()
72-
queue.add(target)
73-
visited.add(target)
74-
while (queue.isNotEmpty() && distance <= k) {
69+
queue.addLast(target)
70+
var distances = 0
71+
while (queue.isNotEmpty()) {
7572
val size = queue.size
7673
for (i in 0 until size) {
7774
val node = queue.removeFirst()
78-
if (distance == k) {
75+
if (visited.contains(node)) continue
76+
visited.add(node)
77+
if (distances == k) {
7978
results.add(node.`val`)
8079
}
81-
82-
if (node.left != null && !visited.contains(node.left)) {
83-
queue.addLast(node.left)
84-
visited.add(node.left)
85-
}
86-
if (node.right != null && !visited.contains(node.right)) {
87-
queue.addLast(node.right)
88-
visited.add(node.right)
89-
}
90-
if (parents.containsKey(node) && !visited.contains(parents[node]!!)) {
91-
queue.addLast(parents[node]!!)
92-
visited.add(parents[node]!!)
93-
}
80+
if (node.left != null && !visited.contains(node.left)) queue.addLast(node.left)
81+
if (node.right != null && !visited.contains(node.right)) queue.addLast(node.right)
82+
if (parentMap[node] != null && !visited.contains(parentMap[node]!!)) queue.addLast(parentMap[node]!!)
9483
}
95-
distance++
84+
distances++
85+
if (distances > k) break
9686
}
9787
return results
9888
}
9989

100-
private fun findParent(root: TreeNode?, target: TreeNode?) {
101-
if (root == null) return
90+
private fun updateParents(root: TreeNode, target: TreeNode): HashMap<TreeNode, TreeNode> {
91+
val parentMap = hashMapOf<TreeNode, TreeNode>()
10292
val queue = ArrayDeque<TreeNode>()
10393
queue.addLast(root)
104-
while (queue.isNotEmpty() && !parents.containsKey(target)) {
94+
while (queue.isNotEmpty() && !parentMap.containsKey(target)) {
10595
val node = queue.removeFirst()
106-
10796
if (node.left != null) {
108-
parents[node.left] = node
97+
parentMap[node.left] = node
10998
queue.addLast(node.left)
11099
}
111100
if (node.right != null) {
112-
parents[node.right] = node
101+
parentMap[node.right] = node
113102
queue.addLast(node.right)
114103
}
115104
}
105+
return parentMap
116106
}
117107
```

topics/dynamic-programming.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,10 +539,11 @@ longestCommonSubsequence(A, B, A.length, B.length)
539539

540540
### Top-Down DP
541541
```kotlin
542+
// m, n represent the length of substring.
542543
fun longestCommonSubsequence(A: String, B: String, m: Int, n: Int, dp: Array<IntArray>): Int {
543-
if (m < 0 || n < 0) return 0
544+
if (m == 0 || n == 0) return 0
544545
if (dp[m][n] != -1) return dp[m][n]
545-
dp[m][n] = if (A[m] == B[n]) 1 + longestCommonSubsequence(A, B, m - 1, n - 1, dp)
546+
dp[m][n] = if (A[m - 1] == B[n - 1]) 1 + longestCommonSubsequence(A, B, m - 1, n - 1, dp)
546547
else max(
547548
longestCommonSubsequence(A, B, m - 1, n, dp),
548549
longestCommonSubsequence(A, B, m, n - 1, dp)

topics/problems-solutions.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@
124124
|[863. All Nodes Distance K in Binary Tree](../leetcode/863.all-nodes-distance-k-in-binary-tree.md)|Medium|
125125
|[110. Balanced Binary Tree](../leetcode/110.balanced-binary-tree.md)|Easy|
126126
|[297. Serialize and Deserialize Binary Tree](../leetcode/297.serialize-and-deserialize-binary-tree.md)|Hard|
127+
|[572. Subtree of Another Tree](../leetcode/572.subtree-of-another-tree.md)|Easy|
127128

128-
> * https://leetcode.com/problems/subtree-of-another-tree/ 5k e
129129
> * https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/ 6.1k m
130130
> * https://leetcode.com/problems/count-of-smaller-numbers-after-self/ 5k hard
131131
> * https://leetcode.com/problems/binary-tree-pruning/ 2k m
@@ -236,6 +236,7 @@
236236
|[279. Perfect Squares](../leetcode/279.perfect-squares.md)|Medium|
237237
|[322. Coin Change](../leetcode/322.coin-change.md)|Medium|
238238
|[518. Coin Change 2](../leetcode/518.coin-change-2.md)|Medium|
239+
|[1155. Number of Dice Rolls With Target Sum](../leetcode/1155.number-of-dice-rolls-with-target-sum.md)|Medium|
239240

240241
> * https://leetcode.com/problems/combination-sum-iv/ 5k m
241242
> * https://leetcode.com/problems/minimum-cost-for-tickets/ 5k m
@@ -311,6 +312,7 @@
311312
|[704. Binary Search](../leetcode/704.binary-search.md)|Easy|
312313
|[33. Search in Rotated Sorted Array](../leetcode/33.search-in-rotated-sorted-array.md)|Medium|
313314
|[34. Find First and Last Position of Element in Sorted Array](../leetcode/34.find-first-and-last-position-of-element-in-sorted-array.md)|Medium|
315+
|[35. Search Insert Position](../leetcode/35.search-insert-position.md)|Easy}
314316
|[153. Find Minimum in Rotated Sorted Array](../leetcode/153.find-minimum-in-rotated-sorted-array.md)|Medium|
315317
|[74. Search a 2D Matrix](../leetcode/74.search-a-2d-matrix.md)|Medium|
316318
|[378. Kth Smallest Element in a Sorted Matrix](../leetcode/378.kth-smallest-element-in-a-sorted-matrix.md)|Medium|

topics/searching.md

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,46 @@ fun binarySearch(A: IntArray, target: Int): Int {
2424

2525
* **Time Complexity**: It takes `O(lg n)` time, it keeps searching the half size of subarray, that is, keeps divide `n` by 2 = `O(lg n)`.
2626

27+
## Variants
28+
```kotlin
29+
fun binarySearch(A: IntArray, target: Int): Int {
30+
var left = 0
31+
var right = A.size - 1
32+
while (left < right) {
33+
val middle = left + (right - left) / 2
34+
if (A[middle] < target) {
35+
left = middle + 1
36+
} else {
37+
right = middle
38+
}
39+
}
40+
return if (A[left] == target) left else -1
41+
}
42+
```
43+
44+
> From [Binary Search 101](https://leetcode.com/problems/binary-search/solutions/423162/Binary-Search-101-The-Ultimate-Binary-Search-Handbook/)
45+
46+
Some key points:
47+
48+
* How to shrink the searching range by moving `left` and `right`?
49+
```js
50+
if (100% sure logic) {
51+
left = middle + 1
52+
} else {
53+
right = middle
54+
}
55+
56+
// or
57+
if (100% sure logic) {
58+
right = middle - 1
59+
} else {
60+
left = middle
61+
}
62+
```
63+
* Always use `while (left < right)`, so when while loop breaks, we will have `left == right`, and then check if the target exists or not. (might need further check, such as `if (A[left] == target)`).
64+
2765
## Resources
28-
- [ ] CTCI
29-
- [X] [Khan Academy](https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/binary-search)
30-
- [X] [LC Learn - Binary Search](https://leetcode.com/explore/learn/card/binary-search/) // A very interesting and slightly different ways to implement different binary search algorithm.
31-
- [ ] [Google Recuriter Recommended Problems List](https://turingplanet.org/2020/09/18/leetcode_planning_list/#Binary_Search)
66+
- CTCI
67+
- [Khan Academy](https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/binary-search)
68+
- [LC Learn - Binary Search](https://leetcode.com/explore/learn/card/binary-search/) // A very interesting and slightly different ways to implement different binary search algorithm.
69+
- [Google Recuriter Recommended Problems List](https://turingplanet.org/2020/09/18/leetcode_planning_list/#Binary_Search)

0 commit comments

Comments
 (0)