Skip to content

Commit 2ee5c12

Browse files
committed
Update some problem solutions for a better, easy-to-understand one
1 parent b2e1bb7 commit 2ee5c12

9 files changed

+156
-80
lines changed

leetcode/209.minimum-size-subarray-sum.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ fun minSubArrayLen(target: Int, nums: IntArray): Int {
1313
var currentSum = 0
1414
while (end < nums.size) {
1515
currentSum += nums[end]
16+
// We try to find the minimum size subarray sum, so we have to
17+
// shrink the window to become valid and minimum size!!
1618
while (currentSum - nums[start] >= target) {
1719
currentSum -= nums[start]
1820
start++

leetcode/226.invert-binary-tree.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ fun invertTree(root: TreeNode?): TreeNode? {
66
if (root == null) return null
77
if (root.left == null && root.right == null) return root
88
// We have to preserve the original left and right child here, in case of references changes after inverting.
9-
//f132820721
109

1110
// Wrong results:
1211
// root.left = invertTree(root.right)

leetcode/34.find-first-and-last-position-of-element-in-sorted-array.md

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ The key difference is the case that found, i.e. `nums[middle] == target`, the `m
1212
// 1. The first index, then the we found nothing
1313
// when keeping searching and idx will be our final result.
1414
[100,..., (777) 777, 777, 777,...,999]
15-
start mid end
15+
left mid right
1616
idx
1717

1818
// 2. Not the first or the last index
1919
[100,..., 777, 777, (777), 777,...,999]
20-
start mid end
20+
left mid right
2121
idx
2222

2323
// 3. The last index
2424
[100,..., 777, 777, 777, (777),...,999]
25-
start mid end
25+
left mid right
2626
idx
2727
```
2828

2929
For the above three possible cases, the first index will always be the left part or the current `mid`.
3030

31-
So we update the result index (`idx`) firstand keep searching the left part (updating `end`), if we find again, it means there exist an index prior to the current result.
31+
So we update the result index (`idx`) first and keep searching the left part (updating `right`), if we find again, it means there exist an index prior to the current result.
3232

3333

3434
```kotlin
@@ -41,36 +41,99 @@ fun searchRange(nums: IntArray, target: Int): IntArray {
4141

4242
private fun searchFirstIndex(nums: IntArray, target: Int): Int {
4343
var index = -1
44-
var start = 0
45-
var end = nums.size - 1
46-
while (start <= end) {
47-
val middle = start + (end - start) / 2
44+
var left = 0
45+
var right = nums.size - 1
46+
while (left <= right) {
47+
val middle = left + (right - left) / 2
4848
// We update the index first
4949
if (nums[middle] == target) index = middle
5050
// Then keep searching if there exists target that index < current result
5151
if (nums[middle] >= target) {
52-
end = middle - 1
52+
right = middle - 1
5353
} else {
54-
start = middle + 1
54+
left = middle + 1
5555
}
5656
}
5757
return index
5858
}
5959

6060
private fun searchLastIndex(nums: IntArray, target: Int): Int {
6161
var index = -1
62-
var start = 0
63-
var end = nums.size - 1
64-
while (start <= end) {
65-
val middle = start + (end - start) / 2
62+
var left = 0
63+
var right = nums.size - 1
64+
while (left <= right) {
65+
val middle = left + (right - left) / 2
6666
if (nums[middle] == target) index = middle
6767
if (nums[middle] <= target) {
68-
start = middle + 1
68+
left = middle + 1
6969
} else {
70-
end = middle - 1
70+
right = middle - 1
7171
}
7272

7373
}
7474
return index
7575
}
76+
```
77+
78+
> Nice explanation: https://medium.com/@lindingchi/binary-search-%E9%82%A3%E4%BA%9B%E8%97%8F%E5%9C%A8%E7%B4%B0%E7%AF%80%E8%A3%A1%E7%9A%84%E9%AD%94%E9%AC%BC-%E4%B8%89-%E5%BE%88%E5%A4%9A%E7%9B%B8%E5%90%8C%E7%9A%84%E6%83%85%E5%A2%83-c2215d1b9dc7
79+
80+
如果我們要找 `target=8` 的第一個位置,假設我們有三個 `8`,那麼 `middle` 可能會落在三個位置,那麼我們要怎麼縮限下一個搜尋的範圍:
81+
82+
```js
83+
8 8 8
84+
M
85+
86+
8 8 8
87+
M
88+
89+
// 這兩種情境第一個位置可能是當前 M 或者他的左邊,所以當 target <= nums[middle],我們要搜尋左半邊。
90+
91+
8 8 8
92+
M
93+
94+
// 第一個位置就是在 M 位置,這樣套用上述邏輯,最後 L 和 R 和 M 都會走到這個 8 的左邊位置,在執行一次程式,L 就會跑到第一個 8 然後跳出迴圈。
95+
96+
```
97+
98+
```kotlin
99+
// Implemented based on the above explanation post!!
100+
101+
fun searchRange(nums: IntArray, target: Int): IntArray {
102+
return intArrayOf(
103+
findStartingPosition(nums, target),
104+
findEndingPosition(nums, target)
105+
)
106+
}
107+
108+
private fun findStartingPosition(nums: IntArray, target: Int): Int {
109+
var found = false
110+
var left = 0
111+
var right = nums.size - 1
112+
while (left <= right) {
113+
val middle = left + (right - left) / 2
114+
if (nums[middle] == target) found = true
115+
if (nums[middle] >= target) {
116+
right = middle - 1
117+
} else {
118+
left = middle + 1
119+
}
120+
}
121+
return if (found) left else - 1
122+
}
123+
124+
private fun findEndingPosition(nums: IntArray, target: Int): Int {
125+
var found = false
126+
var left = 0
127+
var right = nums.size - 1
128+
while (left <= right) {
129+
val middle = left + (right - left) / 2
130+
if (nums[middle] == target) found = true
131+
if (target >= nums[middle]) {
132+
left = middle + 1
133+
} else {
134+
right = middle - 1
135+
}
136+
}
137+
return if (found) left - 1 else - 1
138+
}
76139
```

leetcode/35.search-insert-position.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## [35. Search Insert Position](https://leetcode.com/problems/search-insert-position/)
22

3+
We're going to find **all the numbers >= `target`, and return the smallest one.** (a.k.a., `left` pointer)
4+
35
```kotlin
46
fun searchInsert(nums: IntArray, target: Int): Int {
57
var left = 0

leetcode/5.longest-palindromic-substring.md

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ fun longestPalindrome(s: String): String {
66
var results = ""
77
for (i in 0 until s.length) {
88
for (j in i until s.length) {
9-
val substring = s.substring(i, j)
9+
val substring = s.substring(i..j)
10+
println("$i, $j, $substring")
1011
if (isPalindrome(substring)) {
11-
if (substring.length > results) {
12+
if (substring.length > results.length) {
1213
results = substring
1314
}
1415
}
1516
}
17+
println()
1618
}
19+
return results
1720
}
1821

1922
private fun isPalindrome(s: String): Boolean {
@@ -50,43 +53,47 @@ dp[i][j] = dp[i + 1][j - 1] && s[i] == s[j]
5053

5154
```kotlin
5255
fun longestPalindrome(s: String): String {
53-
val len = s.length
54-
val dp = Array(len) { _ -> BooleanArray(len)}
55-
56-
// Base case:
57-
// We will setup in the for loop below, so we don't do here.
58-
// Or we can do here, and REMEMBER to skip below.
59-
//
60-
// for (i in 0 until len) {
61-
// dp[i][i] = true
62-
// if (i + 1 < len) {
63-
// dp[i][i + 1] = (s[i] == s[i + 1])
64-
// }
65-
66-
// We will use start + offset to calculate the substring.
67-
var resultStart = 0
68-
var resultLength = 1
69-
70-
for (i in len - 1 downTo 0) {
71-
for (j in i + 1 until len) {
72-
// j - i < 2 means the length of substring is 1 or 2, this is
73-
// the base cases.
74-
dp[i][j] = (dp[i + 1][j - 1] || j - i < 2) && s[i] == s[j]
75-
76-
// If the current substring is palindrome, we update the longest palindrome result
77-
if (dp[i][j] == true) {
56+
val dp = Array(s.length) { _ -> BooleanArray(s.length) }
57+
var result = ""
58+
59+
// How do we iterate i,j? i expands toward left, j expends toward right.
60+
// We can iterate in this backward way:
61+
// for (i in len - 1 downTo 0) {
62+
// for (j in i + 1 until len) {
63+
64+
// Or foward way, mind the i, j order.
65+
for (j in 0 until s.length) {
66+
for (i in j downTo 0) {
67+
68+
// Base cases: when length is 1 or 2.
69+
dp[i][j] = if (i == j || i + 1 == j) s[i] == s[j]
70+
else dp[i + 1][j - 1] && s[i] == s[j]
71+
72+
if (dp[i][j]) {
7873
val currentLength = j - i + 1
79-
if (currentLength > resultLength) {
80-
resultLength = currentLength
81-
resultStart = i
82-
}
74+
if (currentLength > result.length) {
75+
result = s.substring(i..j)
76+
}
8377
}
8478
}
8579
}
86-
87-
return s.substring(resultStart, resultStart + resultLength)
80+
return result
8881
}
8982
```
83+
84+
```js
85+
s = "babad"
86+
87+
// Forward way
88+
i|0 |1 0|2 1 0|3 2 1 0|4 3 2 1 0
89+
j|0 |1 |2 |3 |4
90+
---------------------------------
91+
dp|O O X O X O O X O X O X X X X
92+
93+
// Backward
94+
i|4 |3 |2 |...
95+
j|4 |3 4|2 3 4|...
96+
```
9097
* **Time Complexity**: `O(n^2)`.
9198
* **Space Complexity**: `O(n^2)`.
9299

leetcode/516.longest-palindromic-subsequence.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ fun longestPalindromeSubseq(s: String): Int {
8787
// `j` will be the next character of `i` to end.
8888
for (i in n - 1 downTo 0) {
8989
for (j in i + 1 until n) {
90+
91+
// Or we can iterate from the beginning (mind the i, j order)
92+
// for (j in 0 until n) {
93+
// for (i in j downTo 0) {
9094
dp[i][j] = if (s[i] == s[j]) dp[i + 1][j - 1] + 2
9195
else max(dp[i + 1][j], dp[i][j - 1])
9296
}

leetcode/704.binary-search.md

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

33
```kotlin
44
fun search(nums: IntArray, target: Int): Int {
5-
var start = 0
6-
var end = nums.size - 1
7-
while (start <= end) {
8-
val middle = start + (end - start) / 2
5+
var left = 0
6+
var right = nums.size - 1
7+
while (left <= right) {
8+
val middle = left + (right - left) / 2
99
if (nums[middle] == target) return middle
1010
else if (nums[middle] > target) {
11-
end = middle - 1
11+
right = middle - 1
1212
} else {
13-
start = middle + 1
13+
left = middle + 1
1414
}
1515
}
1616
return -1

leetcode/735.asteroid-collision.md

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,35 @@
22

33
```kotlin
44
fun asteroidCollision(asteroids: IntArray): IntArray {
5+
// 30, -40
6+
// -5, -10
7+
// -5, 10
58
val stack = Stack<Int>()
69
for (i in 0 until asteroids.size) {
7-
val coming = asteroids[i]
8-
if (coming < 0) {
9-
if (stack.isEmpty()) {
10-
results.add(coming)
10+
val right = asteroids[i]
11+
var existInFinal = true
12+
while (stack.isNotEmpty() && stack.peek() > 0 && right < 0) {
13+
val left = stack.peek()
14+
if (left + right > 0) {
15+
existInFinal = false
16+
break
17+
} else if (left + right == 0) {
18+
existInFinal = false
19+
stack.pop()
20+
break
1121
} else {
12-
while (!stack.isEmpty()) {
13-
val existing = stack.peek()
14-
if (existing + coming == 0) {
15-
stack.pop()
16-
break
17-
} else if (existing + coming < 0) {
18-
stack.pop()
19-
if (stack.isEmpty()) {
20-
results.add(coming)
21-
}
22-
} else {
23-
break
24-
}
25-
}
22+
stack.pop()
2623
}
27-
} else if (coming > 0) {
28-
stack.push(coming)
24+
}
25+
if (existInFinal) {
26+
stack.push(right)
2927
}
3028
}
31-
val result = IntArray(stack.size)
29+
val results = IntArray(stack.size)
3230
val size = stack.size
33-
for (i in 0 untili size) {
34-
result[i] = stack.pop()
31+
for (i in size - 1 downTo 0) {
32+
results[i] = stack.pop()
3533
}
36-
return result
34+
return results
3735
}
3836
```

topics/searching.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ if (100% sure logic) {
6060
left = middle
6161
}
6262
```
63+
6364
* 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)`).
6465

6566
## Resources

0 commit comments

Comments
 (0)