mirror of
https://github.com/Rainyy21/framework_note.git
synced 2026-10-11 01:10:28 -04:00
vault backup: 2026-06-05 18:15:42
This commit is contained in:
@@ -6,8 +6,75 @@ tags:
|
||||
- array
|
||||
- hash-table
|
||||
- union-find
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/longest-consecutive-sequence/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 128. Longest Consecutive Sequence
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Longest Consecutive Sequence](https://leetcode.com/problems/longest-consecutive-sequence/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Given an unsorted array of integers `nums`, return *the length of the longest consecutive elements sequence.*
|
||||
|
||||
You must write an algorithm that runs in `O(n)` time.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
> **Input:** `nums = [100,4,200,1,3,2]`
|
||||
> **Output:** `4`
|
||||
> **Explanation:** The longest consecutive elements sequence is `[1, 2, 3, 4]`. Therefore its length is 4.
|
||||
|
||||
### 📥 Example 2
|
||||
> **Input:** `nums = [0,3,7,2,5,8,4,6,0,1]`
|
||||
> **Output:** `9`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Hash Set — *Optimal*
|
||||
To achieve $O(n)$ time complexity, we use a hash set for $O(1)$ lookups. The core idea is to identify the start of each possible sequence. A number `n` is the start of a sequence if `n - 1` is not present in the set.
|
||||
|
||||
1. Insert all numbers from `nums` into a hash set.
|
||||
2. Iterate through each number `n` in the set:
|
||||
- Check if `n - 1` is in the set.
|
||||
- If `n - 1` is NOT in the set, `n` is the start of a sequence.
|
||||
- From `n`, keep checking for `n + 1`, `n + 2`, ... and increment the current sequence length.
|
||||
- Update the maximum length found so far.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $O(N)$ where $N$ is the number of elements. Although there is a nested while loop, each element is visited at most twice (once by the main loop and once by the while loop), resulting in linear time.
|
||||
- **Space Complexity:** $O(N)$ to store the elements in the hash set.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
class Solution:
|
||||
def longestConsecutive(self, nums: List[int]) -> int:
|
||||
num_set = set(nums)
|
||||
longest = 0
|
||||
|
||||
for n in num_set:
|
||||
# Check if n is the start of a sequence
|
||||
if (n - 1) not in num_set:
|
||||
length = 1
|
||||
while (n + length) in num_set:
|
||||
length += 1
|
||||
longest = max(length, longest)
|
||||
|
||||
return longest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Identifying Sequence Starts:** By checking for the absence of `n - 1`, we ensure that we only start counting from the beginning of a sequence, avoiding redundant work.
|
||||
- **Hash Set for Efficiency:** Trading space for time by using a Hash Set allows us to reduce what would be an $O(n^2)$ or $O(n \log n)$ problem into $O(n)$.
|
||||
|
||||
@@ -6,8 +6,95 @@ tags:
|
||||
- array
|
||||
- matrix
|
||||
- prefix-sum
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/grid-game/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 2017. Grid Game
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Grid Game](https://leetcode.com/problems/grid-game/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
You are given a **0-indexed** 2D array `grid` of size `2 x n`, where `grid[r][c]` represents the number of points at cell `(r, c)`. Two robots are playing a game on this grid.
|
||||
|
||||
Both robots start at `(0, 0)` and want to reach `(1, n-1)`. Each robot may only move to the **right** (`(r, c) -> (r, c + 1)`) or **down** (`(r, c) -> (r + 1, c)`).
|
||||
|
||||
1. **Robot 1** moves first. It collects all points on its path, and those cells are set to `0`.
|
||||
2. **Robot 2** moves second. It collects points from the remaining cells on its path.
|
||||
3. **Goal:** Robot 1 wants to **minimize** the points Robot 2 collects. Robot 2 wants to **maximize** its own points.
|
||||
|
||||
Return *the number of points collected by the second robot* assuming both play optimally.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
> **Input:** `grid = [[2,5,4],[1,5,1]]`
|
||||
> **Output:** `4`
|
||||
> **Explanation:**
|
||||
> Robot 1 takes path (0,0) -> (0,1) -> (1,1) -> (1,2). The cells (0,0), (0,1), (1,1), and (1,2) become 0.
|
||||
> Robot 2 can then take path (0,0) -> (0,1) -> (0,2) -> (1,2) to collect 4 points (only grid[0][2] remains).
|
||||
|
||||
### 📥 Example 2
|
||||
> **Input:** `grid = [[3,3,1],[8,5,2]]`
|
||||
> **Output:** `4`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Prefix Sums — *Optimal*
|
||||
Since there are only 2 rows, each robot must transition from the top row to the bottom row exactly once. If Robot 1 "drops" to the second row at column `i`, then:
|
||||
- The only points left in the **top row** are from column `i + 1` to `n - 1`.
|
||||
- The only points left in the **bottom row** are from column `0` to `i - 1`.
|
||||
|
||||
Robot 2 will optimally choose the maximum of these two remaining segments. Robot 1, knowing this, will choose the "drop" column `i` that minimizes Robot 2's maximum possible score.
|
||||
|
||||
1. Calculate the total sum of the top row.
|
||||
2. Iterate through each column `i` (representing Robot 1's drop point):
|
||||
- Keep track of the points remaining in the top row (sum of `grid[0][i+1:]`).
|
||||
- Keep track of the points remaining in the bottom row (sum of `grid[1][:i]`).
|
||||
- For each `i`, Robot 2's score is `max(top_remaining, bottom_remaining)`.
|
||||
- Minimize this score across all `i`.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $O(N)$ where $N$ is the number of columns. We traverse the grid twice (once for the total sum and once to find the optimal column).
|
||||
- **Space Complexity:** $O(1)$ if we calculate sums on the fly (ignoring input space).
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
class Solution:
|
||||
def gridGame(self, grid: List[List[int]]) -> int:
|
||||
n = len(grid[0])
|
||||
top_sum = sum(grid[0])
|
||||
bottom_sum = 0
|
||||
res = float("inf")
|
||||
|
||||
for i in range(n):
|
||||
# If Robot 1 drops at column i:
|
||||
# Robot 2 can either take the remaining top part...
|
||||
top_sum -= grid[0][i]
|
||||
|
||||
# ...or the remaining bottom part.
|
||||
# (bottom_sum here represents grid[1][0...i-1])
|
||||
|
||||
robot2_score = max(top_sum, bottom_sum)
|
||||
res = min(res, robot2_score)
|
||||
|
||||
# Prepare bottom_sum for the next iteration (i + 1)
|
||||
bottom_sum += grid[1][i]
|
||||
|
||||
return res
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Game Theory Simplification:** In problems where Robot 1 wants to minimize Robot 2's maximum, look for the bottleneck. Here, the bottleneck is Robot 1's single vertical move.
|
||||
- **Prefix/Suffix Sum Strategy:** When dealing with split ranges (e.g., everything before `i` and everything after `i`), prefix and suffix sums are the most efficient way to compute segment totals in $O(1)$.
|
||||
|
||||
@@ -5,8 +5,78 @@ difficulty: Medium
|
||||
tags:
|
||||
- array
|
||||
- prefix-sum
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/product-of-array-except-self/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 238. Product of Array Except Self
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Given an integer array `nums`, return an array `answer` such that `answer[i]` is equal to the product of all the elements of `nums` except `nums[i]`.
|
||||
|
||||
The product of any prefix or suffix of `nums` is guaranteed to fit in a **32-bit** integer.
|
||||
|
||||
**Constraints:**
|
||||
- You must write an algorithm that runs in `O(n)` time and without using the division operation.
|
||||
- Can you solve the problem in `O(1)` extra space complexity? (The output array does **not** count as extra space for space complexity analysis.)
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
> **Input:** `nums = [1,2,3,4]`
|
||||
> **Output:** `[24,12,8,6]`
|
||||
|
||||
### 📥 Example 2
|
||||
> **Input:** `nums = [-1,1,0,-3,3]`
|
||||
> **Output:** `[0,0,9,0,0]`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Prefix and Suffix Products — *Optimal*
|
||||
The product of all elements except `nums[i]` is the product of everything to the **left** of `i` multiplied by everything to the **right** of `i`.
|
||||
|
||||
1. Initialize the `res` array with 1s.
|
||||
2. **Left Pass (Prefix):** Iterate through the array from left to right. Maintain a `prefix` variable representing the product of all elements seen so far. For each `i`, set `res[i] = prefix`, then update `prefix *= nums[i]`.
|
||||
3. **Right Pass (Suffix):** Iterate through the array from right to left. Maintain a `postfix` variable representing the product of all elements to the right. For each `i`, multiply `res[i]` by `postfix`, then update `postfix *= nums[i]`.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $O(N)$ because we traverse the array twice.
|
||||
- **Space Complexity:** $O(1)$ extra space (excluding the output array).
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
class Solution:
|
||||
def productExceptSelf(self, nums: List[int]) -> List[int]:
|
||||
res = [1] * len(nums)
|
||||
|
||||
# Prefix pass
|
||||
prefix = 1
|
||||
for i in range(len(nums)):
|
||||
res[i] = prefix
|
||||
prefix *= nums[i]
|
||||
|
||||
# Postfix pass
|
||||
postfix = 1
|
||||
for i in range(len(nums) - 1, -1, -1):
|
||||
res[i] *= postfix
|
||||
postfix *= nums[i]
|
||||
|
||||
return res
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Avoiding Division:** When a problem forbids division but involves "all elements except current," think about splitting the problem into "everything before" and "everything after."
|
||||
- **Space Optimization:** Instead of creating separate prefix and suffix arrays, you can store one in the output array and apply the other on the fly using a single variable.
|
||||
|
||||
@@ -7,8 +7,88 @@ tags:
|
||||
- hash-table
|
||||
- string
|
||||
- design
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/encode-and-decode-strings/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 271. Encode and Decode Strings
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Encode and Decode Strings](https://leetcode.com/problems/encode-and-decode-strings/) (Note: This is a Premium problem, also available on [NeetCode](https://neetcode.io/problems/string-encode-and-decode))
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Design an algorithm to encode a list of strings to a single string. The encoded string is then sent over the network and is decoded back to the original list of strings.
|
||||
|
||||
Please implement `encode` and `decode` functions.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
> **Input:** `["lint","code","love","you"]`
|
||||
> **Output:** `["lint","code","love","you"]`
|
||||
> **Explanation:** One possible encoding is `"4#lint4#code4#love3#you"`.
|
||||
|
||||
### 📥 Example 2
|
||||
> **Input:** `["we", "say", ":", "yes"]`
|
||||
> **Output:** `["we", "say", ":", "yes"]`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Length-Prefixing — *Optimal*
|
||||
The challenge in encoding a list of strings is identifying where one string ends and the next begins, especially when the strings themselves contain special characters or delimiters.
|
||||
|
||||
The most robust solution is to use **Length-Prefixing**:
|
||||
1. **Encode:** For each string `s`, append its length followed by a delimiter (like `#`) and the string itself: `len(s) + "#" + s`.
|
||||
2. **Decode:**
|
||||
- Find the position of the first `#`.
|
||||
- The characters before `#` represent the length of the next string.
|
||||
- Read that many characters immediately following the `#`.
|
||||
- Repeat until the entire encoded string is processed.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $O(N)$ for both encoding and decoding, where $N$ is the total number of characters across all strings.
|
||||
- **Space Complexity:** $O(N)$ to store the encoded string or the resulting list of decoded strings.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
class Solution:
|
||||
def encode(self, strs: List[str]) -> str:
|
||||
res = ""
|
||||
for s in strs:
|
||||
res += str(len(s)) + "#" + s
|
||||
return res
|
||||
|
||||
def decode(self, s: str) -> List[str]:
|
||||
res, i = [], 0
|
||||
|
||||
while i < len(s):
|
||||
# Find the delimiter index
|
||||
j = i
|
||||
while s[j] != "#":
|
||||
j += 1
|
||||
|
||||
# The length of the next string is s[i:j]
|
||||
length = int(s[i:j])
|
||||
|
||||
# The actual string is from j + 1 to j + 1 + length
|
||||
res.append(s[j + 1 : j + 1 + length])
|
||||
|
||||
# Move i to the start of the next length-prefix
|
||||
i = j + 1 + length
|
||||
|
||||
return res
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Robust Delimiters:** Simple delimiters (like using a comma) fail if the input strings contain that character. Length-prefixing is a standard technique in network protocols to handle arbitrary data.
|
||||
- **State Management during Decoding:** Use pointers (or indices) to keep track of your position in the encoded string as you parse the lengths and segments.
|
||||
|
||||
@@ -10,8 +10,74 @@ tags:
|
||||
- heap-priority-queue
|
||||
- bucket-sort
|
||||
- quickselect
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/top-k-frequent-elements/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 347. Top K Frequent Elements
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Given an integer array `nums` and an integer `k`, return *the `k` most frequent elements*. You may return the answer in **any order**.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
> **Input:** `nums = [1,1,1,2,2,3]`, `k = 2`
|
||||
> **Output:** `[1,2]`
|
||||
|
||||
### 📥 Example 2
|
||||
> **Input:** `nums = [1]`, `k = 1`
|
||||
> **Output:** `[1]`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Bucket Sort — *Optimal*
|
||||
While we can use a Heap to solve this in $O(N \log K)$, we can achieve $O(N)$ using a variation of Bucket Sort.
|
||||
|
||||
1. **Count Frequencies:** Use a hash map to count the occurrence of each number.
|
||||
2. **Create Buckets:** Create an array of lists (`buckets`) where the index represents the frequency. Since a number can appear at most `len(nums)` times, the array size will be `len(nums) + 1`.
|
||||
3. **Fill Buckets:** For each number and its count from the frequency map, add the number to `buckets[count]`.
|
||||
4. **Collect Results:** Iterate through the `buckets` array from right to left (highest frequency to lowest) and collect the first `k` elements.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $O(N)$ where $N$ is the length of the array. Counting frequencies takes $O(N)$, filling buckets takes $O(N)$, and collecting the top $k$ elements takes $O(N)$.
|
||||
- **Space Complexity:** $O(N)$ to store the frequency map and the buckets.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
class Solution:
|
||||
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
|
||||
count = {}
|
||||
# index is frequency, value is list of numbers with that frequency
|
||||
freq = [[] for _ in range(len(nums) + 1)]
|
||||
|
||||
for n in nums:
|
||||
count[n] = 1 + count.get(n, 0)
|
||||
|
||||
for n, c in count.items():
|
||||
freq[c].append(n)
|
||||
|
||||
res = []
|
||||
for i in range(len(freq) - 1, 0, -1):
|
||||
for n in freq[i]:
|
||||
res.append(n)
|
||||
if len(res) == k:
|
||||
return res
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Trading Space for Time:** Bucket sort allows us to bypass the $O(N \log N)$ sorting limit by using the fact that the maximum possible frequency is bounded by the array size.
|
||||
- **Frequency as an Index:** A common pattern in counting problems is to use the frequency itself as an index to automatically group or sort elements.
|
||||
|
||||
@@ -6,8 +6,115 @@ tags:
|
||||
- array
|
||||
- hash-table
|
||||
- matrix
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/valid-sudoku/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 36. Valid Sudoku
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Valid Sudoku](https://leetcode.com/problems/valid-sudoku/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Determine if a **9 x 9 Sudoku board** is valid. Only the filled cells need to be validated according to the following rules:
|
||||
|
||||
1. Each row must contain the digits `1-9` without repetition.
|
||||
2. Each column must contain the digits `1-9` without repetition.
|
||||
3. Each of the nine `3 x 3` sub-boxes of the grid must contain the digits `1-9` without repetition.
|
||||
|
||||
**Note:**
|
||||
- A Sudoku board (partially filled) could be valid but is not necessarily solvable.
|
||||
- Only the filled cells need to be validated according to the mentioned rules.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
**Input:**
|
||||
```python
|
||||
board =
|
||||
[["5","3",".",".","7",".",".",".","."]
|
||||
,["6",".",".","1","9","5",".",".","."]
|
||||
,[".","9","8",".",".",".",".","6","."]
|
||||
,["8",".",".",".","6",".",".",".","3"]
|
||||
,["4",".",".","8",".","3",".",".","1"]
|
||||
,["7",".",".",".","2",".",".",".","6"]
|
||||
,[".","6",".",".",".",".","2","8","."]
|
||||
,[".",".",".","4","1","9",".",".","5"]
|
||||
,[".",".",".",".","8",".",".","7","9"]]
|
||||
```
|
||||
**Output:** `true`
|
||||
|
||||
### 📥 Example 2
|
||||
**Input:**
|
||||
```python
|
||||
board =
|
||||
[["8","3",".",".","7",".",".",".","."]
|
||||
,["6",".",".","1","9","5",".",".","."]
|
||||
,[".","9","8",".",".",".",".","6","."]
|
||||
,["8",".",".",".","6",".",".",".","3"]
|
||||
,["4",".",".","8",".","3",".",".","1"]
|
||||
,["7",".",".",".","2",".",".",".","6"]
|
||||
,[".","6",".",".",".",".","2","8","."]
|
||||
,[".",".",".","4","1","9",".",".","5"]
|
||||
,[".",".",".",".","8",".",".","7","9"]]
|
||||
```
|
||||
**Output:** `false`
|
||||
**Explanation:** Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8's in the top-left 3x3 sub-box, it is invalid.
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Hash Sets for Rows, Columns, and Boxes — *Optimal*
|
||||
We use three collections of sets to track the numbers we've seen:
|
||||
1. `rows`: 9 sets, one for each row.
|
||||
2. `cols`: 9 sets, one for each column.
|
||||
3. `boxes`: 9 sets, one for each 3x3 sub-grid.
|
||||
|
||||
We iterate through every cell `(r, c)` in the 9x9 board. If the cell is not empty (i.e., not `.`):
|
||||
- Calculate the box index: `box_idx = (r // 3) * 3 + (c // 3)`.
|
||||
- Check if the digit already exists in `rows[r]`, `cols[c]`, or `boxes[box_idx]`.
|
||||
- If it exists, the board is invalid.
|
||||
- If not, add the digit to all three sets and continue.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $\mathcal{O}(1)$ or $\mathcal{O}(N^2)$ where $N=9$. Since the board size is fixed at 9x9, we always perform 81 operations.
|
||||
- **Space Complexity:** $\mathcal{O}(1)$ or $\mathcal{O}(N^2)$ to store the sets for rows, columns, and boxes. In the worst case, we store 81 entries.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
class Solution:
|
||||
def isValidSudoku(self, board: List[List[str]]) -> bool:
|
||||
cols = collections.defaultdict(set)
|
||||
rows = collections.defaultdict(set)
|
||||
squares = collections.defaultdict(set) # key = (r // 3, c // 3)
|
||||
|
||||
for r in range(9):
|
||||
for c in range(9):
|
||||
if board[r][c] == ".":
|
||||
continue
|
||||
if (
|
||||
board[r][c] in rows[r]
|
||||
or board[r][c] in cols[c]
|
||||
or board[r][c] in squares[(r // 3, c // 3)]
|
||||
):
|
||||
return False
|
||||
cols[c].add(board[r][c])
|
||||
rows[r].add(board[r][c])
|
||||
squares[(r // 3, c // 3)].add(board[r][c])
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Coordinate Mapping:** Mapping a 2D coordinate `(r, c)` to a 1D sub-grid index or a tuple key `(r // 3, c // 3)` is a crucial technique for matrix problems.
|
||||
- **Trade-off:** Using hash sets provides $\mathcal{O}(1)$ lookup time, making the validation process very efficient.
|
||||
- **Constraints Matter:** Since the board size is fixed (9x9), "optimal" here refers to the single-pass nature and clean logic rather than asymptotic growth beyond the constant size.
|
||||
|
||||
@@ -8,8 +8,115 @@ tags:
|
||||
- math
|
||||
- randomized
|
||||
- design
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/insert-delete-getrandom-o1/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 380. Insert Delete GetRandom O(1)
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Insert Delete GetRandom O(1)](https://leetcode.com/problems/insert-delete-getrandom-o1/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Implement the `RandomizedSet` class:
|
||||
|
||||
- `RandomizedSet()` Initializes the `RandomizedSet` object.
|
||||
- `bool insert(int val)` Inserts an item `val` into the set if not present. Returns `true` if the item was not present, `false` otherwise.
|
||||
- `bool remove(int val)` Removes an item `val` from the set if present. Returns `true` if the item was present, `false` otherwise.
|
||||
- `int getRandom()` Returns a random element from the current set of elements (it's guaranteed that at least one element exists when this method is called). Each element must have the **same probability** of being returned.
|
||||
|
||||
You must implement the functions of the class such that each function works in **average** `O(1)` time complexity.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
**Input:**
|
||||
```python
|
||||
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
|
||||
[[], [1], [2], [2], [], [1], [2], []]
|
||||
```
|
||||
**Output:**
|
||||
```python
|
||||
[null, true, false, true, 2, true, false, 2]
|
||||
```
|
||||
**Explanation:**
|
||||
```python
|
||||
randomizedSet = RandomizedSet()
|
||||
randomizedSet.insert(1) # Inserts 1. Returns true. Set: [1]
|
||||
randomizedSet.remove(2) # Returns false as 2 is not in the set.
|
||||
randomizedSet.insert(2) # Inserts 2. Returns true. Set: [1, 2]
|
||||
randomizedSet.getRandom() # Returns either 1 or 2 randomly.
|
||||
randomizedSet.remove(1) # Removes 1. Returns true. Set: [2]
|
||||
randomizedSet.insert(2) # Returns false as 2 is already in the set.
|
||||
randomizedSet.getRandom() # Since 2 is the only number, returns 2.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: List + Hash Map — *Optimal*
|
||||
To achieve average $\mathcal{O}(1)$ for all operations, we need to combine two data structures:
|
||||
1. **A Dynamic Array (List):** Provides $\mathcal{O}(1)$ time to get a random element by index and $\mathcal{O}(1)$ time to append an element.
|
||||
2. **A Hash Map:** Maps values to their indices in the list. This provides $\mathcal{O}(1)$ time to check if a value exists and to find its location in the array.
|
||||
|
||||
#### The "Swap and Pop" Trick
|
||||
The main challenge is `remove(val)` in $\mathcal{O}(1)$. Removing an element from the middle of an array is $\mathcal{O}(N)$. To do it in $\mathcal{O}(1)$:
|
||||
1. Find the index of the element to remove using the Hash Map.
|
||||
2. Swap this element with the **last element** in the List.
|
||||
3. Update the Hash Map for the last element (which has now moved to the new index).
|
||||
4. Remove the last element from the List (pop) and the Hash Map (delete).
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** Average $\mathcal{O}(1)$ for all operations (`insert`, `remove`, `getRandom`).
|
||||
- **Space Complexity:** $\mathcal{O}(N)$ to store $N$ elements in both the list and the hash map.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
import random
|
||||
|
||||
class RandomizedSet:
|
||||
|
||||
def __init__(self):
|
||||
self.val_to_idx = {}
|
||||
self.nums = []
|
||||
|
||||
def insert(self, val: int) -> bool:
|
||||
if val in self.val_to_idx:
|
||||
return False
|
||||
self.val_to_idx[val] = len(self.nums)
|
||||
self.nums.append(val)
|
||||
return True
|
||||
|
||||
def remove(self, val: int) -> bool:
|
||||
if val not in self.val_to_idx:
|
||||
return False
|
||||
|
||||
# Move the last element to the place of the element to delete
|
||||
idx_to_remove = self.val_to_idx[val]
|
||||
last_val = self.nums[-1]
|
||||
|
||||
self.nums[idx_to_remove] = last_val
|
||||
self.val_to_idx[last_val] = idx_to_remove
|
||||
|
||||
# Remove the last element
|
||||
self.nums.pop()
|
||||
del self.val_to_idx[val]
|
||||
return True
|
||||
|
||||
def getRandom(self) -> int:
|
||||
return random.choice(self.nums)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Hybrid Data Structures:** Combining a List (for random access) and a Hash Map (for lookup) is a powerful pattern for designing efficient data structures.
|
||||
- **Efficient Deletion:** The "swap with last and pop" technique is a standard way to achieve $\mathcal{O}(1)$ deletion in an unordered list when indices are tracked.
|
||||
- **Randomization:** Use `random.choice()` or `random.randint()` for uniform selection from a list in $\mathcal{O}(1)$.
|
||||
|
||||
@@ -5,8 +5,76 @@ difficulty: Medium
|
||||
tags:
|
||||
- array
|
||||
- hash-table
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/find-all-duplicates-in-an-array/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 442. Find All Duplicates in an Array
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Find All Duplicates in an Array](https://leetcode.com/problems/find-all-duplicates-in-an-array/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Given an integer array `nums` of length `n` where all the integers of `nums` are in the range `[1, n]` and each integer appears **once** or **twice**, return an array of all the integers that appears **twice**.
|
||||
|
||||
You must write an algorithm that runs in $\mathcal{O}(n)$ time and uses only **constant** auxiliary space.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
**Input:** `nums = [4,3,2,7,8,2,3,1]`
|
||||
**Output:** `[2,3]`
|
||||
|
||||
### 📥 Example 2
|
||||
**Input:** `nums = [1,1,2]`
|
||||
**Output:** `[1]`
|
||||
|
||||
### 📥 Example 3
|
||||
**Input:** `nums = [1]`
|
||||
**Output:** `[]`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Negative Marking (In-place) — *Optimal*
|
||||
Since the input numbers are in the range `[1, n]` and the array length is `n`, we can use the array itself to track seen numbers. We use the **sign** of the number at a specific index to indicate whether the number corresponding to that index has been encountered.
|
||||
|
||||
1. Iterate through the array. For each number `x = nums[i]`:
|
||||
2. Calculate the target index: `index = abs(x) - 1`.
|
||||
3. Check the value at `nums[index]`:
|
||||
- If `nums[index] < 0`, it means the number `abs(x)` has been seen before. Add `abs(x)` to the result list.
|
||||
- If `nums[index] > 0`, negate the value at `nums[index]` to mark the number `abs(x)` as "seen".
|
||||
|
||||
This technique allows us to store the "seen" information without using extra space.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $\mathcal{O}(N)$ as we traverse the array exactly once.
|
||||
- **Space Complexity:** $\mathcal{O}(1)$ auxiliary space (excluding the space for the result list).
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
class Solution:
|
||||
def findDuplicates(self, nums: List[int]) -> List[int]:
|
||||
res = []
|
||||
for x in nums:
|
||||
idx = abs(x) - 1
|
||||
if nums[idx] < 0:
|
||||
res.append(abs(x))
|
||||
else:
|
||||
nums[idx] = -nums[idx]
|
||||
return res
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **In-place Hashing:** When numbers are in the range `[1, n]`, the array itself can be used as a hash table by mapping values to indices.
|
||||
- **Sign as a Boolean:** Using the sign of a number as a flag is a common trick to save space in array-based problems.
|
||||
- **Problem Constraints:** The constraint that each number appears at most twice is key; if a number appeared three times, the negation logic would flip it back to positive, breaking the "seen" flag.
|
||||
|
||||
@@ -7,8 +7,85 @@ tags:
|
||||
- hash-table
|
||||
- string
|
||||
- sorting
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/group-anagrams/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 49. Group Anagrams
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Group Anagrams](https://leetcode.com/problems/group-anagrams/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Given an array of strings `strs`, group the **anagrams** together. You can return the answer in **any order**.
|
||||
|
||||
An **Anagram** is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
**Input:** `strs = ["eat","tea","tan","ate","nat","bat"]`
|
||||
**Output:** `[["bat"],["nat","tan"],["ate","eat","tea"]]`
|
||||
|
||||
### 📥 Example 2
|
||||
**Input:** `strs = [""]`
|
||||
**Output:** `[[""]]`
|
||||
|
||||
### 📥 Example 3
|
||||
**Input:** `strs = ["a"]`
|
||||
**Output:** `[["a"]]`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Categorize by Sorted String
|
||||
Two strings are anagrams if and only if their sorted versions are equal. We can use a hash map where the key is the sorted string and the value is a list of anagrams.
|
||||
- **Time Complexity:** $\mathcal{O}(N \cdot K \log K)$ where $N$ is the number of strings and $K$ is the maximum length of a string.
|
||||
- **Space Complexity:** $\mathcal{O}(N \cdot K)$
|
||||
|
||||
### Approach 2: Categorize by Character Count — *Optimal*
|
||||
Instead of sorting, we can represent each string as a frequency array of size 26 (for 'a' to 'z'). Two strings are anagrams if their frequency arrays are identical.
|
||||
1. Initialize a hash map `res`.
|
||||
2. For each string in `strs`:
|
||||
- Create a count array of size 26, initialized to 0.
|
||||
- For each character in the string, increment its corresponding index in the count array.
|
||||
- Convert the count array to a tuple (to make it hashable) and use it as a key in `res`.
|
||||
- Append the original string to the list at that key.
|
||||
3. Return `res.values()`.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $\mathcal{O}(N \cdot K)$ where $N$ is the number of strings and $K$ is the maximum length of a string. We iterate through each string and each character once.
|
||||
- **Space Complexity:** $\mathcal{O}(N \cdot K)$ to store the result in the hash map.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
from collections import defaultdict
|
||||
|
||||
class Solution:
|
||||
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
|
||||
res = defaultdict(list) # mapping charCount to list of Anagrams
|
||||
|
||||
for s in strs:
|
||||
count = [0] * 26 # a ... z
|
||||
for c in s:
|
||||
count[ord(c) - ord("a")] += 1
|
||||
|
||||
# Convert list to tuple so it can be used as a key in dictionary
|
||||
res[tuple(count)].append(s)
|
||||
|
||||
return list(res.values())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Hashing Frequency Arrays:** Using a frequency array as a hash map key is a common technique for string problems where order doesn't matter (like anagrams).
|
||||
- **Tuple conversion:** In Python, lists are mutable and cannot be used as dictionary keys. Converting them to tuples (which are immutable) solves this.
|
||||
- **Asymptotic Optimization:** While sorting is often "fast enough," the character count approach is asymptotically superior for long strings.
|
||||
|
||||
@@ -6,8 +6,87 @@ tags:
|
||||
- array
|
||||
- hash-table
|
||||
- prefix-sum
|
||||
status: unsolve
|
||||
date_solved: 2026-05-26
|
||||
status: Solved
|
||||
date_solved: 2026-06-05
|
||||
leetcode_url: https://leetcode.com/problems/subarray-sum-equals-k/
|
||||
review_needed: false
|
||||
---
|
||||
|
||||
# 560. Subarray Sum Equals K
|
||||
|
||||
> [!info] **Problem Link**: [LeetCode - Subarray Sum Equals K](https://leetcode.com/problems/subarray-sum-equals-k/)
|
||||
|
||||
## 📝 Problem Description
|
||||
|
||||
Given an array of integers `nums` and an integer `k`, return *the total number of subarrays whose sum equals to `k`*.
|
||||
|
||||
A **subarray** is a contiguous **non-empty** sequence of elements within an array.
|
||||
|
||||
---
|
||||
|
||||
### 📥 Example 1
|
||||
**Input:** `nums = [1,1,1]`, `k = 2`
|
||||
**Output:** `2`
|
||||
|
||||
### 📥 Example 2
|
||||
**Input:** `nums = [1,2,3]`, `k = 3`
|
||||
**Output:** `2`
|
||||
|
||||
---
|
||||
|
||||
## 💡 Approaches & Explanations
|
||||
|
||||
### Approach 1: Prefix Sum + Hash Map — *Optimal*
|
||||
The key idea is that the sum of any subarray `nums[j..i]` can be calculated using prefix sums:
|
||||
$$\text{sum}(j..i) = \text{prefix\_sum}[i] - \text{prefix\_sum}[j-1]$$
|
||||
We want to find pairs $(i, j)$ such that:
|
||||
$$\text{prefix\_sum}[i] - \text{prefix\_sum}[j-1] = k$$
|
||||
Rearranging the formula:
|
||||
$$\text{prefix\_sum}[j-1] = \text{prefix\_sum}[i] - k$$
|
||||
|
||||
As we iterate through the array, we calculate the current prefix sum and check how many times the value `(current_prefix_sum - k)` has appeared before.
|
||||
|
||||
1. Initialize a hash map `prefix_sums` with `{0: 1}` (this represents a prefix sum of 0 occurring once, which helps catch subarrays starting from index 0).
|
||||
2. Maintain a running `cur_sum`.
|
||||
3. For each number `num` in `nums`:
|
||||
- Add `num` to `cur_sum`.
|
||||
- If `cur_sum - k` is in `prefix_sums`, add its frequency to the result `count`.
|
||||
- Increment the frequency of `cur_sum` in `prefix_sums`.
|
||||
4. Return `count`.
|
||||
|
||||
#### 📊 Complexity Analysis
|
||||
- **Time Complexity:** $\mathcal{O}(N)$ where $N$ is the length of the array. We traverse the array once.
|
||||
- **Space Complexity:** $\mathcal{O}(N)$ to store the frequency of prefix sums in the hash map.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code Implementations
|
||||
|
||||
### Python3
|
||||
```python
|
||||
from collections import defaultdict
|
||||
|
||||
class Solution:
|
||||
def subarraySum(self, nums: List[int], k: int) -> int:
|
||||
count = 0
|
||||
cur_sum = 0
|
||||
prefix_sums = {0: 1} # prefix_sum -> frequency
|
||||
|
||||
for num in nums:
|
||||
cur_sum += num
|
||||
diff = cur_sum - k
|
||||
|
||||
if diff in prefix_sums:
|
||||
count += prefix_sums[diff]
|
||||
|
||||
prefix_sums[cur_sum] = prefix_sums.get(cur_sum, 0) + 1
|
||||
|
||||
return count
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Key Takeaways & Lessons
|
||||
- **Difference of Prefix Sums:** This is the go-to technique for "subarray sum" problems. By storing previous sums in a hash map, we can turn a $\mathcal{O}(N^2)$ problem into $\mathcal{O}(N)$.
|
||||
- **The Zero Case:** Initializing the hash map with `{0: 1}` is a common edge-case handler for prefix sum problems. It accounts for the case where the prefix sum itself equals `k`.
|
||||
- **Negative Numbers:** This approach works even if the array contains negative numbers, unlike the sliding window technique which requires non-negative elements for monotonic sum growth.
|
||||
|
||||
Reference in New Issue
Block a user