Skip to content

Commit 9a987ff

Browse files
committed
Scan addendum: 21 missed problems + alternatives across ch3-17; fix Alien Dictionary post-order bug, ch03 dead link, ch02 duplicated-page corruption
1 parent 28ded9f commit 9a987ff

35 files changed

Lines changed: 4724 additions & 234 deletions

‎CodingInterviewFightClub/src/SUMMARY.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
- [3.5 Insert Interval](ch03-arrays/insert-interval.md)
5757
- [3.6 Rotate Image](ch03-arrays/rotate-image.md)
5858
- [3.7 Spiral Matrix](ch03-arrays/spiral-matrix.md)
59+
- [3.8 Trapping Rain Water](ch03-arrays/trapping-rain-water.md)
60+
- [3.9 Container With Most Water](ch03-arrays/container-with-most-water.md)
61+
- [3.10 Product Of Array Except Self](ch03-arrays/product-of-array-except-self.md)
5962
- [2.12 Closest Subsequence Sum](ch02-dynamic-programming/closest-subsequence-sum.md)
6063

6164
- [4. Linked Lists](ch04-linked-lists/index.md)
@@ -65,6 +68,7 @@
6568
- [4.3 Merge Two Sorted Lists](ch04-linked-lists/merge-two-sorted-lists.md)
6669
- [4.4 Remove Nth Node From End](ch04-linked-lists/remove-nth-node-from-end.md)
6770
- [4.5 Linked List Cycle II](ch04-linked-lists/linked-list-cycle-ii.md)
71+
- [4.6 Find The Duplicate Number](ch04-linked-lists/find-the-duplicate-number.md)
6872

6973
- [5. Trees](ch05-trees/index.md)
7074
- [5.0 Pattern Primer: The Recursive Data Structure](ch05-trees/pattern-primer.md)
@@ -74,6 +78,9 @@
7478
- [5.4 Binary Tree Maximum Path Sum](ch05-trees/binary-tree-maximum-path-sum.md)
7579
- [5.5 Serialize And Deserialize Binary Tree](ch05-trees/serialize-and-deserialize-binary-tree.md)
7680
- [5.6 Binary Tree Inorder Traversal (Iterative)](ch05-trees/binary-tree-inorder-traversal-iterative.md)
81+
- [5.7 Construct Tree From Preorder And Inorder](ch05-trees/construct-binary-tree-from-preorder-and-inorder.md)
82+
- [5.8 Binary Tree Right Side View](ch05-trees/binary-tree-right-side-view.md)
83+
- [5.9 Path Sum III](ch05-trees/path-sum-iii.md)
7784

7885
- [6. Graphs](ch06-graphs/index.md)
7986
- [6.0 Pattern Primer: The Seven Engines](ch06-graphs/pattern-primer.md)
@@ -84,6 +91,9 @@
8491
- [6.5 Cheapest Flights With K Stops](ch06-graphs/cheapest-flights-with-k-stops.md)
8592
- [6.6 Min Cost To Connect All Points](ch06-graphs/min-cost-to-connect-all-points.md)
8693
- [6.7 Strongly Connected Components](ch06-graphs/strongly-connected-components.md)
94+
- [6.8 Alien Dictionary](ch06-graphs/alien-dictionary.md)
95+
- [6.9 Redundant Connection](ch06-graphs/redundant-connection.md)
96+
- [6.10 Flood Fill](ch06-graphs/flood-fill.md)
8797

8898
- [7. Heaps & Priority Queues](ch07-heaps/index.md)
8999
- [7.0 Pattern Primer: The Lazy Sorted Structure](ch07-heaps/pattern-primer.md)
@@ -104,6 +114,7 @@
104114
- [8.5 Largest Rectangle In Histogram](ch08-stacks/largest-rectangle-in-histogram.md)
105115
- [8.6 Evaluate Reverse Polish Notation](ch08-stacks/evaluate-reverse-polish-notation.md)
106116
- [8.7 Remove K Digits](ch08-stacks/remove-k-digits.md)
117+
- [8.8 Decode String](ch08-stacks/decode-string.md)
107118

108119
- [9. Strings](ch09-strings/index.md)
109120
- [9.0 Pattern Primer: The Three Lenses](ch09-strings/pattern-primer.md)
@@ -114,6 +125,7 @@
114125
- [9.5 Longest Common Prefix](ch09-strings/longest-common-prefix.md)
115126
- [9.6 Reverse Words In A String](ch09-strings/reverse-words-in-a-string.md)
116127
- [9.7 Validate IP Address](ch09-strings/validate-ip-address.md)
128+
- [9.8 Find The Index Of The First Occurrence (Rabin-Karp)](ch09-strings/find-the-index-of-the-first-occurrence.md)
117129

118130
- [10. Hash Tables & Sets](ch10-hash-tables/index.md)
119131
- [10.0 Pattern Primer: O(1) Lookup, Three Moves](ch10-hash-tables/pattern-primer.md)
@@ -124,6 +136,7 @@
124136
- [10.5 First Unique Character](ch10-hash-tables/first-unique-character.md)
125137
- [10.6 Design HashMap](ch10-hash-tables/design-hash-map.md)
126138
- [10.7 Roman To Integer](ch10-hash-tables/roman-to-integer.md)
139+
- [10.8 Subarray Sum Equals K](ch10-hash-tables/subarray-sum-equals-k.md)
127140

128141
- [11. Greedy](ch11-greedy/index.md)
129142
- [11.0 Pattern Primer: The Local Choice, Defended](ch11-greedy/pattern-primer.md)
@@ -134,6 +147,7 @@
134147
- [11.5 Car Fleet](ch11-greedy/car-fleet.md)
135148
- [11.6 Task Scheduler](ch11-greedy/task-scheduler.md)
136149
- [11.7 Minimum Number Of Refueling Stops](ch11-greedy/minimum-number-of-refueling-stops.md)
150+
- [11.8 Best Time To Buy And Sell Stock II](ch11-greedy/best-time-to-buy-and-sell-stock-ii.md)
137151

138152
- [12. Backtracking](ch12-backtracking/index.md)
139153
- [12.0 Pattern Primer: DFS With an Undo Button](ch12-backtracking/pattern-primer.md)
@@ -144,6 +158,9 @@
144158
- [12.5 Palindrome Partitioning](ch12-backtracking/palindrome-partitioning.md)
145159
- [12.6 Restore IP Addresses](ch12-backtracking/restore-ip-addresses.md)
146160
- [12.7 Sudoku Solver](ch12-backtracking/sudoku-solver.md)
161+
- [12.8 Combination Sum](ch12-backtracking/combination-sum.md)
162+
- [12.9 Partition To K Equal Sum Subsets](ch12-backtracking/partition-to-k-equal-sum-subsets.md)
163+
- [12.10 Next Permutation](ch12-backtracking/next-permutation.md)
147164

148165
- [13. Tries](ch13-tries/index.md)
149166
- [13.0 Pattern Primer: The Prefix Structure](ch13-tries/pattern-primer.md)
@@ -164,6 +181,7 @@
164181
- [14.5 H-Index](ch14-sorting/h-index.md)
165182
- [14.6 Russian Doll Envelopes](ch14-sorting/russian-doll-envelopes.md)
166183
- [14.7 Top K Frequent Elements (QuickSelect)](ch14-sorting/top-k-frequent-elements-quickselect.md)
184+
- [14.8 Sort Colors](ch14-sorting/sort-colors.md)
167185

168186
- [15. Sliding Window](ch15-sliding-window/index.md)
169187
- [15.0 Pattern Primer: The Moving Window](ch15-sliding-window/pattern-primer.md)
@@ -174,6 +192,7 @@
174192
- [15.5 Minimum Size Subarray Sum](ch15-sliding-window/minimum-size-subarray-sum.md)
175193
- [15.6 Sliding Window Maximum](ch15-sliding-window/sliding-window-maximum.md)
176194
- [15.7 Max Consecutive Ones III](ch15-sliding-window/max-consecutive-ones-iii.md)
195+
- [15.8 Permutation In String](ch15-sliding-window/permutation-in-string.md)
177196

178197
- [16. Bit Manipulation](ch16-bit-manipulation/index.md)
179198
- [16.0 Pattern Primer: The Bit Identities](ch16-bit-manipulation/pattern-primer.md)
@@ -194,6 +213,8 @@
194213
- [17.5 Shortest Path Visiting All Nodes](ch17-advanced-graphs/shortest-path-visiting-all-nodes.md)
195214
- [17.6 Reorder Routes To City Zero](ch17-advanced-graphs/reorder-routes-to-make-all-paths-lead-to-city-zero.md)
196215
- [17.7 Evaluate Division](ch17-advanced-graphs/evaluate-division.md)
216+
- [17.8 Bellman-Ford](ch17-advanced-graphs/bellman-ford.md)
217+
- [17.9 Reconstruct Itinerary](ch17-advanced-graphs/reconstruct-itinerary.md)
197218

198219
- [18. Design & Caches](ch18-design-caches/index.md)
199220
- [18.0 Pattern Primer: Composing Structures](ch18-design-caches/pattern-primer.md)

‎CodingInterviewFightClub/src/ch02-dynamic-programming/partition-equal-subset-sum.md‎

Lines changed: 0 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -38,239 +38,6 @@ $$
3838

3939
meaning "reachable before (skip x)" or "reachable by taking x (from state s−x)". Base: `dp[0] = true` (empty subset sums to 0). Answer: `dp[target]`.
4040

41-
**Why descending?** Exactly the 0/1 argument: reading `dp[s - x]` in descending order guarantees it reflects *previous* items only, so each number is used at most once. (Try ascending and `nums = [1, 1]`, target 1 → you'd get `true` — the bug of double-using.)
42-
43-
## Approach 1 — Brute force
44-
45-
Enumerate all $2^n$ subsets, check sums. $2^{200}$ — the largest number most people will ever see in an interview setting. DP is the *only* reasonable answer.
46-
47-
## Approach 2 — Top-down memoized DFS (the repo's first version)
48-
49-
```kotlin
50-
/**
51-
* @param nums the array of positive integers
52-
* @return true iff nums can be split into two subsets of equal sum
53-
*/
54-
fun canPartition(nums: IntArray): Boolean {
55-
val sum = nums.sum()
56-
if (sum % 2 != 0) return false
57-
val target = sum / 2
58-
59-
// memo[i][s] = -1 unknown, 0 false, 1 true
60-
val memo = Array(nums.size) { IntArray(target + 1) { -1 } }
61-
62-
/**
63-
* @param i the current item index
64-
* @param currentSum the running sum of the chosen subset
65-
* @return true iff a subset of nums[i..] can reach `target` from currentSum
66-
*/
67-
fun dfs(i: Int, currentSum: Int): Boolean = when {
68-
currentSum == target -> true
69-
i == nums.size -> false
70-
memo[i][currentSum] != -1 -> memo[i][currentSum] == 1
71-
else -> (
72-
dfs(i + 1, currentSum + nums[i]) || // take nums[i]
73-
dfs(i + 1, currentSum) // skip nums[i]
74-
).also { memo[i][currentSum] = if (it) 1 else 0 }
75-
}
76-
return dfs(0, 0)
77-
}
78-
```
79-
80-
## Approach 3 — Bottom-up boolean knapsack (optimal)
81-
82-
```kotlin
83-
/**
84-
* @param nums the array of positive integers
85-
* @return true iff nums can be split into two subsets of equal sum
86-
*/
87-
fun canPartitionBottomUp(nums: IntArray): Boolean {
88-
val sum = nums.sum()
89-
if (sum % 2 != 0) return false
90-
91-
val target = sum / 2
92-
val dp = BooleanArray(target + 1).apply { this[0] = true } // empty subset sums to 0
93-
94-
for (num in nums) {
95-
for (s in target downTo num) { // DESCENDING: 0/1 usage of each number
96-
dp[s] = dp[s] || dp[s - num]
97-
}
98-
}
99-
return dp[target]
100-
}
101-
```
102-
103-
```java
104-
public class PartitionEqualSubsetSum {
105-
/**
106-
* @param nums the array of positive integers
107-
* @return true iff nums can be split into two subsets of equal sum
108-
*/
109-
public boolean canPartition(int[] nums) {
110-
int sum = 0;
111-
for (int x : nums) sum += x;
112-
if (sum % 2 != 0) return false;
113-
114-
int target = sum / 2;
115-
boolean[] dp = new boolean[target + 1];
116-
dp[0] = true; // empty subset sums to 0
117-
118-
for (int num : nums) {
119-
for (int s = target; s >= num; s--) { // descending: 0/1 semantics
120-
dp[s] = dp[s] || dp[s - num];
121-
}
122-
}
123-
return dp[target];
124-
}
125-
}
126-
```
127-
128-
```cpp
129-
#include <vector>
130-
131-
class PartitionEqualSubsetSum {
132-
public:
133-
/**
134-
* @param nums the array of positive integers
135-
* @return true iff nums can be split into two subsets of equal sum
136-
*/
137-
bool canPartition(const std::vector<int>& nums) {
138-
int sum = 0;
139-
for (int x : nums) sum += x;
140-
if (sum % 2 != 0) return false;
141-
142-
int target = sum / 2;
143-
std::vector<bool> dp(target + 1, false);
144-
dp[0] = true;
145-
146-
for (int num : nums) {
147-
for (int s = target; s >= num; s--) { // descending: 0/1 semantics
148-
dp[s] = dp[s] || dp[s - num];
149-
}
150-
}
151-
return dp[target];
152-
}
153-
};
154-
```
155-
156-
```python
157-
def can_partition(nums: list[int]) -> bool:
158-
"""
159-
@param nums: the array of positive integers
160-
@return: True iff nums can be split into two subsets of equal sum
161-
"""
162-
total = sum(nums)
163-
if total % 2 != 0:
164-
return False
165-
166-
target = total // 2
167-
dp = [False] * (target + 1)
168-
dp[0] = True # empty subset sums to 0
169-
170-
for num in nums:
171-
for s in range(target, num - 1, -1): # descending: 0/1 semantics
172-
dp[s] = dp[s] or dp[s - num]
173-
return dp[target]
174-
```
175-
176-
```rust
177-
impl Solution {
178-
/// @param nums the array of positive integers
179-
/// @return true iff nums can be split into two subsets of equal sum
180-
pub fn can_partition(nums: Vec<i32>) -> bool {
181-
let sum: i32 = nums.iter().sum();
182-
if sum % 2 != 0 {
183-
return false;
184-
}
185-
let target = (sum / 2) as usize;
186-
let mut dp = vec![false; target + 1];
187-
dp[0] = true; // empty subset sums to 0
188-
189-
for num in nums {
190-
let mut s = target;
191-
while s >= num as usize { // descending: 0/1 semantics
192-
dp[s] = dp[s] || dp[s - num as usize];
193-
s -= 1;
194-
}
195-
}
196-
dp[target]
197-
}
198-
}
199-
```
200-
201-
## Dry run
202-
203-
**Input:** `nums = [1, 5, 11, 5]`. `sum = 22`, `target = 11`.
204-
205-
```
206-
dp (booleans over sums 0..11):
207-
initial: T F F F F F F F F F F F
208-
after 1: T T F F F F F F F F F F (1 reachable)
209-
after 5: T T F F F F T T F F F F (5 and 6 reachable)
210-
after 11: T T F F F F T T F F F T (11 reachable -> dp[11]=true already!)
211-
after 5 (2nd): T T F F F T T T T T T T (5,6,7,8,10,11 reachable)
212-
Answer: dp[11] = true ✓ (subset {11} or {1,5,5})
213-
```
214-
215-
Trace the interesting update (second `5`, `s = 11`):
216-
217-
```
218-
s=11: dp[11] = dp[11] || dp[6] = true || true -> stays true (already reachable via {11})
219-
s=10: dp[10] = dp[10] || dp[5] = false || true -> true ({5,5})
220-
```
221-
222-
**Why descending matters (the bug to dodge):** with `nums = [1, 1]` and `target = 1`, an ascending loop would do: `s=1: dp[1] = dp[1] || dp[0] = true` — fine so far; but for `target = 2` (nums=[1,1], sum=2, target=1 — no, target is 1)... take `nums=[2,2]`, target=2: descending: s=2: dp[2]=dp[0]=true (one 2). ✓. Ascending: s=2: dp[2] = dp[0] = true — but then... still only one pass through nums, so it's fine for one item per outer iteration. The real contamination: `nums=[2]`, target=4? Not applicable (sum 2). The classic failure: `nums=[1,1]`, sum=2, target=1: both loops give true correctly. Better example: `nums = [2, 4]`, target=3: nothing reaches 3 → false either way. Hmm — the descending requirement is really about *within one outer iteration*: `nums=[2, 2, 4]`, target = 4: descending: after first 2: dp[2]=T. After second 2: s=4: dp[4]=dp[2](from first 2 only)=T... wait descending from target: s=4: dp[4]||dp[2]=T → dp[4]=true. But that used BOTH 2s (2+2=4)! And that's *correct* — each number used once, and there are two 2s. OK: the classic contamination example is `nums = [1, 1]` with target = 1 via *ascending within the same item* — but each outer iteration is one item, and ascending within one item: for `num=1, target=1`: s=1: dp[1]=dp[1]||dp[0]=true. Only one item processed, correct. The actual failure needs TWO copies of the same value: `nums=[1,1,1]`, target=3: ascending never reuses the *same* item because each item is one outer pass...
223-
224-
Hold on — the real issue: ascending reuses the same item within one outer pass. E.g. `nums = [2]`, `target = 4`: descending: s=4: dp[4]=dp[2]=false; s=3..2: dp[2]=dp[0]=true. Result dp[4]=false ✓ (one 2 can't make 4). Ascending: s=2: dp[2]=true; s=3: dp[3]=dp[1]=false; s=4: dp[4]=dp[2]=true ← WRONG! The same single item 2 was used twice. That's the correct bug example: `canPartition([2])` with sum=2, target=1 — not applicable. Since target = sum/2 and the array must be non-empty with sum even, `[2]` gives target 1, and descending/ascending both give false. Hmm, target = sum/2 ≤ sum - min... for `[2, 2]`, sum=4, target=2: descending: after first 2: dp[2]=true. after second 2: s=2: dp[2]=dp[2]||dp[0]=true. ✓. ascending: same result. The contamination needs target > sum of all items... but target = half the sum, so if all items are < target, multiple items are needed, and within ONE item ascending can self-stack: `[2, 2, 2, 2]` sum=8 target=4. Descending: after 1st 2: dp[2]=T. 2nd: s=4: dp[4]=dp[2]=T (uses both 2s — correct). s=3,2: dp[2] stays. 3rd: s=4: dp[4] already T. ✓ true (2+2). Ascending with 1st 2: s=2: T; s=3: dp[3]=dp[1]=F; s=4: dp[4]=dp[2]=T ← TRUE but with only ONE item?! [2] can't make 4 with one copy. But wait the array has four 2s, so dp[4]=true is correct anyway. Ugh — the clean demonstration needs an array where ascending gives true but the correct answer is false. `nums=[2]` target=1 → no. `nums=[2,2]` target=2 → both true. `nums=[2,4]` sum=6 target=3 → no. `nums=[2,2,2]` sum=6 target=3 → no reachable sum 3. `nums=[2,2,2,2]`? true. `nums=[3,3]` target=3: ascending: s=3: dp[3]=dp[0]=T ✓ true. `nums=[4,4]` target=4: true. It seems for even-total arrays the ascending bug rarely flips the answer on the full array... The real flips: `nums=[2,4,6]` sum=12 target=6: descending: after 2: dp[2]; after 4: dp[4],dp[6]=dp[2] → T (2+4) ✓. ascending: after 2: dp[2],dp[3]?no, dp[4]=dp[2]→T! Wait ascending with item 2 alone reaches dp[4] via dp[2] (self-stack): dp[2]=T (s=2), s=3: dp[3]=dp[1]=F, s=4: dp[4]=dp[2]=T → "4 reachable" with only the item 2 (twice)! Then after 4: s=6: dp[6]=dp[2]=T → still true (2+4 correct). Hmm answer stays true.
225-
226-
The canonical counterexample where ascending breaks the FINAL answer: need an array where target is reachable only by double-using a single item and no valid combination exists. sum even, target=sum/2. E.g. `nums=[2, 2]` — no. `nums=[1, 5]` sum=6 target=3: ascending item 1: s=1: dp[1]=T, s=2: dp[2]=dp[1]=T, s=3: dp[3]=dp[2]=T → "3 reachable" — but [1,5] can't make 3! dp[3]=true → wrong TRUE! Descending item 1: s=3: dp[3]=dp[2]=F, s=2: dp[2]=dp[1]=F, s=1: dp[1]=dp[0]=T → dp[3]=false ✓, then item 5: s=3: dp[3]=dp[3]||dp[-2] → false. Final false ✓ correct.
227-
228-
So `nums=[1,5]` is the clean example. I'll use that in the dry run as the "descending vs ascending" demonstration.
229-
230-
Let me fix the dry-run section in the page I'm about to write — I haven't written it yet. Good. I'll include nums=[1,5] example.<|end▁of▁thinking|>
231-
232-
<||DSML||tool_calls>
233-
<||DSML||invoke name="write">
234-
<||DSML||parameter name="content" string="true"># 2.6 Partition Equal Subset Sum
235-
236-
> **Source:** [`src/main/kotlin/dynamic_programming/PartitionEqualSubsetSum.kt`](https://github.com/arpanpathak/AdvancedAlgorithmPatterns/blob/main/src/main/kotlin/dynamic_programming/PartitionEqualSubsetSum.kt)
237-
> **Pattern:** subset-sum reachability (boolean knapsack) · **Core page**
238-
239-
## The Problem
240-
241-
Given a non-empty array `nums` of positive integers, can you partition it into two subsets with **equal sums**?
242-
243-
- Constraints: $1 \le n \le 200$, $1 \le nums[i] \le 100$ → total sum ≤ 20,000.
244-
245-
## Examples
246-
247-
```
248-
Input: nums = [1, 5, 11, 5] -> true (partition {1,5,5} and {11}; both sum to 11)
249-
Input: nums = [1, 2, 3, 5] -> false (total 11 is odd, impossible)
250-
Input: nums = [1, 2, 5] -> false (even total 8, but no subset sums to 4)
251-
```
252-
253-
## Intuition — reduce to subset-sum
254-
255-
Two observations collapse the problem:
256-
257-
1. **The target is forced.** If the total sum $S$ is odd, an equal split is impossible → `false` immediately. Otherwise both halves must sum to $S/2$.
258-
2. **The question becomes:** does *any* subset of `nums` sum to exactly $S/2$? (The other half is whatever's left — automatically $S/2$.)
259-
260-
That's **subset-sum**, which is 0/1 knapsack with `value == weight` and a boolean question. State:
261-
262-
$$
263-
dp[s] = \text{can some subset of the items seen so far sum to exactly } s
264-
$$
265-
266-
Recurrence (per item `x`, descending over sums — the 0/1 discipline from [2.4](zero-one-knapsack.md)):
267-
268-
$$
269-
dp[s] = dp[s] \;\lor\; dp[s - x]
270-
$$
271-
272-
meaning "reachable before (skip x)" or "reachable by taking x (from state s−x)". Base: `dp[0] = true` (empty subset sums to 0). Answer: `dp[target]`.
273-
27441
**Why descending?** Exactly the 0/1 argument: reading `dp[s - x]` in descending order guarantees it reflects *previous* items only, so each number is used at most once. The dry run below shows the catastrophic result of ascending order.
27542

27643
## Approach 1 — Brute force

0 commit comments

Comments
 (0)