Skip to content

Commit b7026d0

Browse files
Merge pull request #99 from romankurnovskii/problems-160-337-347-378-412-438-448-460-543-560-581-617-695-973-981-1029-1046-1167-1249-1288-1679
Add solutions and explanations for problems 160, 337, 347, 378, 412, 438, 448, 460, 543, 560, 581, 617, 695, 973, 981, 1029, 1046, 1167, 1249, 1288, 1679
2 parents 2734f86 + 77e132b commit b7026d0

File tree

36 files changed

+2007
-149
lines changed

36 files changed

+2007
-149
lines changed

books/All.md

Lines changed: 373 additions & 0 deletions
Large diffs are not rendered by default.

books/Top_100_Liked_Questions.md

Lines changed: 373 additions & 0 deletions
Large diffs are not rendered by default.

data/book-sets.json

Lines changed: 150 additions & 148 deletions
Large diffs are not rendered by default.

explanations/1046/en.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
- **Constraints:** Array length is between 1 and 30, and each stone weight is between 1 and 1000.
7+
- **Time Complexity:** O(n log n) - We perform at most n operations, each involving heap operations that take O(log n) time.
8+
- **Space Complexity:** O(n) - The heap stores at most n elements.
9+
- **Edge Case:** If there's only one stone, return its weight.
10+
11+
**1.2 High-level approach:**
12+
The goal is to simulate smashing the two heaviest stones together until at most one stone remains. We use a max-heap (implemented with negated values in a min-heap) to efficiently get the two heaviest stones at each step.
13+
14+
**1.3 Brute force vs. optimized strategy:**
15+
- **Brute Force:** Sort the array, take the two largest, smash them, insert the result, and repeat. Sorting each time takes O(n log n), leading to O(n^2 log n) overall.
16+
- **Optimized Strategy (Max Heap):** Use a max-heap to always get the two heaviest stones in O(log n) time. This reduces overall time to O(n log n).
17+
- **Emphasize the optimization:** The heap data structure allows us to efficiently maintain and retrieve the maximum elements without full sorting.
18+
19+
**1.4 Decomposition:**
20+
1. Convert the array into a max-heap (using negated values for Python's min-heap).
21+
2. While there are at least 2 stones:
22+
- Pop the two heaviest stones.
23+
- If they're different, push their difference back into the heap.
24+
3. Return the last stone's weight (or 0 if no stones remain).
25+
26+
### Steps (The "How")
27+
28+
**2.1 Initialization & Example Setup:**
29+
Let's use an example: `stones = [2,7,4,1,8,1]`
30+
31+
Initialize:
32+
- `heap = [-2,-7,-4,-1,-8,-1]` (negated for max-heap)
33+
- `heapq.heapify(heap)`
34+
35+
**2.2 Start Processing:**
36+
We repeatedly pop two stones and smash them.
37+
38+
**2.3 Trace Walkthrough:**
39+
40+
| Step | Pop 1 | Pop 2 | Difference | Heap After | Result |
41+
|------|-------|-------|------------|------------|--------|
42+
| 1 | 8 | 7 | 1 | [-4,-2,-1,-1,1] | Continue |
43+
| 2 | 4 | 2 | 2 | [-2,-1,-1,1] | Continue |
44+
| 3 | 2 | 1 | 1 | [-1,-1,1] | Continue |
45+
| 4 | 1 | 1 | 0 | [-1] | Continue |
46+
| 5 | 1 | - | - | [] | Done |
47+
48+
**2.4 Return Result:**
49+
After all operations, one stone of weight 1 remains, so return 1.
50+

explanations/1288/en.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
- **Constraints:** Array length is between 1 and 1000, and interval values are between 0 and 10^5.
7+
- **Time Complexity:** O(n log n) - We sort the intervals, which dominates the time complexity.
8+
- **Space Complexity:** O(1) - We only use a few variables, excluding the input array.
9+
- **Edge Case:** If there's only one interval, return 1.
10+
11+
**1.2 High-level approach:**
12+
The goal is to count intervals that are not covered by any other interval. An interval [a,b) is covered by [c,d) if c <= a and b <= d. We sort intervals by start (ascending) and end (descending), then track the maximum end seen so far.
13+
14+
**1.3 Brute force vs. optimized strategy:**
15+
- **Brute Force:** For each interval, check if it's covered by any other interval. This takes O(n^2) time.
16+
- **Optimized Strategy (Sorting):** Sort intervals by start, then by end (descending). If an interval's end is greater than the maximum end seen so far, it's not covered. This takes O(n log n) time.
17+
- **Emphasize the optimization:** Sorting allows us to process intervals in one pass, reducing time complexity from O(n^2) to O(n log n).
18+
19+
**1.4 Decomposition:**
20+
1. Sort intervals by start (ascending), then by end (descending) for same starts.
21+
2. Initialize max_end = -1 and result counter = 0.
22+
3. For each interval, if its end > max_end, it's not covered, so increment counter and update max_end.
23+
4. Return the counter.
24+
25+
### Steps (The "How")
26+
27+
**2.1 Initialization & Example Setup:**
28+
Let's use an example: `intervals = [[1,4],[3,6],[2,8]]`
29+
30+
After sorting: `[[1,4],[2,8],[3,6]]` (sorted by start, then end descending)
31+
32+
Initialize:
33+
- `max_end = -1`
34+
- `res = 0`
35+
36+
**2.2 Start Checking:**
37+
We iterate through sorted intervals.
38+
39+
**2.3 Trace Walkthrough:**
40+
41+
| Interval | Start | End | End > max_end? | Action | max_end | res |
42+
|----------|-------|-----|----------------|--------|---------|-----|
43+
| [1,4] | 1 | 4 | Yes (4 > -1) | Count, update | 4 | 1 |
44+
| [2,8] | 2 | 8 | Yes (8 > 4) | Count, update | 8 | 2 |
45+
| [3,6] | 3 | 6 | No (6 <= 8) | Skip | 8 | 2 |
46+
47+
**2.4 Return Result:**
48+
The result is 2, meaning [1,4] and [2,8] are not covered, while [3,6] is covered by [2,8].
49+

explanations/160/en.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
- **Constraints:** We have two linked lists with `m` and `n` nodes respectively, where `1 <= m, n <= 3 * 10^4`. Each node value is between `1` and `10^5`.
7+
- **Time Complexity:** O(m + n) - In the worst case, we traverse both lists once before finding the intersection or determining there is none.
8+
- **Space Complexity:** O(1) - We only use two pointer variables, no additional data structures.
9+
- **Edge Case:** When the two lists have no intersection, both pointers will eventually become `None` and the loop will terminate, returning `None`.
10+
11+
**1.2 High-level approach:**
12+
The goal is to find the node where two linked lists intersect, or return `None` if they don't intersect. The key insight is that if we traverse both lists simultaneously, switching to the other list when we reach the end, both pointers will eventually meet at the intersection point (if it exists) after traversing the same total distance.
13+
14+
![Two linked lists intersecting](https://assets.leetcode.com/uploads/2021/03/05/160_statement.png)
15+
16+
**1.3 Brute force vs. optimized strategy:**
17+
- **Brute Force:** For each node in list A, check if it exists in list B by traversing list B. This would take O(m * n) time complexity.
18+
- **Optimized Strategy (Two Pointers):** Use two pointers that traverse both lists, switching lists when reaching the end. This ensures both pointers cover the same total distance and will meet at the intersection. Time complexity is O(m + n) with O(1) space.
19+
20+
**1.4 Decomposition:**
21+
1. Initialize two pointers, one for each list.
22+
2. Traverse both lists simultaneously, moving each pointer forward.
23+
3. When a pointer reaches the end of its list, switch it to the head of the other list.
24+
4. Continue until both pointers point to the same node (intersection found) or both are `None` (no intersection).
25+
26+
### Steps (The "How")
27+
28+
**2.1 Initialization & Example Setup:**
29+
Let's use an example: `headA = [4,1,8,4,5]` and `headB = [5,6,1,8,4,5]`, where the lists intersect at node with value 8.
30+
31+
Initialize:
32+
- `p1 = headA` (points to node 4)
33+
- `p2 = headB` (points to node 5)
34+
35+
**2.2 Start Checking:**
36+
We enter a loop that continues while `p1 != p2`.
37+
38+
**2.3 Trace Walkthrough:**
39+
40+
| Step | p1 position | p2 position | p1 value | p2 value | Action |
41+
|------|-------------|--------------|----------|----------|--------|
42+
| 1 | headA[0] | headB[0] | 4 | 5 | Both advance |
43+
| 2 | headA[1] | headB[1] | 1 | 6 | Both advance |
44+
| 3 | headA[2] | headB[2] | 8 | 1 | Both advance |
45+
| 4 | headA[3] | headB[3] | 4 | 8 | Both advance |
46+
| 5 | headA[4] | headB[4] | 5 | 4 | Both advance |
47+
| 6 | None | headB[5] | - | 5 | p1 switches to headB |
48+
| 7 | headB[0] | None | 5 | - | p2 switches to headA |
49+
| 8 | headB[1] | headA[0] | 6 | 4 | Both advance |
50+
| 9 | headB[2] | headA[1] | 1 | 1 | Both advance |
51+
| 10 | headB[3] | headA[2] | 8 | 8 | **Match!** Intersection found |
52+
53+
**2.4 Increment and Loop:**
54+
At each iteration:
55+
- If `p1` is not `None`, move it to `p1.next`; otherwise, set it to `headB`.
56+
- If `p2` is not `None`, move it to `p2.next`; otherwise, set it to `headA`.
57+
58+
**2.5 Return Result:**
59+
When `p1 == p2`, the loop exits. This happens either:
60+
- When both point to the intersection node (return that node)
61+
- When both are `None` (no intersection, return `None`)
62+
63+
In our example, both pointers meet at the node with value 8, which is the intersection point.
64+

explanations/337/en.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
- **Constraints:** The tree has at most 10^4 nodes, and each node value is between 0 and 10^4.
7+
- **Time Complexity:** O(n) - We visit each node exactly once, where n is the number of nodes.
8+
- **Space Complexity:** O(h) - The recursion stack depth is at most the height h of the tree. In the worst case (skewed tree), h = n, giving O(n) space.
9+
- **Edge Case:** If the tree is empty (root is None), return 0.
10+
11+
**1.2 High-level approach:**
12+
The goal is to find the maximum amount of money we can rob from a binary tree without robbing two directly connected nodes. We use dynamic programming with a post-order traversal, where for each node we calculate two values: the maximum if we rob this node, and the maximum if we don't rob this node.
13+
14+
![House Robber III tree](https://assets.leetcode.com/uploads/2021/03/10/rob1-tree.jpg)
15+
16+
**1.3 Brute force vs. optimized strategy:**
17+
- **Brute Force:** Try all possible combinations of robbing/not robbing each node, checking constraints. This would be exponential time O(2^n).
18+
- **Optimized Strategy (DP with DFS):** For each node, calculate the maximum profit for two cases: robbing this node (can't rob children) and not robbing this node (can rob children). This takes O(n) time.
19+
- **Emphasize the optimization:** By storing both possibilities at each node, we avoid recalculating subproblems and reduce time complexity from exponential to linear.
20+
21+
**1.4 Decomposition:**
22+
1. Perform a post-order traversal of the tree.
23+
2. For each node, return a tuple: (rob_this, dont_rob_this).
24+
3. If we rob this node, we can't rob its children, so we take children's "don't rob" values.
25+
4. If we don't rob this node, we can choose the maximum from each child's two options.
26+
5. Return the maximum of the root's two options.
27+
28+
### Steps (The "How")
29+
30+
**2.1 Initialization & Example Setup:**
31+
Let's use an example: `root = [3,2,3,null,3,null,1]`
32+
33+
Initialize:
34+
- Start DFS from root node with value 3.
35+
36+
**2.2 Start Processing:**
37+
We traverse the tree using post-order DFS (process children before parent).
38+
39+
**2.3 Trace Walkthrough:**
40+
41+
| Node | Left Child Result | Right Child Result | Rob This | Don't Rob This | Max |
42+
|------|-------------------|-------------------|----------|----------------|-----|
43+
| 1 (leaf) | (0,0) | (0,0) | 1 + 0 + 0 = 1 | 0 + 0 = 0 | 1 |
44+
| 3 (leaf) | (0,0) | (0,0) | 3 + 0 + 0 = 3 | 0 + 0 = 0 | 3 |
45+
| 2 | (0,0) | (3,0) | 2 + 0 + 0 = 2 | 0 + 3 = 3 | 3 |
46+
| 3 (root) | (2,3) | (1,0) | 3 + 3 + 0 = 6 | 3 + 1 = 4 | 7 |
47+
48+
**2.4 Return Result:**
49+
The final result is max(rob_root, dont_rob_root) = max(6, 4) = 7.
50+

explanations/347/en.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
- **Constraints:** Array length n is between 1 and 10^5, and values are between -10^4 and 10^4.
7+
- **Time Complexity:** O(n) - We iterate through the array once to count frequencies, then iterate through buckets once.
8+
- **Space Complexity:** O(n) - We use a dictionary to count frequencies and buckets array of size n+1.
9+
- **Edge Case:** If k equals the number of unique elements, return all unique elements.
10+
11+
**1.2 High-level approach:**
12+
The goal is to find the k most frequent elements in an array. We use bucket sort: count frequencies, then place numbers into buckets indexed by frequency, and collect the top k from the highest frequency buckets.
13+
14+
**1.3 Brute force vs. optimized strategy:**
15+
- **Brute Force:** Count frequencies, then sort by frequency and take top k. This takes O(n log n) time.
16+
- **Optimized Strategy (Bucket Sort):** Use bucket sort where each bucket index represents a frequency. This takes O(n) time since frequencies are bounded by n.
17+
- **Emphasize the optimization:** Bucket sort eliminates the need for sorting, reducing time complexity from O(n log n) to O(n).
18+
19+
**1.4 Decomposition:**
20+
1. Count the frequency of each number using a dictionary.
21+
2. Create buckets where index represents frequency.
22+
3. Place each number into its frequency bucket.
23+
4. Iterate through buckets from highest to lowest frequency, collecting numbers until we have k elements.
24+
25+
### Steps (The "How")
26+
27+
**2.1 Initialization & Example Setup:**
28+
Let's use an example: `nums = [1,1,1,2,2,3]`, `k = 2`
29+
30+
Initialize:
31+
- `count = {}` (empty dictionary)
32+
- `buckets = [[] for _ in range(7)]` (7 buckets for frequencies 0-6)
33+
- `res = []` (empty result list)
34+
35+
**2.2 Start Counting:**
36+
Count frequencies: `count = {1: 3, 2: 2, 3: 1}`
37+
38+
**2.3 Trace Walkthrough:**
39+
40+
| Number | Frequency | Bucket Index | Bucket Contents |
41+
|--------|-----------|--------------|-----------------|
42+
| 1 | 3 | 3 | [1] |
43+
| 2 | 2 | 2 | [2] |
44+
| 3 | 1 | 1 | [3] |
45+
46+
After placing in buckets:
47+
- `buckets[3] = [1]`
48+
- `buckets[2] = [2]`
49+
- `buckets[1] = [3]`
50+
51+
**2.4 Collect Top K:**
52+
Iterate from bucket 3 down to bucket 1:
53+
- Bucket 3: Add 1 to res → `res = [1]`
54+
- Bucket 2: Add 2 to res → `res = [1, 2]` (k=2, done)
55+
56+
**2.5 Return Result:**
57+
Return `[1, 2]` as the top 2 most frequent elements.
58+

explanations/378/en.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
- **Constraints:** Matrix is n x n where 1 <= n <= 300, and values are between -10^9 and 10^9. k is between 1 and n^2.
7+
- **Time Complexity:** O(k log n) - We perform k heap operations, each taking O(log n) time.
8+
- **Space Complexity:** O(n) - The heap contains at most n elements (one from each row).
9+
- **Edge Case:** If k = 1, return the smallest element (matrix[0][0]).
10+
11+
**1.2 High-level approach:**
12+
The goal is to find the kth smallest element in a sorted matrix. We use a min-heap to efficiently track the smallest elements across all rows, popping k-1 times to get the kth smallest.
13+
14+
**1.3 Brute force vs. optimized strategy:**
15+
- **Brute Force:** Flatten the matrix, sort it, and return the kth element. This takes O(n^2 log n^2) time and O(n^2) space.
16+
- **Optimized Strategy (Min Heap):** Use a min-heap to merge sorted rows, similar to merge k sorted lists. This takes O(k log n) time and O(n) space.
17+
- **Emphasize the optimization:** The heap approach avoids storing all elements and only processes the k smallest elements we need.
18+
19+
**1.4 Decomposition:**
20+
1. Initialize a min-heap with the first element of each row.
21+
2. Pop the smallest element k-1 times.
22+
3. Each time we pop, add the next element from the same row to the heap.
23+
4. The kth smallest element is at the top of the heap.
24+
25+
### Steps (The "How")
26+
27+
**2.1 Initialization & Example Setup:**
28+
Let's use an example: `matrix = [[1,5,9],[10,11,13],[12,13,15]]`, `k = 8`
29+
30+
Initialize:
31+
- `heap = [(1,0,0), (10,1,0), (12,2,0)]` (value, row, col)
32+
33+
**2.2 Start Processing:**
34+
We need to pop 7 times (k-1 = 7) to get the 8th smallest.
35+
36+
**2.3 Trace Walkthrough:**
37+
38+
| Step | Pop | Heap After Pop | Add Next |
39+
|------|-----|----------------|----------|
40+
| 1 | 1 | [(10,1,0), (12,2,0)] | (5,0,1) |
41+
| 2 | 5 | [(10,1,0), (12,2,0)] | (9,0,2) |
42+
| 3 | 9 | [(10,1,0), (12,2,0)] | - |
43+
| 4 | 10 | [(11,1,1), (12,2,0)] | (11,1,1) |
44+
| 5 | 11 | [(12,2,0), (13,1,2)] | (13,1,2) |
45+
| 6 | 12 | [(13,1,2), (13,2,1)] | (13,2,1) |
46+
| 7 | 13 | [(13,2,1), (15,2,2)] | (15,2,2) |
47+
48+
**2.4 Return Result:**
49+
After 7 pops, the heap top is `(13,2,1)`, so the 8th smallest is 13.
50+

explanations/412/en.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**1.1 Constraints & Complexity:**
6+
- **Constraints:** We need to process integers from 1 to n, where `1 <= n <= 10^4`.
7+
- **Time Complexity:** O(n) - We iterate through each number from 1 to n exactly once.
8+
- **Space Complexity:** O(n) - We create a result list of size n to store the output strings.
9+
- **Edge Case:** When n = 1, we simply return `["1"]`.
10+
11+
**1.2 High-level approach:**
12+
The goal is to generate a list of strings where numbers divisible by 3 are replaced with "Fizz", numbers divisible by 5 are replaced with "Buzz", and numbers divisible by both 3 and 5 are replaced with "FizzBuzz".
13+
14+
![FizzBuzz sequence visualization](https://assets.leetcode.com/static_assets/others/fizzbuzz.png)
15+
16+
**1.3 Brute force vs. optimized strategy:**
17+
- **Brute Force:** Check divisibility by 15 first, then by 3, then by 5, then use the number. This is the same as our optimized approach - there's no more efficient way to solve this problem.
18+
- **Optimized Strategy:** Use conditional checks in the correct order (check for 15 first, then 3, then 5) to determine the appropriate string for each number.
19+
20+
**1.4 Decomposition:**
21+
1. Iterate through numbers from 1 to n.
22+
2. For each number, check if it's divisible by both 3 and 5 (i.e., divisible by 15).
23+
3. If not, check if it's divisible by 3.
24+
4. If not, check if it's divisible by 5.
25+
5. Otherwise, use the number itself as a string.
26+
6. Add the result to the output list.
27+
28+
### Steps (The "How")
29+
30+
**2.1 Initialization & Example Setup:**
31+
Let's use an example: `n = 15`.
32+
33+
Initialize:
34+
- `res = []` (empty list to store results)
35+
36+
**2.2 Start Checking:**
37+
We iterate through numbers from 1 to 15.
38+
39+
**2.3 Trace Walkthrough:**
40+
41+
| i | Divisible by 3? | Divisible by 5? | Divisible by 15? | Result |
42+
|---|-----------------|-----------------|------------------|--------|
43+
| 1 | No | No | No | "1" |
44+
| 2 | No | No | No | "2" |
45+
| 3 | Yes | No | No | "Fizz" |
46+
| 4 | No | No | No | "4" |
47+
| 5 | No | Yes | No | "Buzz" |
48+
| 6 | Yes | No | No | "Fizz" |
49+
| 7 | No | No | No | "7" |
50+
| 8 | No | No | No | "8" |
51+
| 9 | Yes | No | No | "Fizz" |
52+
| 10 | No | Yes | No | "Buzz" |
53+
| 11 | No | No | No | "11" |
54+
| 12 | Yes | No | No | "Fizz" |
55+
| 13 | No | No | No | "13" |
56+
| 14 | No | No | No | "14" |
57+
| 15 | Yes | Yes | Yes | "FizzBuzz" |
58+
59+
**2.4 Increment and Loop:**
60+
For each iteration, we check the conditions in order:
61+
1. If `i % 3 == 0 and i % 5 == 0`: append "FizzBuzz"
62+
2. Else if `i % 3 == 0`: append "Fizz"
63+
3. Else if `i % 5 == 0`: append "Buzz"
64+
4. Else: append `str(i)`
65+
66+
**2.5 Return Result:**
67+
After processing all numbers from 1 to n, return the `res` list containing all the strings.
68+

0 commit comments

Comments
 (0)