Skip to content

Commit ac5867f

Browse files
committed
Update stack problem notes
1 parent 1462fb9 commit ac5867f

File tree

47 files changed

+1160
-494
lines changed

Some content is hidden

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

47 files changed

+1160
-494
lines changed

leetcode/1004.max-consecutive-ones-iii.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,26 @@ fun longestOnes(nums: IntArray, k: Int): Int {
3434
var right = 0
3535
var zeroCount = 0
3636
var maxLength = 0
37-
while (right < nums.size) {
37+
for (right in nums.indices) {
3838
if (nums[right] == 0) zeroCount++
3939
while (zeroCount > k) {
4040
if (nums[left] == 0) zeroCount--
4141
left++
4242
}
4343
maxLength = maxOf(maxLength, right - left + 1)
44-
right++
4544
}
4645
return maxLength
4746
}
4847
```
4948

5049
* **Time Complexity**: `O(n)`.
51-
* **Space Complexity**: `O(1)`.
50+
* **Space Complexity**: `O(1)`.
51+
52+
## Dynamic Programming
53+
`dp[i][k]` represents the longest substring of `1's` ending at index `i` with at most `k` zeros.
54+
55+
And the state transition is:
56+
* If `nums[i] == 1`, `dp[i][k] = dp[i - 1][k] + 1`.
57+
* If `nums[i] == 0`, `dp[i][k] = dp[i - 1][k - 1] + 1`.
58+
59+
* **Time Complexity**: `O(n * k)`.

leetcode/1021.remove-outermost-parentheses.md

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

33
## Counting
44
We maintain a left parenthsis count, it will be the outermost when:
5-
1. It's the first left parenthsis. it's the left parenthsis that makes the count to 1.
6-
2. It's the last left parenthsis, it's the right parenthsis that makes the count to 0.
5+
1. It's the first left parenthsis. it's the left parenthsis that makes the count `0 -> 1`.
6+
2. It's the last left parenthsis, it's the right parenthsis that makes the count `1 -> 0`.
77

88
If it's not the outermost, we append it to the result.
99
```kotlin
@@ -12,11 +12,11 @@ fun removeOuterParentheses(s: String): String {
1212
val result = StringBuilder()
1313
for (c in s) {
1414
if (c == '(') {
15-
if (leftCount != 0) result.append(c.toString())
15+
if (leftCount > 0) result.append(c.toString())
1616
leftCount++
1717
} else {
1818
leftCount--
19-
if (leftCount != 0) result.append(c.toString())
19+
if (leftCount > 0) result.append(c.toString())
2020
}
2121
}
2222
return result.toString()

leetcode/1190.reverse-substrings-between-each-pair-of-parentheses.md

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,37 @@
11
# [1190. Reverse Substrings Between Each Pair of Parentheses](https://leetcode.com/problems/reverse-substrings-between-each-pair-of-parentheses/)
22

3+
The problem is to reverse the string between each pair of parentheses.
4+
```js
5+
(u(love)i)
6+
(u evol i)
7+
iloveu
8+
```
9+
10+
## Breakdowns
11+
> 1. If we don't have nested parentheses `s = "(abc)def(ghi)"`?
12+
13+
We can simply identify the substrings inside `()`, reverse it and remove the parentheses.
14+
315
## Stack
4-
We use stack for the nested structure and reverse the string between each pair of parentheses.
16+
We use stack for the nested structure and reverse the string when encountering the `)` parentheses:
17+
1. Push the character to the stack until we encounter `)`.
18+
2. When we encounter `)`, we pop the characters from the stack until we encounter `(`.
19+
3. **Reverse the string** and push back to the stack.
20+
4. Continue until the end of the string, then concatenate the stack contents.
21+
522
```js
623
s = "((ab)z)i"
724
*
825
stack = (, (, a, b
926

10-
// We pop b, a to form "ba" and push back to stack
27+
// We pop `b`, `a` to form "ba" and push back to stack
1128
stack = (, ba
1229

1330
s = "((ab)z)i"
1431
*
1532
stack = (, ba, z
1633

17-
// We pop z, ba to form "zab" and push back to stack
34+
// We pop `z`, `ba` to form "zab" and push back to stack
1835
stack = zab
1936

2037
s = "((ab)z)i"
@@ -25,6 +42,28 @@ stack = zab, i
2542
return "zabi"
2643
```
2744
45+
There is a common pitfall that we forget to **reverse the string when poping from the stack**.
46+
47+
```js
48+
s = "(u(love)i)"
49+
*
50+
stack = (, u, (, l, o, v, e, )
51+
52+
// We pop `e`, `v`, `o`, `l` to form "evol" and push back to stack
53+
stack = (, u, evol
54+
55+
// Keep iterating the string
56+
stack = (, u, evol, i, )
57+
58+
// We pop `i`
59+
stack = (, u, evol,
60+
temp = i
61+
62+
// We pop `evol`, `u` to form "iloveu" and push back to stack
63+
stack = (, u
64+
temp = ilove // We should reverse the string when poping `evol` from the stack!!
65+
```
66+
2867
```kotlin
2968
fun reverseParentheses(s: String): String {
3069
val stack = Stack<String>()
@@ -48,9 +87,52 @@ fun reverseParentheses(s: String): String {
4887
* **Time Complexity:** `O(n^2)`, we iterate through the string and reverse the string between each pair of parentheses when poping from the stack.
4988
* **Space Complexity:** `O(n)`
5089
90+
## Two Pointers
5191
> TODO: There is another solution that traverse the string based on the nested structure:
5292
>
5393
> ```js
5494
> (ABCDE(FIJKL)MNOPQ)
5595
> 1 --> 3 <-- 2 --> 4
56-
> ABCDE, LKJIHF, MNOPQ
96+
> ABCDE, LKJIHF, MNOPQ
97+
98+
99+
> The following was copied from ChatGPT, need to verify.
100+
Instead of a stack, we can use an index-mapping approach to efficiently swap characters:
101+
1. Use a stack to track opening parentheses indices.
102+
2. Create an array to store matching parentheses positions.
103+
3. Use two pointers to process the string in the correct order based on matching indices.
104+
105+
```kotlin
106+
fun reverseParentheses(s: String): String {
107+
val n = s.length
108+
val pair = IntArray(n)
109+
val stack = ArrayDeque<Int>()
110+
111+
// Step 1: Build a map of matching parentheses indices
112+
for (i in s.indices) {
113+
if (s[i] == '(') stack.push(i)
114+
if (s[i] == ')') {
115+
val j = stack.pop()
116+
pair[i] = j
117+
pair[j] = i
118+
}
119+
}
120+
121+
// Step 2: Process the string using two pointers
122+
val result = StringBuilder()
123+
var i = 0
124+
var direction = 1
125+
126+
while (i < n) {
127+
if (s[i] == '(' || s[i] == ')') {
128+
i = pair[i] // Jump to the matching parenthesis
129+
direction = -direction // Reverse direction
130+
} else {
131+
result.append(s[i])
132+
}
133+
i += direction
134+
}
135+
136+
return result.toString()
137+
}
138+
```

leetcode/1249.minimum-remove-to-make-valid-parentheses.md

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
# [1249. Minimum Remove to Make Valid Parentheses](https://leetcode.com/problems/minimum-remove-to-make-valid-parentheses/)
22

3-
## Clarification Questions
4-
* No, it's clear from problem description.
5-
6-
## Test Cases
7-
### Normal Cases
8-
```
9-
Input:
10-
Output:
11-
```
12-
### Edge / Corner Cases
13-
*
14-
```
15-
Input:
16-
Output:
17-
```
18-
193
## Stack
204
We use stack to match the parentheses, and we store the index when any unmatched parentheses are found.
215
```kotlin
@@ -100,7 +84,6 @@ fun minRemoveToMakeValid(s: String): String {
10084
}
10185
var rightCount = 0
10286
for (i in s.length - 1 downTo 0) {
103-
if (toRemove.contains(i)) continue // Remember to skip the invalid left parentheses
10487
val c = s[i]
10588
if (c == ')') rightCount++
10689
else if (c == '(') rightCount--

leetcode/129.sum-root-to-leaf-numbers.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,10 @@ For `2 -> 3 -> 5`, we traverse to build the number `235`:
3535
> Source: https://leetcode.cn/problems/sum-root-to-leaf-numbers/solutions/464666/qiu-gen-dao-xie-zi-jie-dian-shu-zi-zhi-he-by-leetc/
3636
3737
Steps by steps:
38-
![](../media/129.sum-root-to-leaf-numbers-1.png)
39-
![](../media/129.sum-root-to-leaf-numbers-2.png)
40-
![](../media/129.sum-root-to-leaf-numbers-3.png)
41-
![](../media/129.sum-root-to-leaf-numbers-4.png)
38+
![](../media/129.sum-root-to-leaf-numbers-0-1.png)
39+
![](../media/129.sum-root-to-leaf-numbers-0-2.png)
4240
```kotlin
41+
// Bottom-up approach
4342
fun sumNumbers(root: TreeNode?): Int {
4443
return dfs(root, 0)
4544
}
@@ -56,6 +55,25 @@ private fun dfs(root: TreeNode?, num: Int): Int {
5655
val right = dfs(root.right, newNum)
5756
return left + right
5857
}
58+
59+
// Or top-down approach
60+
private var sum = 0
61+
62+
fun sumNumbers(root: TreeNode?): Int {
63+
dfs(root, 0)
64+
return sum
65+
}
66+
67+
private fun dfs(root: TreeNode?, num: Int) {
68+
if (root == null) return
69+
val newNum = num * 10 + root.`val`
70+
if (root.left == null && root.right == null) {
71+
sum += newNum
72+
return
73+
}
74+
dfs(root.left, newNum)
75+
dfs(root.right, newNum)
76+
}
5977
```
6078

6179
* **Time Complexity:** `O(n)`

leetcode/1405.longest-happy-string.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ fun longestDiverseString(a: Int, b: Int, c: Int): String {
3737
if (count[i1] > 0) maxHeap.add(i1)
3838
if (count[i2] > 0) maxHeap.add(i2)
3939
}
40-
// Append the last one character at most 2 times
4140
if (maxHeap.isNotEmpty()) {
4241
val i1 = maxHeap.poll()
42+
// Append the last one character at most 2 times
4343
val c1 = min(count[i1], 2)
4444
str.append(('a' + i1).toString().repeat(c1))
4545
}

leetcode/1443.minimum-time-to-collect-all-apples-in-a-tree.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,37 @@
1515
```
1616

1717
## DFS
18-
To find the minimum time to collect all apples, we can use DFS to traverse the tree. We define `dfs(root)` as the time to collect all apples in the subtree rooted at `root`. For each root, we can calculate the time to collect all apples in the subtree (postorder), then add the time to traverse from the root to the subtree if there are apples in the subtree, which is `dfs(root) = dfs(child) + 2` if `dfs(child) > 0` (there are apples in the subtree), or dfs(root) = 0 if `dfs(child) = 0` (there are no apples in the subtree).
18+
To find the minimum time to collect all apples, we can use DFS to traverse the tree. We define `dfs(root)` as the time to collect all apples in the subtree rooted at `root`. For each root, we can calculate the time to collect all apples in the subtree (postorder), then add the time to traverse from the root to the subtree if there are apples in the subtree.
19+
20+
```js
21+
dfs(root)
22+
+2 or 0 / \ +2 or 0
23+
dfs(left) dfs(right)
24+
```
25+
26+
`dfs(root)` will be:
27+
1. The sum of `2 + dfs(child)` if there are apples in the subtree of the child node or the child node itself.
28+
2. `0` if there are no any apples in the subtree of the child node, including child node itself.
1929

2030
There are several possible cases:
21-
* There are apples in the subtree: `dfs(root) = 2 + dfs(left) = 2 + 2 = 4`
31+
* There are apples in the subtree in child node, that is `dfs(left) > 0`, so `dfs(root) = 2 + dfs(left) = 2 + 2 = 4`
2232
```js
2333
dfs(root)
24-
/ 2
34+
/ +2
2535
X dfs(left) = 2
2636
/ \
2737
O X
2838

2939
dfs(root)
30-
/ 2
40+
/ +2
3141
O dfs(left) = 2
3242
/ \
3343
O X
3444
```
35-
* There are no apples in the subtree, but there is an apple in the left or right child: `dfs(root) = 2 + dfs(left) = 2 + 0 = 2`
45+
* There are no apples in the subtree of child node `dfs(left) == 0`, but there is an apple in the left or right child: `dfs(root) = 2 + dfs(left) = 2 + 0 = 2`
3646
```js
3747
dfs(root)
38-
/ 2
48+
/ +2
3949
O dfs(left) = 0
4050
/ \
4151
X X
@@ -49,7 +59,7 @@ There are several possible cases:
4959
X X
5060
```
5161

52-
* Root has an apple or not: Here we don't care about if the root has an apple or not, because the time to collect the apple is `0`. And the time to collect the apple in current root will be calculated in the parent node, or `0` for top-level root.
62+
* Root has an apple or not: Here we **don't care about** if the root has an apple or not, because the time to collect the apple is `0`. And the time to collect the apple in current root will be calculated in the parent node, or `0` for top-level root.
5363

5464
```kotlin
5565
fun minTime(n: Int, edges: Array<IntArray>, hasApple: List<Boolean>): Int {
@@ -83,7 +93,7 @@ private fun dfs(
8393
if (child != parent) {
8494
val childTime = dfs(tree, child, root, hasApple)
8595
/**
86-
* childTime > 0: There are apples in the subtree
96+
* childTime > 0: There are apples in the subtree of child node
8797
* root dfs(root) = 2 + dfs(child) = 4
8898
* /
8999
* X dfs(child) = 2
@@ -98,7 +108,7 @@ private fun dfs(
98108
*/
99109
if (childTime > 0) {
100110
totalTime += childTime + 2
101-
} else {
111+
} else if (hasApple[child]) {
102112
totalTime += 2
103113
}
104114
}

0 commit comments

Comments
 (0)