Skip to content

Commit 9d945bf

Browse files
committed
Add solutions and explanations for problems 27, 156, 157, 158, 159, 161, 163, 170
1 parent 66c5a52 commit 9d945bf

File tree

21 files changed

+24378
-18323
lines changed

21 files changed

+24378
-18323
lines changed

books/All.md

Lines changed: 1251 additions & 2 deletions
Large diffs are not rendered by default.

books/LeetCode_75.md

Lines changed: 1150 additions & 1047 deletions
Large diffs are not rendered by default.

books/LeetCode_Top_150.md

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

books/Top_100_Liked_Questions.md

Lines changed: 2221 additions & 1999 deletions
Large diffs are not rendered by default.

data/book-sets.json

Lines changed: 4153 additions & 167 deletions
Large diffs are not rendered by default.

data/leetcode-problems.json

Lines changed: 11330 additions & 11316 deletions
Large diffs are not rendered by default.

explanations/156/en.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We need to flip a binary tree upside down, where the original leftmost node becomes the new root, and the original root becomes the rightmost leaf. The transformation follows a specific pattern where left children become parents and right children become left children.
6+
7+
**1.1 Constraints & Complexity:**
8+
- Input size: The tree can have up to O(n) nodes where n is the number of nodes
9+
- **Time Complexity:** O(n) where n is the number of nodes - we visit each node exactly once
10+
- **Space Complexity:** O(h) where h is the height of the tree - the recursion stack depth equals the tree height
11+
- **Edge Case:** If the tree is empty or has no left child, return the root as-is
12+
13+
**1.2 High-level approach:**
14+
We use recursion to traverse to the leftmost node, which becomes the new root. During the backtracking phase, we reassign pointers to transform the tree structure according to the upside-down pattern.
15+
16+
![Binary tree upside down transformation](https://assets.leetcode.com/static_assets/others/binary-tree-upside-down.png)
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
- **Brute Force:** Create a new tree by copying nodes and reassigning pointers iteratively. This requires O(n) extra space.
20+
- **Optimized Strategy:** Use recursion to transform the tree in-place by reassigning pointers during backtracking. This uses O(h) space for recursion.
21+
- **Why it's better:** We modify the tree in-place without creating new nodes, making it more memory efficient.
22+
23+
**1.4 Decomposition:**
24+
1. Recursively traverse to the leftmost node, which will become the new root
25+
2. During backtracking, reassign the current node's left child's left pointer to the current node's right child
26+
3. Reassign the current node's left child's right pointer to the current node itself
27+
4. Set the current node's left and right pointers to None to break old connections
28+
5. Return the new root (leftmost node) from the deepest recursion
29+
30+
### Steps (The "How")
31+
32+
**2.1 Initialization & Example Setup:**
33+
Let's use the example tree with nodes [1,2,3,4,5]:
34+
- Root: 1, left: 2, right: 3
35+
- Node 2: left: 4, right: 5
36+
- After transformation, node 4 becomes the new root
37+
38+
**2.2 Start Processing:**
39+
We begin recursive traversal starting from the root, always going left first.
40+
41+
**2.3 Trace Walkthrough:**
42+
43+
| Node | Has Left? | Action | New Root | Transformations |
44+
|------|-----------|--------|----------|------------------|
45+
| 1 | Yes | Go left to 2 | - | - |
46+
| 2 | Yes | Go left to 4 | - | - |
47+
| 4 | No | Return 4 | 4 | - |
48+
| 2 | - | Backtrack | 4 | 4.left = 5, 4.right = 2, 2.left = None, 2.right = None |
49+
| 1 | - | Backtrack | 4 | 2.left = 3, 2.right = 1, 1.left = None, 1.right = None |
50+
51+
**2.4 Increment and Loop:**
52+
The recursion naturally handles the traversal. We continue until we reach a node with no left child.
53+
54+
**2.5 Return Result:**
55+
Return the new root (node 4), which is the leftmost node from the original tree. The tree is now upside down with all pointers correctly reassigned.
56+

explanations/157/en.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We need to read n characters from a file using the read4 API, which reads exactly 4 characters at a time. We must handle cases where n might not be a multiple of 4, and we need to stop reading when we've read n characters or reached the end of the file.
6+
7+
**1.1 Constraints & Complexity:**
8+
- Input size: `1 <= n <= 1000` characters to read
9+
- **Time Complexity:** O(n) - we read at most n characters, and each read4 call reads up to 4 characters
10+
- **Space Complexity:** O(1) - we use a fixed-size buffer of 4 characters
11+
- **Edge Case:** If n is 0, we return 0 without reading. If the file has fewer than n characters, we return the actual number read
12+
13+
**1.2 High-level approach:**
14+
We repeatedly call read4 to read 4 characters at a time, copying them to the destination buffer until we've read n characters or reached the end of the file. We handle partial reads when n is not a multiple of 4.
15+
16+
![Reading characters in chunks of 4](https://assets.leetcode.com/static_assets/others/read4-api.png)
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
- **Brute Force:** Call read4 once for every character needed, which would be inefficient and incorrect since read4 reads 4 characters at a time.
20+
- **Optimized Strategy:** Call read4 in a loop, reading 4 characters at a time and copying only what we need. This minimizes API calls.
21+
- **Why it's better:** We make the minimum number of read4 calls needed, reading in optimal chunks of 4.
22+
23+
**1.4 Decomposition:**
24+
1. Create a temporary buffer of size 4 to store characters from read4
25+
2. Initialize a counter to track total characters read
26+
3. Loop while we haven't read n characters yet
27+
4. Call read4 to get up to 4 characters
28+
5. Copy the minimum of (characters read, remaining needed) to the destination buffer
29+
6. Update the total count and continue until done
30+
31+
### Steps (The "How")
32+
33+
**2.1 Initialization & Example Setup:**
34+
Let's use the example: `n = 5`, file contains "abcde"
35+
- Initialize `buf4 = ['', '', '', '']` (temporary buffer)
36+
- Initialize `total = 0` (characters read so far)
37+
38+
**2.2 Start Reading:**
39+
We begin the loop to read characters until we've read n characters or reached EOF.
40+
41+
**2.3 Trace Walkthrough:**
42+
43+
| Iteration | total | n - total | read4() returns | buf4 | Copy to buf | total after |
44+
|-----------|-------|-----------|------------------|------|-------------|--------------|
45+
| 1 | 0 | 5 | 4 | ['a','b','c','d'] | buf[0:4] = ['a','b','c','d'] | 4 |
46+
| 2 | 4 | 1 | 1 | ['e','','',''] | buf[4:5] = ['e'] | 5 |
47+
| End | 5 | 0 | - | - | - | 5 |
48+
49+
**2.4 Increment and Loop:**
50+
After each read4 call, we update total by the number of characters copied. We continue until total >= n or read4 returns 0 (EOF).
51+
52+
**2.5 Return Result:**
53+
Return `total = 5`, which is the number of characters successfully read into the buffer. The buffer now contains ['a','b','c','d','e'].
54+

explanations/158/en.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We need to read n characters from a file using the read4 API, but the read function may be called multiple times. We must maintain state between calls to handle leftover characters from previous read4 calls that weren't fully consumed.
6+
7+
**1.1 Constraints & Complexity:**
8+
- Input size: `1 <= n <= 1000` characters per read call
9+
- **Time Complexity:** O(n) per read call - we process at most n characters per call
10+
- **Space Complexity:** O(1) amortized - we use a queue that stores at most 4 characters at a time
11+
- **Edge Case:** If read is called with n=0, return 0. If previous calls left characters in the queue, use them first
12+
13+
**1.2 High-level approach:**
14+
We maintain a queue to store leftover characters from previous read4 calls. When read is called, we first consume characters from the queue, then call read4 if needed. Any extra characters from read4 are stored in the queue for future calls.
15+
16+
![Maintaining buffer between multiple read calls](https://assets.leetcode.com/static_assets/others/read4-multiple-calls.png)
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
- **Brute Force:** Ignore previous state and start fresh each time, which would lose leftover characters and produce incorrect results.
20+
- **Optimized Strategy:** Use a queue to buffer leftover characters between calls, ensuring we don't lose any data and minimize read4 calls.
21+
- **Why it's better:** We correctly handle multiple calls by preserving state, and we avoid redundant read4 calls by reusing buffered characters.
22+
23+
**1.4 Decomposition:**
24+
1. Initialize a queue in the constructor to store leftover characters
25+
2. When read is called, first check if the queue has characters and use them
26+
3. If more characters are needed, call read4 to get new characters
27+
4. Store any extra characters from read4 (beyond what's needed) in the queue
28+
5. Return the total number of characters read
29+
30+
### Steps (The "How")
31+
32+
**2.1 Initialization & Example Setup:**
33+
Let's use the example: First call `read(buf, 1)`, then `read(buf, 2)`, file contains "abc"
34+
- Initialize `self.queue = []` in constructor
35+
- First call needs 1 character, second call needs 2 characters
36+
37+
**2.2 Start Reading:**
38+
We begin by checking the queue for leftover characters, then call read4 if needed.
39+
40+
**2.3 Trace Walkthrough:**
41+
42+
| Call | n | Queue Before | Action | read4() | Queue After | Return |
43+
|------|---|--------------|--------|---------|------------|--------|
44+
| 1 | 1 | [] | Call read4 | 4 chars: ['a','b','c',''] | ['b','c'] | 1 |
45+
| 2 | 2 | ['b','c'] | Use queue first | - | [] | 2 |
46+
47+
**Detailed first call:**
48+
- Queue is empty, call read4 → gets ['a','b','c',''] (3 valid chars)
49+
- Need 1 char, take 'a' from read4 result
50+
- Store remaining ['b','c'] in queue
51+
- Return 1
52+
53+
**Detailed second call:**
54+
- Queue has ['b','c'], take both
55+
- Need 2 chars, have 2 in queue, perfect
56+
- Return 2
57+
58+
**2.4 Increment and Loop:**
59+
We continue processing characters from the queue and calling read4 until we've read n characters or reached EOF.
60+
61+
**2.5 Return Result:**
62+
Return the total number of characters read (idx). The queue now contains any leftover characters for future read calls.
63+

explanations/159/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+
**Restate the problem:** We need to find the length of the longest substring that contains at most two distinct characters. This means we can have 0, 1, or 2 different characters in our substring, but not 3 or more.
6+
7+
**1.1 Constraints & Complexity:**
8+
- Input size: `1 <= s.length <= 10^5`
9+
- **Time Complexity:** O(n) where n is the length of the string - each character is visited at most twice (once by right pointer, once by left pointer)
10+
- **Space Complexity:** O(1) - we store at most 2 distinct characters in the hash map
11+
- **Edge Case:** If the string has fewer than 2 distinct characters, return the entire string length
12+
13+
**1.2 High-level approach:**
14+
We use a sliding window technique with two pointers. The right pointer expands the window to include new characters, and the left pointer shrinks it when we have more than two distinct characters. We track character frequencies using a hash map.
15+
16+
![Sliding window with two distinct characters](https://assets.leetcode.com/static_assets/others/sliding-window-two-distinct.png)
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
- **Brute Force:** Check all possible substrings O(n²) and count distinct characters for each, which is O(n³) total.
20+
- **Optimized Strategy:** Use sliding window to maintain a valid substring, expanding and contracting as needed. This is O(n) time.
21+
- **Why it's better:** We avoid checking redundant substrings and process each character at most twice, making it much more efficient.
22+
23+
**1.4 Decomposition:**
24+
1. Initialize two pointers (left and right) at the start of the string
25+
2. Use a hash map to count character frequencies in the current window
26+
3. Expand the window by moving the right pointer and updating counts
27+
4. When we have more than 2 distinct characters, shrink the window by moving the left pointer
28+
5. Track the maximum window size throughout the process
29+
30+
### Steps (The "How")
31+
32+
**2.1 Initialization & Example Setup:**
33+
Let's use the example: `s = "eceba"`
34+
- Initialize `left = 0`, `right = 0`, `res = 0`
35+
- Initialize `char_count = {}` (empty hash map)
36+
37+
**2.2 Start Checking:**
38+
We begin expanding the window by moving the right pointer.
39+
40+
**2.3 Trace Walkthrough:**
41+
42+
| Step | right | s[right] | char_count | Distinct | Action | left | res |
43+
|------|-------|-----------|------------|----------|--------|-----|-----|
44+
| 0 | 0 | 'e' | {'e':1} | 1 | Expand | 0 | 1 |
45+
| 1 | 1 | 'c' | {'e':1,'c':1} | 2 | Expand | 0 | 2 |
46+
| 2 | 2 | 'e' | {'e':2,'c':1} | 2 | Expand | 0 | 3 |
47+
| 3 | 3 | 'b' | {'e':2,'c':1,'b':1} | 3 | Shrink | 1 | 3 |
48+
| 3a | 3 | 'b' | {'e':1,'c':1,'b':1} | 3 | Shrink | 2 | 3 |
49+
| 3b | 3 | 'b' | {'e':1,'b':1} | 2 | Expand | 2 | 3 |
50+
| 4 | 4 | 'a' | {'e':1,'b':1,'a':1} | 3 | Shrink | 3 | 3 |
51+
| 4a | 4 | 'a' | {'b':1,'a':1} | 2 | Expand | 3 | 3 |
52+
53+
**2.4 Increment and Loop:**
54+
After processing each character, we increment the right pointer. If we need to shrink, we increment the left pointer until we have at most 2 distinct characters.
55+
56+
**2.5 Return Result:**
57+
Return `res = 3`, which is the length of the longest substring with at most two distinct characters. In this case, "ece" or "ceb" both have length 3.
58+

0 commit comments

Comments
 (0)