mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 13:12:44 +00:00
feat: refactored docs structure
This commit is contained in:
1 parent
d388b3000e
commit
9de25fecff
212 files changed
+641
-4169
No files matched your search
+74
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: '153. Find Minimum in Rotated Sorted Array'
|
||||
description: 'Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:'
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "nIVW4P8b1VA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/62"
|
||||
sidebar:
|
||||
label: 'Find Minimum in Rotated Sorted Array'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [3,4,5,1,2]`
|
||||
- Output: `1`
|
||||
- Explanation: The original array was `[1,2,3,4,5]` rotated `3` times.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [4,5,6,7,0,1,2]`
|
||||
- Output: `0`
|
||||
- Explanation: The original array was `[0,1,2,4,5,6,7]` and it was rotated `4` times.
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [11,13,15,17]`
|
||||
- Output: `11`
|
||||
- Explanation: The original array was `[11,13,15,17]` and it was rotated `4` times.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == nums.length`
|
||||
- `1 <= n <= 5000`
|
||||
- `-5000 <= nums[i] <= 5000`
|
||||
- All the integers of `nums` are unique.
|
||||
- `nums` is sorted and rotated between 1 and `n` times.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["findMin(nums)"]) --> I["l = 0, r = n-1, lowest_index = -1"]
|
||||
I --> W{"l <= r?"}
|
||||
W -- no --> E(["return nums[lowest_index]"])
|
||||
W -- yes --> M["m = (l + r) // 2"]
|
||||
M --> C{"nums[m] <= nums[-1]?"}
|
||||
C -- yes --> A["in the right sorted run: record lowest_index = m, r = m - 1"]
|
||||
A --> W
|
||||
C -- no --> B["still in the left sorted run: l = m + 1"]
|
||||
B --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def findMin(self, nums: List[int]) -> int:
|
||||
l, r = 0, len(nums) - 1
|
||||
lowest_index = -1
|
||||
|
||||
while l <= r:
|
||||
m = (l + r) // 2
|
||||
if nums[m] <= nums[-1]:
|
||||
lowest_index = m
|
||||
r = m - 1
|
||||
else:
|
||||
l = m + 1
|
||||
|
||||
return nums[lowest_index]
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: '162. Find Peak Element'
|
||||
description: A peak element is an element that is strictly greater than its neighbors
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/64"
|
||||
sidebar:
|
||||
label: 'Find Peak Element'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must write an algorithm that runs in O(log n) time.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,2,3,1]`
|
||||
- Output: `2`
|
||||
- Explanation: `3` is a peak element and your function should return the index number `2`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [1,2,1,3,5,6,4]`
|
||||
- Output: `5`
|
||||
- Explanation: Your function can return either index number `1` where the peak element is `2`, or index number `5` where the peak element is `6`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 1000`
|
||||
- `-2^31 <= nums[i] <= 2^31 - 1`
|
||||
- `nums[i] != nums[i + 1] for all valid i.`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def findPeakElement(self, nums: list[int]) -> int:
|
||||
left, right = 0, len(nums) - 1
|
||||
while left < right:
|
||||
mid = (left + right) // 2
|
||||
if nums[mid] < nums[mid + 1]:
|
||||
left = mid + 1
|
||||
else:
|
||||
right = mid
|
||||
|
||||
return left
|
||||
```
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: '33. Search in Rotated Sorted Array'
|
||||
description: There is an integer array nums sorted in ascending order (with distinct values)
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "U8XENwh8Oy8"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/60"
|
||||
sidebar:
|
||||
label: 'Search in Rotated Sorted Array'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must write an algorithm with O(log n) runtime complexity.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [4,5,6,7,0,1,2], target = 0`
|
||||
- Output: `4`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [4,5,6,7,0,1,2], target = 3`
|
||||
- Output: `-1`
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [1], target = 0`
|
||||
- Output: `-1`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 5000`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
- All values of `nums` are unique.
|
||||
- `nums` is an ascending array that is possibly rotated.
|
||||
- `-10^4 <= target <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["search(nums, target)"]) --> I["l = 0, r = n - 1"]
|
||||
I --> W{"l <= r?"}
|
||||
W -- no --> E(["return -1"])
|
||||
W -- yes --> M["mid = floor((l + r) / 2)"]
|
||||
M --> F{"nums[mid] == target?"}
|
||||
F -- yes --> R(["return mid"])
|
||||
F -- no --> H{"nums[l] <= nums[mid]? — left half sorted"}
|
||||
H -- yes --> A{"target outside [nums[l], nums[mid]]?"}
|
||||
A -- yes --> A1["l = mid + 1 — search the right half"]
|
||||
A -- no --> A2["r = mid - 1"]
|
||||
H -- no --> B{"target outside [nums[mid], nums[r]]?"}
|
||||
B -- yes --> B1["r = mid - 1 — search the left half"]
|
||||
B -- no --> B2["l = mid + 1"]
|
||||
A1 --> W
|
||||
A2 --> W
|
||||
B1 --> W
|
||||
B2 --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @param {number} target
|
||||
* @return {number}
|
||||
*/
|
||||
var search = function(nums, target) {
|
||||
let l = 0, r = nums.length - 1;
|
||||
|
||||
while (l <= r){
|
||||
let mid = Math.floor((l + r)/2);
|
||||
if (target === nums[mid]) {
|
||||
return mid
|
||||
}
|
||||
|
||||
// Left sorted portion
|
||||
if (nums[l] <= nums[mid]){
|
||||
if (target > nums[mid] || target < nums[l]){
|
||||
l = mid + 1;
|
||||
}else{
|
||||
r = mid - 1;
|
||||
}
|
||||
}
|
||||
// Right sorted portion
|
||||
else{
|
||||
if(target < nums[mid] || target > nums[r]){
|
||||
r = mid - 1;
|
||||
}else{
|
||||
l = mid + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
```
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: '34. Find First and Last Position of Element in Sorted Array'
|
||||
description: Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/65"
|
||||
sidebar:
|
||||
label: 'Find First and Last Position of Element in Sorted Array'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must write an algorithm with O(log n) runtime complexity.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [5,7,7,8,8,10], target = 8`
|
||||
- Output: `[3,4]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [5,7,7,8,8,10], target = 6`
|
||||
- Output: `[-1,-1]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [], target = 0`
|
||||
- Output: `[-1,-1]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `0 <= nums.length <= 10^5`
|
||||
- `-10^9 <= nums[i] <= 10^9`
|
||||
- `nums` is a non-decreasing array.
|
||||
- `-10^9 <= target <= 10^9`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def searchRange(self, nums: list[int], target: int) -> list[int]:
|
||||
def find(first):
|
||||
result = -1
|
||||
left, right = 0, len(nums) - 1
|
||||
while left <= right:
|
||||
mid = (left + right) // 2
|
||||
if nums[mid] == target:
|
||||
result = mid
|
||||
if first:
|
||||
right = mid - 1
|
||||
else:
|
||||
left = mid + 1
|
||||
elif nums[mid] < target:
|
||||
left = mid + 1
|
||||
else:
|
||||
right = mid - 1
|
||||
return result
|
||||
|
||||
return [find(True), find(False)]
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: '35. Search Insert Position'
|
||||
description: Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "K-RYzDZkzCI"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/63"
|
||||
sidebar:
|
||||
label: 'Search Insert Position'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must write an algorithm with O(log n) runtime complexity.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,3,5,6], target = 5`
|
||||
- Output: `2`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [1,3,5,6], target = 2`
|
||||
- Output: `1`
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [1,3,5,6], target = 7`
|
||||
- Output: `4`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^4`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
- `nums` contains distinct values sorted in ascending order.
|
||||
- `-10^4 <= target <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["searchInsert(nums, target)"]) --> I["l = 0, r = n - 1"]
|
||||
I --> W{"l <= r?"}
|
||||
W -- yes --> M["m = (l + r) // 2"]
|
||||
M --> Q{"nums[m] vs target"}
|
||||
Q -- equal --> R(["return m"])
|
||||
Q -- "nums[m] < target" --> L["l = m + 1"]
|
||||
L --> W
|
||||
Q -- "nums[m] > target" --> H["r = m - 1"]
|
||||
H --> W
|
||||
W -- no --> E(["return (l + r) // 2 + 1 — the insert slot"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def searchInsert(self, nums: List[int], target: int) -> int:
|
||||
l, r = 0, len(nums) - 1
|
||||
|
||||
while l <= r:
|
||||
m = (l + r) // 2
|
||||
if nums[m] == target:
|
||||
return m
|
||||
elif nums[m] < target:
|
||||
l = m + 1
|
||||
elif nums[m] > target:
|
||||
r = m - 1
|
||||
|
||||
return (l + r) // 2 + 1
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: '704. Binary Search'
|
||||
description: Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise, return -1
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "s4DPM8ct1pI"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/58"
|
||||
sidebar:
|
||||
label: 'Binary Search'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must write an algorithm with O(log n) runtime complexity.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [-1,0,3,5,9,12], target = 9`
|
||||
- Output: `4`
|
||||
- Explanation: `9` exists in `nums` and its index is `4`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [-1,0,3,5,9,12], target = 2`
|
||||
- Output: `-1`
|
||||
- Explanation: `2` does not exist in `nums` so return `-1`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^4`
|
||||
- `-10^4 < nums[i], target < 10^4`
|
||||
- All the integers in `nums` are unique.
|
||||
- `nums` is sorted in ascending order.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["search(nums, target)"]) --> I["left = 0, right = n - 1"]
|
||||
I --> W{"left <= right?"}
|
||||
W -- no --> E(["return -1"])
|
||||
W -- yes --> M["mid = left + floor((right - left) / 2)"]
|
||||
M --> Q{"nums[mid] vs target"}
|
||||
Q -- equal --> R(["return mid"])
|
||||
Q -- "nums[mid] < target" --> L["left = mid + 1"]
|
||||
L --> W
|
||||
Q -- "nums[mid] > target" --> H["right = mid - 1"]
|
||||
H --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @param {number} target
|
||||
* @return {number}
|
||||
*/
|
||||
var search = function(nums, target) {
|
||||
let left = 0;
|
||||
let right = nums.length - 1;
|
||||
|
||||
while(left <= right) {
|
||||
const mid = left + Math.floor((right - left) / 2);
|
||||
if (nums[mid] === target) {
|
||||
return mid;
|
||||
} else if(nums[mid] < target){
|
||||
left = mid + 1;
|
||||
} else if (nums[mid] > target) {
|
||||
right = mid - 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: '74. Search a 2D Matrix'
|
||||
description: 'You are given an m x n integer matrix matrix with the following two properties:'
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
video: "Ber2pi2C0j0"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/61"
|
||||
sidebar:
|
||||
label: 'Search a 2D Matrix'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must write a solution in O(log(m * n)) time complexity.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3`
|
||||
- Output: `true`
|
||||
|
||||
### Example 2:
|
||||
- Input: `matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13`
|
||||
- Output: `false`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == matrix.length`
|
||||
- `n == matrix[i].length`
|
||||
- `1 <= m, n <= 100`
|
||||
- `-10^4 <= matrix[i][j], target <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["searchMatrix(matrix, target)"]) --> I["top = 0, bot = ROWS - 1"]
|
||||
I --> W1{"top <= bot?"}
|
||||
W1 -- yes --> R1["row = (top + bot) // 2"]
|
||||
R1 --> C1{"target vs that row's range"}
|
||||
C1 -- "above matrix[row][-1]" --> U["top = row + 1"]
|
||||
C1 -- "below matrix[row][0]" --> V["bot = row - 1"]
|
||||
C1 -- inside --> B(["break — row found"])
|
||||
U --> W1
|
||||
V --> W1
|
||||
W1 -- no --> F(["return False — no row can hold it"])
|
||||
B --> P["row = (top + bot) // 2; l = 0, r = COLS - 1"]
|
||||
P --> W2{"l <= r?"}
|
||||
W2 -- no --> G(["return False"])
|
||||
W2 -- yes --> M["m = (l + r) // 2"]
|
||||
M --> C2{"target vs matrix[row][m]"}
|
||||
C2 -- greater --> X["l = m + 1"]
|
||||
C2 -- smaller --> Y["r = m - 1"]
|
||||
C2 -- equal --> T(["return True"])
|
||||
X --> W2
|
||||
Y --> W2
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
|
||||
ROWS, COLS = len(matrix), len(matrix[0])
|
||||
|
||||
top, bot = 0, ROWS - 1
|
||||
while top <= bot:
|
||||
row = (top + bot) // 2
|
||||
if target > matrix[row][-1]:
|
||||
top = row + 1
|
||||
elif target < matrix[row][0]:
|
||||
bot = row - 1
|
||||
else:
|
||||
break
|
||||
|
||||
if not (top <= bot):
|
||||
return False
|
||||
|
||||
row = (top + bot) // 2
|
||||
l, r = 0, COLS - 1
|
||||
while l <= r:
|
||||
m = (l + r) // 2
|
||||
if target > matrix[row][m]:
|
||||
l = m + 1
|
||||
elif target < matrix[row][m]:
|
||||
r = m - 1
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: '875. Koko Eating Bananas'
|
||||
description: Koko loves to eat bananas. There are n piles of bananas, the i^th pile has piles[i] bananas. The guards have gone and will come back in h hours
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "U2SozAs9RzA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/59"
|
||||
sidebar:
|
||||
label: 'Koko Eating Bananas'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `piles = [3,6,7,11], h = 8`
|
||||
- Output: `4`
|
||||
|
||||
### Example 2:
|
||||
- Input: `piles = [30,11,23,4,20], h = 5`
|
||||
- Output: `30`
|
||||
|
||||
### Example 3:
|
||||
- Input: `piles = [30,11,23,4,20], h = 6`
|
||||
- Output: `23`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= piles.length <= 10^4`
|
||||
- `piles.length <= h <= 10^9`
|
||||
- `1 <= piles[i] <= 10^9`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["minEatingSpeed(piles, h)"]) --> I["l = 1, r = max(piles)"]
|
||||
I --> W{"l < r?"}
|
||||
W -- no --> E(["return l — the smallest workable speed"])
|
||||
W -- yes --> M["mid = floor((l + r) / 2)"]
|
||||
M --> K["kWorks(mid): hours = sum of ceil(p / mid)"]
|
||||
K --> Q{"hours <= h?"}
|
||||
Q -- yes --> A["r = mid — keep mid, try slower"]
|
||||
Q -- no --> B["l = mid + 1 — too slow, speed up"]
|
||||
A --> W
|
||||
B --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} piles
|
||||
* @param {number} h
|
||||
* @return {number}
|
||||
*/
|
||||
var minEatingSpeed = function(piles, h) {
|
||||
// Function to determine if K works for the H
|
||||
function kWorks(k){
|
||||
let hours = 0;
|
||||
for (let p of piles){
|
||||
hours += Math.ceil(p/k)
|
||||
}
|
||||
return hours <= h;
|
||||
}
|
||||
|
||||
// Setup Binary Search
|
||||
let l = 1;
|
||||
let r = Math.max(...piles);
|
||||
|
||||
// Find the K value which works for H and consumes all bananas
|
||||
while (l < r){
|
||||
const mid = Math.floor((l + r) / 2);
|
||||
if (kWorks(mid)){
|
||||
r = mid;
|
||||
}else{
|
||||
l = mid + 1;
|
||||
}
|
||||
}
|
||||
return l;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Binary Search
|
||||
description: 'Half of a sorted range is removed at each step until the target is found.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
Binary search is an efficient array search algorithm. It works by narrowing down the search range by half each time. If you have looked up a word in a physical dictionary, you've already used binary search in real life. Let's look at a simple example:
|
||||
|
||||
*Given a sorted array of integers and an integer called target, find the element that equals the target and return its index. If the element is not found, return -1.*
|
||||
|
||||
The key observation here is that the array is sorted. We pick a random element in the array and compare it to the target.
|
||||
|
||||
- If we happen to pick the element that equals the target (how lucky!), then bingo. We don't need to do any more work; we return its index.
|
||||
- If the element is smaller than the target, then we know the target cannot be found in the section to the left of the current element, since everything to the left is even smaller. So, we discard the current element and everything on the left from the search range.
|
||||
- If the element is larger than the target, then we know the target cannot be found in the section to the right of the current element, since everything to the right is even larger. So, we discard the current element and everything on the right from the search range.
|
||||
|
||||
We repeat this process until we find the target. Instead of picking a random element, we always pick the **middle** element in the current search range. This way, we can discard half of the options and shrink the search range by half each time. This gives us `O(log(N))` runtime.
|
||||
|
||||
This idea can be implemented both iteratively and recursively. However, the major difference is that the iterative version of binary search uses `O(1)` memory while the recursive version uses `O(log(N))` memory.
|
||||
|
||||
## Implementation
|
||||
|
||||
The search range is represented by the `left` and `right` indices that start from both ends of the array and move towards each other as we search. When moving the index, we discard elements and shrink the search range.
|
||||
|
||||
Time Complexity: `O(log(n))`
|
||||
|
||||
|
||||
<Carousel>
|
||||
<CarouselImage src='/binary_search.001.png' />
|
||||
<CarouselImage src='/binary_search.002.png' />
|
||||
<CarouselImage src='/binary_search.003.png' />
|
||||
<CarouselImage src='/binary_search.004.png' />
|
||||
<CarouselImage src='/binary_search.005.png' />
|
||||
<CarouselImage src='/binary_search.006.png' />
|
||||
<CarouselImage src='/binary_search.007.png' />
|
||||
<CarouselImage src='/binary_search.008.png' />
|
||||
</Carousel>
|
||||
|
||||
```python
|
||||
def binary_search(arr: list[int], target: int) -> int:
|
||||
left, right = 0, len(arr) - 1
|
||||
# <= because left and right could point to the same element, < would miss it
|
||||
while left <= right:
|
||||
# double slash for integer division in python 3,
|
||||
# we don't have to worry about integer `left + right` overflow
|
||||
# since python integers can be arbitrarily large
|
||||
mid = (left + right) // 2
|
||||
# found target, return its index
|
||||
if arr[mid] == target:
|
||||
return mid
|
||||
# middle less than target, discard left half by making left search boundary `mid + 1`
|
||||
if arr[mid] < target:
|
||||
left = mid + 1
|
||||
# middle greater than target, discard right half by making right search boundary `mid - 1`
|
||||
else:
|
||||
right = mid - 1
|
||||
return -1 # if we get here we didn't hit above return so we didn't find target
|
||||
```
|
||||
|
||||
### Calculating `mid`
|
||||
|
||||
Note that when calculating `mid`, if the number of elements is even, there are two elements in the middle. We usually follow the convention of picking the first one, equivalent to doing integer division `(left + right) / 2`.
|
||||
|
||||
In most programming languages, we calculate `mid` with `left + floor((right-left) / 2)` to avoid potential integer overflow. However, in Python, we do not need to worry about integer overflow with `left + right` because Python3 integers can be arbitrarily large.
|
||||
|
||||
## Deduction
|
||||
|
||||
It's essential to understand and deduce the algorithm yourself instead of memorizing it. In an actual interview, interviewers may ask you additional questions to test your understanding, so simply memorizing the algorithm may not be enough to convince the interviewer.
|
||||
|
||||
Key elements in writing a correct binary search:
|
||||
|
||||
### 1. When to terminate the loop
|
||||
|
||||
Make sure the `while` loop includes an equality comparison; otherwise, we might skip the loop and miss the potential match for the edge case of a one-element array.
|
||||
|
||||
### 2. Whether/how to update `left` and `right` boundary in the `if` conditions
|
||||
|
||||
Consider which side to discard. If `arr[mid]` is smaller than the `target`, we should discard everything on the left by making `left = mid + 1`.
|
||||
|
||||
### 3. Should I discard the current element?
|
||||
|
||||
For vanilla binary search, we can discard it since it cannot be the final answer if it is not equal to the target. There might be situations where you would want to think twice before discarding the current element. We'll discuss this in the next module.
|
||||
|
||||
## When to use binary search
|
||||
|
||||
Interestingly, binary search works beyond sorted arrays. **You can use binary search whenever you make a binary decision to shrink the search range.**
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: '128. Longest Consecutive Sequence'
|
||||
description: Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Union-Find", slug: "union-find" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/34"
|
||||
sidebar:
|
||||
label: 'Longest Consecutive Sequence'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
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`
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [1,0,1,2]`
|
||||
- Output: `3`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `0 <= nums.length <= 10^5`
|
||||
- `-10^9 <= nums[i] <= 10^9`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def longestConsecutive(self, nums: list[int]) -> int:
|
||||
seen = set(nums)
|
||||
best = 0
|
||||
|
||||
for n in seen:
|
||||
if n - 1 in seen:
|
||||
continue
|
||||
|
||||
length = 1
|
||||
while length + n in seen:
|
||||
length += 1
|
||||
|
||||
best = max(length, best)
|
||||
|
||||
return best
|
||||
```
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: '1832. Check if the Sentence Is Pangram'
|
||||
description: A pangram is a sentence where every letter of the English alphabet appears at least once
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
sidebar:
|
||||
label: 'Check if the Sentence Is Pangram'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `sentence = "thequickbrownfoxjumpsoverthelazydog"`
|
||||
- Output: `true`
|
||||
- Explanation: `sentence` contains at least one of every letter of the English alphabet.
|
||||
|
||||
### Example 2:
|
||||
- Input: `sentence = "leetcode"`
|
||||
- Output: `false`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= sentence.length <= 1000`
|
||||
- `sentence` consists of lowercase English letters.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["checkIfPangram(sentence)"]) --> I["freq = {}"]
|
||||
I --> L{"more char c?"}
|
||||
L -- yes --> C["freq[c] = (freq[c] or 0) + 1"]
|
||||
C --> L
|
||||
L -- no --> Q{"distinct keys in freq == 26?"}
|
||||
Q -- yes --> R(["return true"])
|
||||
Q -- no --> F(["return false"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string} sentence
|
||||
* @return {boolean}
|
||||
*/
|
||||
var checkIfPangram = function(sentence) {
|
||||
const freq = {};
|
||||
for (let c of sentence) freq[c] = (freq[c] || 0) + 1;
|
||||
|
||||
if (Object.keys(freq).length === 26){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: '1. Two Sum'
|
||||
description: You are given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
video: "KLlXCFG5TnA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/29"
|
||||
sidebar:
|
||||
label: 'Two Sum'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Can you come up with an algorithm that is less than O(n^2) time complexity?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [2,7,11,15], target = 9`
|
||||
- Output: `[0,1]`
|
||||
- Explanation: Because `nums[0] + nums[1]` == `9`, we return [0, 1].
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [3,2,4], target = 6`
|
||||
- Output: `[1,2]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [3,3], target = 6`
|
||||
- Output: `[0,1]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `2 <= nums.length <= 10^4`
|
||||
- `-10^9 <= nums[i] <= 10^9`
|
||||
- `-10^9 <= target <= 10^9`
|
||||
- Only one valid answer exists.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["twoSum(nums, target)"]) --> I["seen = {} — value to index"]
|
||||
I --> L{"more (i, num) in nums?"}
|
||||
L -- no --> E(["return []"])
|
||||
L -- yes --> C["complement = target - num"]
|
||||
C --> H{"complement in seen?"}
|
||||
H -- yes --> R(["return [seen[complement], i]"])
|
||||
H -- no --> W["seen[num] = i"]
|
||||
W --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def twoSum(self, nums: List[int], target: int) -> List[int]:
|
||||
seen = {}
|
||||
for i, num in enumerate(nums):
|
||||
complement = target - num
|
||||
if complement in seen:
|
||||
return [seen[complement], i]
|
||||
seen[num] = i
|
||||
return []
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: '205. Isomorphic Strings'
|
||||
description: Given two strings s and t, determine if they are isomorphic
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/33"
|
||||
sidebar:
|
||||
label: 'Isomorphic Strings'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "egg", t = "add"`
|
||||
- Output: `true`
|
||||
- Explanation: The strings `s` and `t` can be made identical by: • Mapping 'e' to 'a'. • Mapping 'g' to 'd'.
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "f11", t = "b23"`
|
||||
- Output: `false`
|
||||
- Explanation: The strings `s` and `t` can not be made identical as `'1`' needs to be mapped to both `'2`' and `'3`'.
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = "paper", t = "title"`
|
||||
- Output: `true`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 5 * 10^4`
|
||||
- `t.length == s.length`
|
||||
- `s` and `t` consist of any valid ascii character.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def isIsomorphic(self, s: str, t: str) -> bool:
|
||||
N = len(t)
|
||||
seen = {}
|
||||
|
||||
for i in range(N):
|
||||
c1, c2 = s[i], t[i]
|
||||
if c1 in seen and seen[c1] != c2:
|
||||
return False
|
||||
elif c1 in seen and seen[c1] == c2:
|
||||
continue
|
||||
else:
|
||||
seen[c1] = c2
|
||||
|
||||
return len(set(s)) == len(set(t))
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: '217. Contains Duplicate'
|
||||
description: Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "3OamzN90kPg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/27"
|
||||
sidebar:
|
||||
label: 'Contains Duplicate'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,2,3,1]`
|
||||
- Output: `true`
|
||||
- Explanation: The element `1` occurs at the indices `0` and `3`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [1,2,3,4]`
|
||||
- Output: `false`
|
||||
- Explanation: All elements are distinct.
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [1,1,1,3,3,4,3,2,4,2]`
|
||||
- Output: `true`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `-10^9 <= nums[i] <= 10^9`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["containsDuplicate(nums)"]) --> I["freq = {}"]
|
||||
I --> L{"more n in nums?"}
|
||||
L -- no --> E(["return false"])
|
||||
L -- yes --> C["freq[n] = (freq[n] or 0) + 1"]
|
||||
C --> Q{"freq[n] >= 2?"}
|
||||
Q -- yes --> R(["return true"])
|
||||
Q -- no --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def containsDuplicate(self, nums: List[int]) -> bool:
|
||||
return len(set(nums)) != len(nums)
|
||||
```
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: '242. Valid Anagram'
|
||||
description: Given two strings s and t, return true if t is an anagram of s, and false otherwise
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "9UtInBqnCgA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/28"
|
||||
sidebar:
|
||||
label: 'Valid Anagram'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
What if the inputs contain Unicode characters? How would you adapt your solution to such a case?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "anagram", t = "nagaram"`
|
||||
- Output: `true`
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "rat", t = "car"`
|
||||
- Output: `false`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length, t.length <= 5 * 10^4`
|
||||
- `s` and `t` consist of lowercase English letters.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["isAnagram(s, t)"]) --> I["freq: count every char of s"]
|
||||
I --> L{"more char c in t?"}
|
||||
L -- no --> E(["return true"])
|
||||
L -- yes --> Q{"freq[c] missing or 0?"}
|
||||
Q -- yes --> R(["return false — t has a char s cannot cover"])
|
||||
Q -- no --> D["freq[c] -= 1"]
|
||||
D --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {string} t
|
||||
* @return {boolean}
|
||||
*/
|
||||
var isAnagram = function(s, t) {
|
||||
let freq = {};
|
||||
for (let c of s) freq[c] = (freq[c] || 0) + 1;
|
||||
|
||||
for (let c of t) {
|
||||
if(!freq[c] || freq[c] === 0){
|
||||
return false;
|
||||
}
|
||||
freq[c] = freq[c] - 1;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: '290. Word Pattern'
|
||||
description: Given a pattern and a string s, find if s follows the same pattern
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/35"
|
||||
sidebar:
|
||||
label: 'Word Pattern'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `pattern = "abba", s = "dog cat cat dog"`
|
||||
- Output: `true`
|
||||
- Explanation: The bijection can be established as: • 'a' maps to "dog". • 'b' maps to "cat".
|
||||
|
||||
### Example 2:
|
||||
- Input: `pattern = "abba", s = "dog cat cat fish"`
|
||||
- Output: `false`
|
||||
|
||||
### Example 3:
|
||||
- Input: `pattern = "aaaa", s = "dog cat cat dog"`
|
||||
- Output: `false`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= pattern.length <= 300`
|
||||
- `pattern` contains only lower-case English letters.
|
||||
- `1 <= s.length <= 3000`
|
||||
- `s` contains only lowercase English letters and spaces ' '.
|
||||
- `s` does not contain any leading or trailing spaces.
|
||||
- All the words in `s` are separated by a single space.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def wordPattern(self, pattern: str, s: str) -> bool:
|
||||
if len(pattern) != len(s.split()):
|
||||
return False
|
||||
|
||||
words = s.split()
|
||||
map = {}
|
||||
|
||||
for i in range(len(pattern)):
|
||||
c = pattern[i]
|
||||
w = words[i]
|
||||
|
||||
if c in map and map[c] != w:
|
||||
return False
|
||||
else:
|
||||
map[c] = w
|
||||
|
||||
return len(set(pattern)) == len(set(words))
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: '347. Top K Frequent Elements'
|
||||
description: Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Divide and Conquer", slug: "divide-and-conquer" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
- { name: "Heap (Priority Queue)", slug: "heap-priority-queue" }
|
||||
- { name: "Bucket Sort", slug: "bucket-sort" }
|
||||
- { name: "Counting", slug: "counting" }
|
||||
- { name: "Quickselect", slug: "quickselect" }
|
||||
video: "YPTqKIgVk-k"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/31"
|
||||
sidebar:
|
||||
label: 'Top K Frequent Elements'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,1,1,2,2,3], k = 2`
|
||||
- Output: `[1,2]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [1], k = 1`
|
||||
- Output: `[1]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [1,2,1,2,1,2,3,1,3,2], k = 2`
|
||||
- Output: `[1,2]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
- `k` is in the range `[1, the number of unique elements in the array]`.
|
||||
- It is guaranteed that the answer is unique.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["topKFrequent(nums, k)"]) --> F["freq: count every value in nums"]
|
||||
F --> K["keys of freq, cast to Number"]
|
||||
K --> O["sort by freq[b] - freq[a] — most frequent first"]
|
||||
O --> R(["return slice(0, k)"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @param {number} k
|
||||
* @return {number[]}
|
||||
*/
|
||||
var topKFrequent = function (nums, k) {
|
||||
const freq = {};
|
||||
for (let n of nums) freq[n] = (freq[n] || 0) + 1;
|
||||
|
||||
return Object.keys(freq)
|
||||
.map(Number)
|
||||
.sort((a, b) => freq[b] - freq[a])
|
||||
.slice(0, k);
|
||||
};
|
||||
```
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: '387. First Unique Character in a String'
|
||||
description: Given a string s, find the first non-repeating character in it and return its index. If it does not exist, return -1
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Queue", slug: "queue" }
|
||||
- { name: "Counting", slug: "counting" }
|
||||
video: "rBENYgWy3xU"
|
||||
sidebar:
|
||||
label: 'First Unique Character in a String'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "leetcode"`
|
||||
- Output: `0`
|
||||
- Explanation: The character `'l'` at index `0` is the first character that does not occur at any other index.
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "loveleetcode"`
|
||||
- Output: `2`
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = "aabb"`
|
||||
- Output: `-1`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 10^5`
|
||||
- `s` consists of only lowercase English letters.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["firstUniqChar(s)"]) --> F["Pass 1 — freq: count every char of s"]
|
||||
F --> L{"Pass 2 — more i in 0..len-1?"}
|
||||
L -- no --> E(["return -1"])
|
||||
L -- yes --> Q{"freq[s[i]] == 1?"}
|
||||
Q -- yes --> R(["return i — leftmost wins because the scan is in order"])
|
||||
Q -- no --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string} s
|
||||
* @return {number}
|
||||
*/
|
||||
var firstUniqChar = function (s) {
|
||||
const freq = {};
|
||||
for (let c of s) freq[c] = (freq[c] || 0) + 1;
|
||||
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
if (freq[s[i]] === 1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
```
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: '451. Sort Characters By Frequency'
|
||||
description: Given a string s, sort it in decreasing order based on the frequency of the characters. The frequency of a character is the number of times it appears in the string
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
- { name: "Heap (Priority Queue)", slug: "heap-priority-queue" }
|
||||
- { name: "Bucket Sort", slug: "bucket-sort" }
|
||||
- { name: "Counting", slug: "counting" }
|
||||
video: "OXdXc9HTrIg"
|
||||
sidebar:
|
||||
label: 'Sort Characters By Frequency'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "tree"`
|
||||
- Output: `"eert"`
|
||||
- Explanation: `'e'` appears twice while `'r'` and `'t'` both appear once. So `'e'` must appear before both `'r'` and `'t'`. Therefore `"eetr"` is also a valid answer.
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "cccaaa"`
|
||||
- Output: `"aaaccc"`
|
||||
- Explanation: Both `'c'` and `'a'` appear three times, so both `"cccaaa"` and `"aaaccc"` are valid answers. Note that `"cacaca"` is incorrect, as the same characters must be together.
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = "Aabb"`
|
||||
- Output: `"bbAa"`
|
||||
- Explanation: `"bbaA"` is also a valid answer, but `"Aabb"` is incorrect. Note that `'A'` and `'a'` are treated as two different characters.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 5 * 10^5`
|
||||
- `s` consists of uppercase and lowercase English letters and digits.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["frequencySort(s)"]) --> F["freq: count every char of s"]
|
||||
F --> C["split s and sort with a two-key comparator"]
|
||||
C --> K1["primary: freq[b] - freq[a] — most frequent first"]
|
||||
C --> K2["tie-break: a.localeCompare(b) — alphabetical"]
|
||||
K1 --> R(["join and return"])
|
||||
K2 --> R
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
var frequencySort = function (s) {
|
||||
// count the frequency of each character
|
||||
const freq = {};
|
||||
for (let c of s) freq[c] = (freq[c] || 0) + 1;
|
||||
|
||||
// sort the characters by frequency
|
||||
return s
|
||||
.split("")
|
||||
.sort((a, b) => freq[b] - freq[a] || a.localeCompare(b))
|
||||
.join("");
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: '49. Group Anagrams'
|
||||
description: Given an array of strings strs, group the anagrams together. You can return the answer in any order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "vzdNOK2oB2E"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/30"
|
||||
sidebar:
|
||||
label: 'Group Anagrams'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `strs = ["eat","tea","tan","ate","nat","bat"]`
|
||||
- Output: `[["bat"],["nat","tan"],["ate","eat","tea"]]`
|
||||
- Explanation: There is no string in `strs` that can be rearranged to form `"bat"`. The strings `"nat"` and `"tan"` are anagrams as they can be rearranged to form each other. The strings `"ate"`, `"eat"`, and `"tea"` are anagrams as they can be rearranged to form each other.
|
||||
|
||||
### Example 2:
|
||||
- Input: `strs = [""]`
|
||||
- Output: `[[""]]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `strs = ["a"]`
|
||||
- Output: `[["a"]]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= strs.length <= 10^4`
|
||||
- `0 <= strs[i].length <= 100`
|
||||
- `strs[i]` consists of lowercase English letters.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["groupAnagrams(strs)"]) --> K["sorted[i] = letters of strs[i], sorted — the anagram key"]
|
||||
K --> I["anagrams = {}"]
|
||||
I --> L{"more i in 0..n-1?"}
|
||||
L -- no --> E(["return Object.values(anagrams)"])
|
||||
L -- yes --> Q{"anagrams has sorted[i]?"}
|
||||
Q -- no --> N["anagrams[sorted[i]] = [strs[i]]"]
|
||||
Q -- yes --> P["anagrams[sorted[i]].push(strs[i])"]
|
||||
N --> L
|
||||
P --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string[]} strs
|
||||
* @return {string[][]}
|
||||
*/
|
||||
var groupAnagrams = function(strs) {
|
||||
let sorted = strs.map(str => str.split("").sort().join(""));
|
||||
let anagrams = {};
|
||||
|
||||
for (let i = 0; i < strs.length; i++){
|
||||
if(!anagrams[sorted[i]]){
|
||||
anagrams[sorted[i]] = [strs[i]]
|
||||
}else{
|
||||
anagrams[sorted[i]].push(strs[i])
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(anagrams);
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: Hash Table
|
||||
description: 'A key maps to a value in constant time, which removes the need for a full scan.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
Sometimes when programming, we need a data structure that maps an arbitrary data type to another arbitrary data type, like an array with non-integer indices. For example, a phone book that allows lookup by a person's name requires such data type.
|
||||
|
||||
A **hash table** (or a hash map) is one way of representing this data structure. The idea of hash table is this:
|
||||
|
||||
Data is stored in a fix-sized array, where each data value has an unique index. When we add a new `(key, value)` pair to the array, we use a hash function to generate a hash value within the range of the array with the `key`. We then store the data `value` at the index indicated by the generated hash value. Later, when we retrieve the data with the `key` from the hash table, we hash the `key` again and look up the data in the array with the corresponding index. Since the hash function always returns the same hash values with the same input, we will always find the correct item in that index.
|
||||
|
||||
|
||||
<Frame caption="Hash Table Operations">
|
||||
|
||||

|
||||
|
||||
</Frame>
|
||||
|
||||
|
||||
## Efficiency
|
||||
|
||||
Assuming the hash function uses constant time, the average time complexity is `O(n / k)` for each insertion/access operation on the table, where `n` is the number of entries in the hash table and `k` is the array size of the table. This assumes the hash function distributes keys uniformly across the `k` buckets, so each bucket holds about `n / k` entries on average.
|
||||
|
||||
### Frequency Counting
|
||||
|
||||
```python
|
||||
def get_counter(arr: list[int]) -> dict[int, int]:
|
||||
# initialize a hash map to store each number and its count
|
||||
counter: dict[int, int] = {}
|
||||
for num in arr:
|
||||
# check if num is a key in the hash map
|
||||
if num in counter:
|
||||
# update the count of num to increase by 1
|
||||
counter[num] += 1
|
||||
else:
|
||||
# set the count of num to 1
|
||||
counter[num] = 1
|
||||
return counter
|
||||
```
|
||||
|
||||
::: tip[Python provides a built-in `collections.Counter` class]
|
||||
```python
|
||||
from collections import Counter
|
||||
|
||||
fq = Counter(arr)
|
||||
```
|
||||
:::
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: '739. Daily Temperatures'
|
||||
description: Given an array of integers temperatures represents the daily temperatures, return an array answer such that answer[i] is the number of days you have to wait after the i^th day to get a warmer temperature. If there is no future day for which this is possible, keep answer[i] == 0 instead
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
video: "cTBiBSnjO3c"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/72"
|
||||
sidebar:
|
||||
label: 'Daily Temperatures'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `temperatures = [73,74,75,71,69,72,76,73]`
|
||||
- Output: `[1,1,4,2,1,1,0,0]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `temperatures = [30,40,50,60]`
|
||||
- Output: `[1,1,1,0]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `temperatures = [30,60,90]`
|
||||
- Output: `[1,1,0]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= temperatures.length <= 10^5`
|
||||
- `30 <= temperatures[i] <= 100`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["dailyTemperatures(temperatures)"]) --> I["ans = zeros(n), stack = [] — indices of days still waiting"]
|
||||
I --> L{"more (i, temp)?"}
|
||||
L -- no --> E(["return ans"])
|
||||
L -- yes --> W{"stack non-empty and temperatures[stack[-1]] < temp?"}
|
||||
W -- yes --> P["prev = stack.pop(); ans[prev] = i - prev — today resolves that day"]
|
||||
P --> W
|
||||
W -- no --> A["stack.append(i)"]
|
||||
A --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
|
||||
ans = [0] * len(temperatures)
|
||||
stack = []
|
||||
for i, temp in enumerate(temperatures):
|
||||
while stack and temperatures[stack[-1]] < temp:
|
||||
prev = stack.pop()
|
||||
ans[prev] = i - prev
|
||||
stack.append(i)
|
||||
return ans
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: '853. Car Fleet'
|
||||
description: There are n cars at given miles away from the starting mile 0, traveling to reach the mile target
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
video: "Pr6T-3yB9RM"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/73"
|
||||
sidebar:
|
||||
label: 'Car Fleet'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]`
|
||||
- Output: `3`
|
||||
- Explanation: • The cars starting at `10 (speed 2`) and `8 (speed 4`) become a fleet, meeting each other at `12`. The fleet forms at `target`. • The car starting at `0 (speed 1`) does not catch up to any other car, so it is a fleet by itself. • The cars starting at `5 (speed 1`) and `3 (speed 3`) become a fleet, meeting each other at `6`. The fleet moves at `speed 1` until it reaches `target`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `target = 10, position = [3], speed = [3]`
|
||||
- Output: `1`
|
||||
- Explanation: There is only one car, hence there is only one fleet.
|
||||
|
||||
### Example 3:
|
||||
- Input: `target = 100, position = [0,2,4], speed = [4,2,1]`
|
||||
- Output: `1`
|
||||
- Explanation: • The cars starting at `0 (speed 4`) and `2 (speed 2`) become a fleet, meeting each other at `4`. The car starting at `4 (speed 1`) travels to `5`. • Then, the fleet at `4 (speed 2`) and the car at `position 5 (speed 1`) become one fleet, meeting each other at `6`. The fleet moves at `speed 1` until it reaches `target`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == position.length == speed.length`
|
||||
- `1 <= n <= 10^5`
|
||||
- `0 < target <= 10^6`
|
||||
- `0 <= position[i] < target`
|
||||
- All the values of `position` are unique.
|
||||
- `0 < speed[i] <= 10^6`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["carFleet(target, position, speed)"]) --> O["zip and sort by position, descending — nearest the target first"]
|
||||
O --> I["stack = [] — arrival time of each fleet's leader"]
|
||||
I --> L{"more (pos, spd)?"}
|
||||
L -- no --> E(["return len(stack) — one entry per fleet"])
|
||||
L -- yes --> T["time = (target - pos) / spd"]
|
||||
T --> Q{"stack empty or time > stack[-1]?"}
|
||||
Q -- yes --> A["stack.append(time) — slower, so it starts a new fleet"]
|
||||
Q -- no --> B["discard — it catches the car ahead and joins that fleet"]
|
||||
A --> L
|
||||
B --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def carFleet(self, target: int, position: List[int], speed: List[int]) -> int:
|
||||
stack = []
|
||||
for pos, spd in sorted(zip(position, speed), reverse=True):
|
||||
time = (target - pos) / spd
|
||||
if not stack or time > stack[-1]:
|
||||
stack.append(time)
|
||||
|
||||
return len(stack)
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Monotonic Stack
|
||||
description: 'A stack keeps its items in sorted order, which finds the next larger or smaller item in one pass.'
|
||||
hidden: true
|
||||
---
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: '238. Product of Array Except Self'
|
||||
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]
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
video: "bNvIQI2wAjk"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/37"
|
||||
sidebar:
|
||||
label: 'Product of Array Except Self'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
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]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `2 <= nums.length <= 10^5`
|
||||
- `-30 <= nums[i] <= 30`
|
||||
- The input is generated such that `answer[i]` is guaranteed to fit in a 32-bit integer.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["productExceptSelf(nums)"]) --> I["answer = ones(n), rightArr = ones(n)"]
|
||||
I --> P1["Pass 1 — left to right: answer[i] = nums[i-1] * answer[i-1]"]
|
||||
P1 --> P2["Pass 2 — right to left: rightArr[i] = nums[i+1] * rightArr[i+1]"]
|
||||
P2 --> P3["Pass 3: answer[i] *= rightArr[i]"]
|
||||
P3 --> E(["return answer — no division needed"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @return {number[]}
|
||||
*/
|
||||
var productExceptSelf = function(nums) {
|
||||
const n = nums.length;
|
||||
const answer = new Array(n).fill(1);
|
||||
const rightArr = new Array(n).fill(1);
|
||||
|
||||
for (let i = 1; i < n; i++) {
|
||||
answer[i] = nums[i - 1] * answer[i - 1];
|
||||
}
|
||||
for (let i = n - 2; i >= 0; i--) {
|
||||
rightArr[i] = nums[i + 1] * rightArr[i + 1];
|
||||
}
|
||||
for (let i = 0; i < n; i++) {
|
||||
answer[i] *= rightArr[i];
|
||||
}
|
||||
return answer;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: '303. Range Sum Query - Immutable'
|
||||
description: Given an integer array nums, handle multiple queries of the following type
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Design", slug: "design" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
video: "2pndAmo_sMA"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/36"
|
||||
sidebar:
|
||||
label: 'Range Sum Query - Immutable'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `["NumArray", "sumRange", "sumRange", "sumRange"]` `[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]`
|
||||
- Output: `[null, 1, -1, -3]`
|
||||
- Explanation: `NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);` `numArray.sumRange(0, 2); // return (-2) + 0 + 3 = 1` `numArray.sumRange(2, 5); // return 3 + (-5) + 2 + (-1) = -1` `numArray.sumRange(0, 5); // return (-2) + 0 + 3 + (-5) + 2 + (-1) = -3`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^4`
|
||||
- `-10^5 <= nums[i] <= 10^5`
|
||||
- `0 <= left <= right < nums.length`
|
||||
- At most `10^4` calls will be made to `sumRange`.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph build["constructor(nums) — O(n) once"]
|
||||
A["prefix = [0]"] --> B{"more n in nums?"}
|
||||
B -- yes --> C["prefix.push(last + n)"]
|
||||
C --> B
|
||||
end
|
||||
build --> Q
|
||||
subgraph Q["sumRange(left, right) — O(1) per call"]
|
||||
D(["return prefix[right + 1] - prefix[left]"])
|
||||
end
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
*/
|
||||
class NumArray {
|
||||
constructor(nums) {
|
||||
this.prefix = [0];
|
||||
for (let n of nums){
|
||||
this.prefix.push(this.prefix[this.prefix.length - 1] + n);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {number} left
|
||||
* @param {number} right
|
||||
* @return {number}
|
||||
*/
|
||||
sumRange(left, right) {
|
||||
return this.prefix[right + 1] - this.prefix[left];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Your NumArray object will be instantiated and called as such:
|
||||
* var obj = new NumArray(nums)
|
||||
* var param_1 = obj.sumRange(left,right)
|
||||
*/
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: '525. Contiguous Array'
|
||||
description: Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/39"
|
||||
sidebar:
|
||||
label: 'Contiguous Array'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [0,1]`
|
||||
- Output: `2`
|
||||
- Explanation: [0, 1] is the longest contiguous subarray with an equal number of `0` and `1`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [0,1,0]`
|
||||
- Output: `2`
|
||||
- Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of `0` and `1`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [0,1,1,1,1,1,0,0,0]`
|
||||
- Output: `6`
|
||||
- Explanation: `[1,1,1,0,0,0]` is the longest contiguous subarray with equal number of `0` and `1`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `nums[i]` is either 0 or 1.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def findMaxLength(self, nums: List[int]) -> int:
|
||||
map = {0: -1}
|
||||
count = 0
|
||||
longest = 0
|
||||
|
||||
for i, num in enumerate(nums):
|
||||
count += 1 if num == 1 else -1
|
||||
|
||||
if count in map:
|
||||
longest = max(longest, i - map[count])
|
||||
else:
|
||||
map[count] = i
|
||||
|
||||
return longest
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: '560. Subarray Sum Equals K'
|
||||
description: Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
video: "fFVZt-6sgyo"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/38"
|
||||
sidebar:
|
||||
label: 'Subarray Sum Equals K'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,1,1], k = 2`
|
||||
- Output: `2`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [1,2,3], k = 3`
|
||||
- Output: `2`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 2 * 10^4`
|
||||
- `-1000 <= nums[i] <= 1000`
|
||||
- `-10^7 <= k <= 10^7`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["subarraySum(nums, k)"]) --> I["map = {0: 1} — the empty prefix; sum = 0, count = 0"]
|
||||
I --> L{"more n in nums?"}
|
||||
L -- no --> E(["return count"])
|
||||
L -- yes --> A["sum += n"]
|
||||
A --> Q{"map has sum - k?"}
|
||||
Q -- yes --> C["count += map.get(sum - k) — every earlier prefix that closes a window"]
|
||||
Q -- no --> W["map.set(sum, count of sum + 1)"]
|
||||
C --> W
|
||||
W --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @param {number} k
|
||||
* @return {number}
|
||||
*/
|
||||
var subarraySum = function(nums, k) {
|
||||
const map = new Map();
|
||||
map.set(0, 1);
|
||||
|
||||
let sum = 0;
|
||||
let count = 0;
|
||||
|
||||
for (let n of nums){
|
||||
sum += n;
|
||||
|
||||
if(map.has(sum - k)){
|
||||
count += map.get(sum - k);
|
||||
}
|
||||
|
||||
map.set(sum, (map.get(sum) || 0) + 1);
|
||||
}
|
||||
return count
|
||||
};
|
||||
```
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: '974. Subarray Sums Divisible by K'
|
||||
description: Given an integer array nums and an integer k, return the number of non-empty subarrays that have a sum divisible by k
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/40"
|
||||
sidebar:
|
||||
label: 'Subarray Sums Divisible by K'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [4,5,0,-2,-3,1], k = 5`
|
||||
- Output: `7`
|
||||
- Explanation: There are `7` subarrays with a sum divisible by `k = 5`: [4, `5`, `0`, `-2`, `-3`, 1], `[5]`, [5, 0], [5, `0`, `-2`, -3], `[0]`, [0, `-2`, -3], [-2, -3]
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [5], k = 9`
|
||||
- Output: `0`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 3 * 10^4`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
- `2 <= k <= 10^4`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def subarraysDivByK(self, nums: list[int], k: int) -> int:
|
||||
remainder_counts = {0: 1}
|
||||
cur_sum = 0
|
||||
count = 0
|
||||
|
||||
for n in nums:
|
||||
cur_sum += n
|
||||
remainder = cur_sum % k
|
||||
|
||||
if remainder in remainder_counts:
|
||||
count += remainder_counts[remainder]
|
||||
|
||||
remainder_counts[remainder] = remainder_counts.get(remainder, 0) + 1
|
||||
|
||||
return count
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Prefix Sum
|
||||
description: 'A running total is stored at each index, so the sum of any range is one subtraction.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
Suppose you are handed an array and asked for the sum of the elements between index `i` and index `j`. One query is easy: loop from `i` to `j` and add. But now imagine thousands of such queries over the same array, or a search that inspects the sum of every possible subarray. Recomputing each range from scratch costs `O(n)` per query, and the work piles up fast.
|
||||
|
||||
Prefix sum removes that repetition. You spend `O(n)` once to precompute running totals, and afterward every range sum collapses into a single subtraction that runs in `O(1)`, no matter how wide the range.
|
||||
|
||||
<YouTube id="CRVxUgmIO8M" title="Prefix Sum Explained" />
|
||||
|
||||
## Transformation
|
||||
|
||||
A prefix sum turns an array into a new array of running totals. For an input array `arr` of length `n`, build a `prefix` array of length `n + 1`:
|
||||
|
||||
`prefix[0] = 0`
|
||||
|
||||
`prefix[k] = arr[0] + arr[1] + ... + arr[k-1]`
|
||||
|
||||
Each entry is the previous total plus one more element, so a single pass builds the whole thing:
|
||||
|
||||
`prefix[k] = prefix[k-1] + arr[k-1]`
|
||||
|
||||
The leading `prefix[0] = 0` is the sum of zero elements. That extra slot is what makes the range math below land exactly on the boundaries, which is why `prefix` has one more cell than `arr`.
|
||||
|
||||
|
||||
<Frame caption="Each bar is the running total up to that index. Every step keeps the previous total (blue) and adds one new element (amber)" hint="">
|
||||
|
||||

|
||||
|
||||
|
||||
</Frame>
|
||||
|
||||
## Any range sum is one subtraction
|
||||
|
||||
The sum of the subarray from index `i` up to (but not including) `j` is `prefix[j] - prefix[i]`. `prefix[j]` is the total of everything before `j`, and `prefix[i]` is the total of everything before `i`, so subtracting throws away the front you did not ask for and leaves the middle.
|
||||
|
||||
## Watch the boundaries
|
||||
|
||||
The one place prefix sums trip people up is the off-by-one at the edges. Keep three facts straight: `prefix` has length `n + 1`, `prefix[0]` is `0`, and `sum(arr[i..j-1]) = prefix[j] - prefix[i]` uses an exclusive right index `j`. If a problem hands you an inclusive right index `r`, translate it to `prefix[r + 1] - prefix[i]`.
|
||||
|
||||
## When to reach for it
|
||||
|
||||
Prefix sums fit whenever you need repeated range sums over a fixed array, or when an algorithm searches across subarray boundaries. For a single one-off sum, a plain loop is simpler; the prefix array earns its `O(n)` setup only once the same array is queried many times.
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: '1004. Max Consecutive Ones III'
|
||||
description: Given a binary array nums and an integer k, return the maximum number of consecutive 1's in the array if you can flip at most k 0's
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/56"
|
||||
sidebar:
|
||||
label: 'Max Consecutive Ones III'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2`
|
||||
- Output: `6`
|
||||
- Explanation: `[1,1,1,0,0,1,1,1,1,1,1]` Bolded numbers were flipped from `0` to `1`. The longest subarray is underlined.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], k = 3`
|
||||
- Output: `10`
|
||||
- Explanation: `[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]` Bolded numbers were flipped from `0` to `1`. The longest subarray is underlined.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `nums[i]` is either 0 or 1.
|
||||
- `0 <= k <= nums.length`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def longestOnes(self, nums: list[int], k: int) -> int:
|
||||
left = 0
|
||||
cur = 0
|
||||
ans = 0
|
||||
|
||||
for right in range(len(nums)):
|
||||
if nums[right] == 0:
|
||||
cur += 1
|
||||
while cur > k:
|
||||
if nums[left] == 0:
|
||||
cur -= 1
|
||||
left += 1
|
||||
|
||||
ans = max(ans, right - left + 1)
|
||||
|
||||
return ans
|
||||
```
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: '121. Best Time to Buy and Sell Stock'
|
||||
description: You are given an array prices where prices[i] is the price of a given stock on the i^th day
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
video: "1pkOgXD63yU"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/50"
|
||||
sidebar:
|
||||
label: 'Best Time to Buy and Sell Stock'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `prices = [7,1,5,3,6,4]`
|
||||
- Output: `5`
|
||||
- Explanation: Buy on day `2` (price = `1`) and sell on day `5` (price = `6`), profit = `6-1 = 5`. Note that buying on day `2` and selling on day `1` is not allowed because you must buy before you sell.
|
||||
|
||||
### Example 2:
|
||||
- Input: `prices = [7,6,4,3,1]`
|
||||
- Output: `0`
|
||||
- Explanation: In this case, no transactions are done and the max profit = `0`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= prices.length <= 10^5`
|
||||
- `0 <= prices[i] <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
::::warning
|
||||
The diagram traces the code below literally. That code does not run: `curr` and `ans` are never initialised, so the first `curr > left` test raises `NameError`, and `left` holds a price rather than an index, so `prices[left]` indexes by value.
|
||||
::::
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["maxProfit(prices)"]) --> I["left = min(prices)"]
|
||||
I --> F{"more right in range(len(prices))?"}
|
||||
F -- no --> E(["return ans"])
|
||||
F -- yes --> W{"curr > left?"}
|
||||
W -- yes --> P["curr -= prices[left]; left += 1"]
|
||||
P --> W
|
||||
W -- no --> M["ans = max(ans, curr)"]
|
||||
M --> F
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def maxProfit(self, prices: List[int]) -> int:
|
||||
left = min(prices)
|
||||
for right in range(len(prices)):
|
||||
while curr > left:
|
||||
curr -= prices[left]
|
||||
left += 1
|
||||
ans = max(ans, curr)
|
||||
return ans
|
||||
```
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: '209. Minimum Size Subarray Sum'
|
||||
description: Given an array of positive integers nums and a positive integer target, return the minimal length of a subarray whose sum is greater than or equal to target. If there is no such subarray, return 0 instead
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
- { name: "Prefix Sum", slug: "prefix-sum" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/54"
|
||||
sidebar:
|
||||
label: 'Minimum Size Subarray Sum'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log(n)).
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `target = 7, nums = [2,3,1,2,4,3]`
|
||||
- Output: `2`
|
||||
- Explanation: The subarray `[4,3]` has the minimal length under the problem constraint.
|
||||
|
||||
### Example 2:
|
||||
- Input: `target = 4, nums = [1,4,4]`
|
||||
- Output: `1`
|
||||
|
||||
### Example 3:
|
||||
- Input: `target = 11, nums = [1,1,1,1,1,1,1,1]`
|
||||
- Output: `0`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= target <= 10^9`
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `1 <= nums[i] <= 10^4`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def minSubArrayLen(self, target: int, nums: list[int]) -> int:
|
||||
window_sum = 0
|
||||
length = len(nums) + 1
|
||||
left = 0
|
||||
|
||||
for right in range(len(nums)):
|
||||
window_sum += nums[right]
|
||||
while window_sum >= target:
|
||||
length = min(length, right - left + 1)
|
||||
window_sum -= nums[left]
|
||||
left += 1
|
||||
if length > len(nums):
|
||||
return 0
|
||||
return length
|
||||
```
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: '3. Longest Substring Without Repeating Characters'
|
||||
description: Given a string s, find the length of the longest substring without duplicate characters
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
video: "wiGpQwVHdE0"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/51"
|
||||
sidebar:
|
||||
label: 'Longest Substring Without Repeating Characters'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "abcabcbb"`
|
||||
- Output: `3`
|
||||
- Explanation: The answer is "abc", with the length of `3`. Note that "bca" and "cab" are also correct answers.
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "bbbbb"`
|
||||
- Output: `1`
|
||||
- Explanation: The answer is "b", with the length of `1`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = "pwwkew"`
|
||||
- Output: `3`
|
||||
- Explanation: The answer is "wke", with the length of `3`. Notice that the answer must be a substring, "pwke" is a subsequence and not a substring.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `0 <= s.length <= 10^5`
|
||||
- `s` consists of English letters, digits, symbols and spaces.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["lengthOfLongestSubstring(s)"]) --> I["left = 0, ans = 0, window = set()"]
|
||||
I --> L{"more (right, c) in s?"}
|
||||
L -- no --> E(["return ans"])
|
||||
L -- yes --> W{"c already in window?"}
|
||||
W -- yes --> P["window.remove(s[left]); left += 1 — shrink until c is free"]
|
||||
P --> W
|
||||
W -- no --> A["window.add(c)"]
|
||||
A --> M["ans = max(ans, right - left + 1)"]
|
||||
M --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def lengthOfLongestSubstring(self, s: str) -> int:
|
||||
left = 0
|
||||
ans = 0
|
||||
window = set()
|
||||
for right, c in enumerate(s):
|
||||
while c in window:
|
||||
window.remove(s[left])
|
||||
left += 1
|
||||
window.add(c)
|
||||
ans = max(ans, right - left + 1)
|
||||
|
||||
return ans
|
||||
```
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: '424. Longest Repeating Character Replacement'
|
||||
description: You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
video: "gqXU1UyA8pk"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/52"
|
||||
sidebar:
|
||||
label: 'Longest Repeating Character Replacement'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "ABAB", k = 2`
|
||||
- Output: `4`
|
||||
- Explanation: Replace the two 'A's with two 'B's or vice versa.
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "AABABBA", k = 1`
|
||||
- Output: `4`
|
||||
- Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA". The substring "BBBB" has the longest repeating letters, which is `4`. There may exists other ways to achieve this answer too.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 10^5`
|
||||
- `s` consists of only uppercase English letters.
|
||||
- `0 <= k <= s.length`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["characterReplacement(s, k)"]) --> I["count = {}, res = 0, l = 0, maxF = 0"]
|
||||
I --> L{"more (r, c) in s?"}
|
||||
L -- no --> E(["return res"])
|
||||
L -- yes --> A["count[c] += 1; maxF = max(maxF, count[c])"]
|
||||
A --> W{"(r - l + 1) - maxF > k? — too many chars to replace"}
|
||||
W -- yes --> P["count[s[l]] -= 1; l += 1 — shrink from the left"]
|
||||
P --> W
|
||||
W -- no --> M["res = max(res, r - l + 1)"]
|
||||
M --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def characterReplacement(self, s: str, k: int) -> int:
|
||||
count = {}
|
||||
res = 0
|
||||
|
||||
l = 0
|
||||
maxF = 0
|
||||
for r, c in enumerate(s):
|
||||
count[c] = 1 + count.get(c, 0)
|
||||
maxF = max(maxF, count[c])
|
||||
|
||||
while (r - l + 1) - maxF > k:
|
||||
count[s[l]] -= 1
|
||||
l += 1
|
||||
|
||||
res = max(res, r - l + 1)
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: '567. Permutation in String'
|
||||
description: Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
video: "UbyhOgBN834"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/53"
|
||||
sidebar:
|
||||
label: 'Permutation in String'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s1 = "ab", s2 = "eidbaooo"`
|
||||
- Output: `true`
|
||||
- Explanation: `s2` contains one permutation of `s1` ("ba").
|
||||
|
||||
### Example 2:
|
||||
- Input: `s1 = "ab", s2 = "eidboaoo"`
|
||||
- Output: `false`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s1.length, s2.length <= 10^4`
|
||||
- `s1` and `s2` consist of lowercase English letters.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["checkInclusion(s1, s2)"]) --> G{"k = len(s1) > len(s2)?"}
|
||||
G -- yes --> X(["return False"])
|
||||
G -- no --> I["need = Counter(s1), window = Counter()"]
|
||||
I --> L{"more (right, c) in s2?"}
|
||||
L -- no --> E(["return False"])
|
||||
L -- yes --> A["window[c] += 1"]
|
||||
A --> B{"right >= k? — window is now longer than k"}
|
||||
B -- yes --> C["drop s2[right - k] from window, deleting the key at 0"]
|
||||
B -- no --> Q
|
||||
C --> Q{"window == need?"}
|
||||
Q -- yes --> R(["return True"])
|
||||
Q -- no --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
from collections import Counter
|
||||
|
||||
|
||||
class Solution:
|
||||
def checkInclusion(self, s1: str, s2: str) -> bool:
|
||||
k = len(s1)
|
||||
if k > len(s2):
|
||||
return False
|
||||
|
||||
need = Counter(s1)
|
||||
window = Counter()
|
||||
|
||||
for right, c in enumerate(s2):
|
||||
window[c] += 1
|
||||
if right >= k:
|
||||
left_char = s2[right - k]
|
||||
window[left_char] -= 1
|
||||
if window[left_char] == 0:
|
||||
del window[left_char]
|
||||
if window == need:
|
||||
return True
|
||||
|
||||
return False
|
||||
```
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: '643. Maximum Average Subarray I'
|
||||
description: You are given an integer array nums consisting of n elements, and an integer k
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Sliding Window", slug: "sliding-window" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/55"
|
||||
sidebar:
|
||||
label: 'Maximum Average Subarray I'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,12,-5,-6,50,3], k = 4`
|
||||
- Output: `12.75000`
|
||||
- Explanation: Maximum average is `(12 - 5 - 6 + 50`) / `4 = 51 / 4 = 12.75`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [5], k = 1`
|
||||
- Output: `5.00000`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == nums.length`
|
||||
- `1 <= k <= n <= 10^5`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def findMaxAverage(self, nums: list[int], k: int) -> float:
|
||||
window_sum = 0
|
||||
for i in range(k):
|
||||
window_sum += nums[i]
|
||||
largest = window_sum
|
||||
|
||||
for right in range(k, len(nums)):
|
||||
left = right - k
|
||||
window_sum -= nums[left]
|
||||
window_sum += nums[right]
|
||||
largest = max(largest, window_sum)
|
||||
|
||||
return largest / k
|
||||
```
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: Sliding Window
|
||||
description: 'Sliding window is a specialized two-pointer family where the pointers define a contiguous interval and you maintain information about that entire interval as it moves.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
|
||||
<YouTube id="tSTO9w3piAo" title="Sliding Window" />
|
||||
|
||||
|
||||
Many problems have two moving boundaries. That alone does not make them sliding window problems.
|
||||
|
||||
A problem belongs to this family when:
|
||||
|
||||
- the answer depends on the subarray or substring between left and right
|
||||
- you can update the window state incrementally as elements enter or leave
|
||||
- you maintain a condition that tells you whether the window is valid
|
||||
|
||||
:::tip
|
||||
The rule for all three: every element enters once and leaves once, so the whole thing runs in O(n).
|
||||
:::
|
||||
|
||||
<Accordion>
|
||||
<AccordionItem title="What is an invariant?">
|
||||
An invariant is a statement that stays true before and after every iteration. In a sliding window, that statement usually describes the window itself, such as “the window is valid,” “it has no duplicates,” or “its sum is at most the target.” It can also describe a helper structure, such as “the frequency map matches the characters currently inside the window.”
|
||||
</AccordionItem>
|
||||
<AccordionItem title="Why invariants matter?">
|
||||
Without an invariant, shrinking the window is just a guess. You no longer know when the window is valid, when to update the answer, or why the left pointer should stop. The invariant is what makes the algorithm correct.
|
||||
</AccordionItem>
|
||||
<AccordionItem title="How sliding window differs from other two-pointer families?">
|
||||
Sliding window is not the same as every other two-pointer pattern. In Container With Most Water, the interval is only the remaining search space. In sliding window, the interval itself is the thing you care about. In Remove Duplicates, the left side is already the finished answer prefix. In sliding window, both pointers describe a live window that is still being checked.
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
|
||||
## 1. Fixed-size windows
|
||||
|
||||
:::tip
|
||||
The window size never changes. The main task is updating the window state efficiently when one element enters and one leaves.
|
||||
:::
|
||||
|
||||
|
||||
<Frame caption="Sliding Window Fixed" hint="">
|
||||
|
||||

|
||||
|
||||
</Frame>
|
||||
|
||||
|
||||
```python
|
||||
def sliding_window_fixed(input, window_size):
|
||||
ans = window = input[0:window_size]
|
||||
for right in range(window_size, len(input)):
|
||||
left = right - window_size
|
||||
remove input[left] from window
|
||||
append input[right] to window
|
||||
ans = optimal(ans, window)
|
||||
return ans
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="">
|
||||
Add `nums[right]` to the window.
|
||||
</Step>
|
||||
<Step title="">
|
||||
If the window has `k` elements, record the answer.
|
||||
</Step>
|
||||
<Step title="">
|
||||
Remove the leftmost element so the window stays at size `k`.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
## 2. Flexible-size windows with a validity rule
|
||||
|
||||
:::tip
|
||||
Here the window expands until invalid, then shrinks until valid again.
|
||||
:::
|
||||
|
||||
<Frame caption="Sliding Window Flexible" hint="">
|
||||
|
||||

|
||||
|
||||
</Frame>
|
||||
|
||||
|
||||
<Tabs inline>
|
||||
<Tab title="Longest Window">
|
||||
|
||||
```python
|
||||
def sliding_window_flexible_longest(input):
|
||||
initialize window, ans
|
||||
left = 0
|
||||
for right in range(len(input)):
|
||||
append input[right] to window
|
||||
while invalid(window): # update left until window is valid again
|
||||
remove input[left] from window
|
||||
left += 1
|
||||
ans = max(ans, window) # window is guaranteed to be valid here
|
||||
return ans
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Shortest Window">
|
||||
|
||||
```python
|
||||
def sliding_window_flexible_shortest(input):
|
||||
initialize window, ans
|
||||
left = 0
|
||||
for right in range(len(input)):
|
||||
append input[right] to window
|
||||
while valid(window):
|
||||
ans = min(ans, window) # window is guaranteed to be valid here
|
||||
remove input[left] from window
|
||||
left += 1
|
||||
return ans
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Steps>
|
||||
<Step title="">
|
||||
Add `nums[right]` to the window.
|
||||
</Step>
|
||||
<Step title="">
|
||||
While the window is invalid, remove `nums[left]` and move `left` up.
|
||||
</Step>
|
||||
<Step title="">
|
||||
The window is now valid. Record its size.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## 3. Frequency-tracking windows
|
||||
|
||||
:::tip
|
||||
The difficulty comes from keeping the bookkeeping correct as characters enter and leave.
|
||||
:::
|
||||
|
||||
|
||||
<Frame caption="Frequency-tracking windows" hint="">
|
||||
|
||||

|
||||
|
||||
</Frame>
|
||||
|
||||
```python
|
||||
from collections import defaultdict
|
||||
|
||||
def frequency_window(s):
|
||||
left = 0
|
||||
counts = defaultdict(int)
|
||||
best = 0
|
||||
|
||||
for right in range(len(s)):
|
||||
counts[s[right]] += 1 # character enters
|
||||
|
||||
while counts[s[right]] > 1: # rule: no repeats
|
||||
counts[s[left]] -= 1 # character leaves
|
||||
left += 1
|
||||
|
||||
best = max(best, right - left + 1)
|
||||
|
||||
return best
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="">
|
||||
Add `s[right]` to the window.
|
||||
</Step>
|
||||
<Step title="">
|
||||
While the window has more than one character, remove `s[left]` and move `left` up.
|
||||
</Step>
|
||||
<Step title="">
|
||||
The window is now valid. Record its size.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
For anagram or "minimum window substring" problems, you also keep a target count. Track how many characters currently match the target with one integer (`matched`). Update it only when a count crosses the target value. This keeps each step `O(1)`.
|
||||
|
||||
```python
|
||||
def anagram_window(s, p):
|
||||
need = defaultdict(int)
|
||||
for c in p:
|
||||
need[c] += 1
|
||||
|
||||
counts = defaultdict(int)
|
||||
matched = 0 # how many chars have counts[c] == need[c]
|
||||
result = []
|
||||
k = len(p)
|
||||
|
||||
for right in range(len(s)):
|
||||
c = s[right]
|
||||
counts[c] += 1
|
||||
if counts[c] == need[c]:
|
||||
matched += 1
|
||||
|
||||
if right >= k: # window too big, remove left
|
||||
d = s[right - k]
|
||||
if counts[d] == need[d]:
|
||||
matched -= 1
|
||||
counts[d] -= 1
|
||||
|
||||
if matched == len(need):
|
||||
result.append(right - k + 1)
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: '150. Evaluate Reverse Polish Notation'
|
||||
description: You are given an array of strings tokens that represents an arithmetic expression in a Reverse Polish Notation
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
video: "iu0082c4HDE"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/69"
|
||||
sidebar:
|
||||
label: 'Evaluate Reverse Polish Notation'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `tokens = ["2","1","+","3","*"]`
|
||||
- Output: `9`
|
||||
- Explanation: `((2 + 1`) * `3`) = `9`
|
||||
|
||||
### Example 2:
|
||||
- Input: `tokens = ["4","13","5","/","+"]`
|
||||
- Output: `6`
|
||||
- Explanation: `(4 + (13 / 5`)) = `6`
|
||||
|
||||
### Example 3:
|
||||
- Input: `tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]`
|
||||
- Output: `22`
|
||||
- Explanation: `((10 * (6 / ((9 + 3`) * `-11`))) + `17`) + `5 = ((10 * (6 / (12 * -11`))) + `17`) + `5 = ((10 * (6 / -132`)) + `17`) + `5 = ((10 * 0`) + `17`) + `5 = (0 + 17`) + `5 = 17 + 5 = 22`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= tokens.length <= 10^4`
|
||||
- `tokens[i]` is either an operator: "+", "-", "*", or "/", or an integer in the range [-200, 200].
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["evalRPN(tokens)"]) --> I["stack = [], operations = + - * /"]
|
||||
I --> L{"more token c?"}
|
||||
L -- no --> E(["return stack[0]"])
|
||||
L -- yes --> Q{"c is an operator?"}
|
||||
Q -- no --> N["stack.append(int(c))"]
|
||||
N --> L
|
||||
Q -- yes --> P["y = stack.pop(), x = stack.pop() — order matters"]
|
||||
P --> C["stack.append(operations[c](x, y))"]
|
||||
C --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def evalRPN(self, tokens: List[str]) -> int:
|
||||
stack = []
|
||||
operations = {
|
||||
"+": lambda x, y: int(x + y),
|
||||
"-": lambda x, y: int(x - y),
|
||||
"*": lambda x, y: int(x * y),
|
||||
"/": lambda x, y: int(x / y),
|
||||
}
|
||||
|
||||
for c in tokens:
|
||||
if c in operations:
|
||||
y = stack.pop()
|
||||
x = stack.pop()
|
||||
calc_func = operations[c]
|
||||
result = calc_func(x, y)
|
||||
stack.append(result)
|
||||
else:
|
||||
stack.append(int(c))
|
||||
|
||||
return stack[0]
|
||||
```
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: '155. Min Stack'
|
||||
description: Design a stack that supports push, pop, top, and retrieving the minimum element in constant time
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Design", slug: "design" }
|
||||
video: "qkLl7nAwDPo"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/68"
|
||||
sidebar:
|
||||
label: 'Min Stack'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must implement a solution with O(1) time complexity for each function.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: ``
|
||||
- Output: ``
|
||||
- Explanation: Input `["MinStack","push","push","push","getMin","pop","top","getMin"]` [[],[-2],[0],[-3],[],[],[],[]] Output `[null,null,null,null,-3,null,0,-2]` Explanation MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); // return `-3` minStack.pop(); minStack.top(); // return `0` minStack.getMin(); // return `-2`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `-2^31 <= val <= 2^31 - 1`
|
||||
- Methods pop, top and getMin operations will always be called on non-empty stacks.
|
||||
- At most 3 * 10^4 calls will be made to push, pop, top, and getMin.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph state["two parallel stacks — same depth, always"]
|
||||
A["stack — the values"]
|
||||
B["minStack — the minimum as of that depth"]
|
||||
end
|
||||
state --> P
|
||||
subgraph P["push(value)"]
|
||||
P1["stack.append(value)"] --> P2["minStack.append(min(value, minStack[-1]))"]
|
||||
end
|
||||
state --> O
|
||||
subgraph O["pop()"]
|
||||
O1["pop both stacks together"]
|
||||
end
|
||||
state --> R
|
||||
subgraph R["top() / getMin() — O(1)"]
|
||||
R1(["stack[-1] / minStack[-1]"])
|
||||
end
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class MinStack:
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
self.minStack = []
|
||||
|
||||
def push(self, value: int) -> None:
|
||||
self.stack.append(value)
|
||||
value = min(value, self.minStack[-1] if self.minStack else value)
|
||||
self.minStack.append(value)
|
||||
|
||||
def pop(self) -> None:
|
||||
self.stack.pop()
|
||||
self.minStack.pop()
|
||||
|
||||
def top(self) -> int:
|
||||
return self.stack[-1]
|
||||
|
||||
def getMin(self) -> int:
|
||||
return self.minStack[-1]
|
||||
|
||||
|
||||
# Your MinStack object will be instantiated and called as such:
|
||||
# obj = MinStack()
|
||||
# obj.push(value)
|
||||
# obj.pop()
|
||||
# param_3 = obj.top()
|
||||
# param_4 = obj.getMin()
|
||||
```
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: '20. Valid Parentheses'
|
||||
description: Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Bracket Sequences", slug: "bracket-sequences" }
|
||||
video: "WTzjTskDFMg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/67"
|
||||
sidebar:
|
||||
label: 'Valid Parentheses'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "()"`
|
||||
- Output: `true`
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "()[]{}"`
|
||||
- Output: `true`
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = "(]"`
|
||||
- Output: `false`
|
||||
|
||||
### Example 4:
|
||||
- Input: `s = "([])"`
|
||||
- Output: `true`
|
||||
|
||||
### Example 5:
|
||||
- Input: `s = "([)]"`
|
||||
- Output: `false`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 10^4`
|
||||
- `s` consists of parentheses only '()[]{}'.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["isValid(s)"]) --> I["stack = [], pairs = closer to opener"]
|
||||
I --> L{"more char c?"}
|
||||
L -- no --> Z{"stack empty?"}
|
||||
Z -- yes --> T(["return True"])
|
||||
Z -- no --> U(["return False — unclosed openers left"])
|
||||
L -- yes --> Q{"c is a closer?"}
|
||||
Q -- no --> A["stack.append(c) — it is an opener"]
|
||||
A --> L
|
||||
Q -- yes --> M{"stack non-empty and pairs[c] == stack[-1]?"}
|
||||
M -- yes --> P["stack.pop() — matched"]
|
||||
P --> L
|
||||
M -- no --> F(["return False — mismatch"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def isValid(self, s: str) -> bool:
|
||||
stack = []
|
||||
pairs = {")": "(", "]": "[", "}": "{"}
|
||||
|
||||
for c in s:
|
||||
if c in pairs:
|
||||
if stack and pairs[c] == stack[-1]:
|
||||
stack.pop()
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
stack.append(c)
|
||||
|
||||
return True if not stack else False
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: '71. Simplify Path'
|
||||
description: You are given an absolute path for a Unix-style file system, which always begins with a slash '/'. Your task is to transform this absolute path into its simplified canonical path
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/70"
|
||||
sidebar:
|
||||
label: 'Simplify Path'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `path = "/home/"`
|
||||
- Output: `"/home"`
|
||||
- Explanation: The trailing slash should be removed.
|
||||
|
||||
### Example 2:
|
||||
- Input: `path = "/home//foo/"`
|
||||
- Output: `"/home/foo"`
|
||||
- Explanation: Multiple consecutive slashes are replaced by a single one.
|
||||
|
||||
### Example 3:
|
||||
- Input: `path = "/home/user/Documents/../Pictures"`
|
||||
- Output: `"/home/user/Pictures"`
|
||||
- Explanation: A double period ".." refers to the directory up a level (the parent directory).
|
||||
|
||||
### Example 4:
|
||||
- Input: `path = "/../"`
|
||||
- Output: `"/"`
|
||||
- Explanation: Going one level up from the root directory is not possible.
|
||||
|
||||
### Example 5:
|
||||
- Input: `path = "/.../a/../b/c/../d/./"`
|
||||
- Output: `"/.../b/d"`
|
||||
- Explanation: "..." is a valid name for a directory in this problem.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= path.length <= 3000`
|
||||
- `path` consists of English letters, digits, period '.', slash '/' or '_'.
|
||||
- `path` is a valid absolute Unix path.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def simplifyPath(self, path: str) -> str:
|
||||
stack = []
|
||||
segments = [item for item in path.split("/") if item]
|
||||
|
||||
for p in segments:
|
||||
if stack and p == "..":
|
||||
stack.pop()
|
||||
elif not stack and p == ".." or p == ".":
|
||||
continue
|
||||
else:
|
||||
stack.append(p)
|
||||
|
||||
return "/" + "/".join(stack)
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Stack
|
||||
description: 'Items are added and removed from one end, so the last item added is the first item removed.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: '11. Container With Most Water'
|
||||
description: You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the i^th line are (i, 0) and (i, height[i])
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "Greedy", slug: "greedy" }
|
||||
video: "UuiTKBwPgAo"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/44"
|
||||
sidebar:
|
||||
label: 'Container With Most Water'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Notice that you may not slant the container.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `height = [1,8,6,2,5,4,8,3,7]`
|
||||
- Output: `49`
|
||||
- Explanation: The above vertical lines are represented by array `[1,8,6,2,5,4,8,3,7]`. In this case, the max area of water (blue section) the container can contain is `49`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `height = [1,1]`
|
||||
- Output: `1`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == height.length`
|
||||
- `2 <= n <= 10^5`
|
||||
- `0 <= height[i] <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["maxArea(height)"]) --> I["res = 0, left = 0, right = len - 1"]
|
||||
I --> W{"left < right?"}
|
||||
W -- no --> E(["return res"])
|
||||
W -- yes --> A["area = (right - left) * min(height[left], height[right])"]
|
||||
A --> M["res = max(res, area)"]
|
||||
M --> C{"height[left] < height[right]?"}
|
||||
C -- yes --> L["left += 1 — drop the shorter wall"]
|
||||
C -- no --> R["right -= 1 — drop the shorter wall"]
|
||||
L --> W
|
||||
R --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def maxArea(self, height: List[int]) -> int: # noqa: F821
|
||||
res = 0
|
||||
left = 0
|
||||
right = len(height) - 1
|
||||
|
||||
while left < right:
|
||||
area = (right - left) * min(height[left], height[right])
|
||||
res = max(res, area)
|
||||
if height[left] < height[right]:
|
||||
left += 1
|
||||
else:
|
||||
right -= 1
|
||||
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: '125. Valid Palindrome'
|
||||
description: A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "String", slug: "string" }
|
||||
video: "jJXJ16kPFWg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/41"
|
||||
sidebar:
|
||||
label: 'Valid Palindrome'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "A man, a plan, a canal: Panama"`
|
||||
- Output: `true`
|
||||
- Explanation: `"amanaplanacanalpanama"` is a palindrome.
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "race a car"`
|
||||
- Output: `false`
|
||||
- Explanation: `"raceacar"` is not a palindrome.
|
||||
|
||||
### Example 3:
|
||||
- Input: `s = " "`
|
||||
- Output: `true`
|
||||
- Explanation: `s` is an empty string `""` after removing non-alphanumeric characters. Since an empty string reads the same forward and backward, it is a palindrome.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 2 * 10^5`
|
||||
- `s` consists only of printable ASCII characters.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["isPalindrome(s)"]) --> N["normal = s stripped of non-alphanumerics, lowercased"]
|
||||
N --> I["i = 0, j = normal.length - 1"]
|
||||
I --> W{"i < j?"}
|
||||
W -- no --> E(["return true — the pointers met in the middle"])
|
||||
W -- yes --> Q{"normal[i] != normal[j]?"}
|
||||
Q -- yes --> F(["return false"])
|
||||
Q -- no --> A["i++; j-- — step both inward"]
|
||||
A --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {string} s
|
||||
* @return {boolean}
|
||||
*/
|
||||
var isPalindrome = function(s) {
|
||||
let normal = s.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()
|
||||
let i = 0, j = normal.length - 1;
|
||||
|
||||
while (i < j) {
|
||||
if(normal[i] !== normal[j]) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: '15. 3Sum'
|
||||
description: Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "jzZsG8n2R9A"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/43"
|
||||
sidebar:
|
||||
label: '3Sum'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Notice that the solution set must not contain duplicate triplets.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [-1,0,1,2,-1,-4]`
|
||||
- Output: `[[-1,-1,2],[-1,0,1]]`
|
||||
- Explanation: `nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0`. `nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0`. `nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0`. The distinct triplets are `[-1,0,1]` and `[-1,-1,2]`. Notice that the order of the output and the order of the triplets does not matter.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [0,1,1]`
|
||||
- Output: `[]`
|
||||
- Explanation: The only possible triplet does not sum up to `0`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [0,0,0]`
|
||||
- Output: `[[0,0,0]]`
|
||||
- Explanation: The only possible triplet sums up to `0`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `3 <= nums.length <= 3000`
|
||||
- `-10^5 <= nums[i] <= 10^5`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["threeSum(nums)"]) --> O["nums.sort() — duplicates become adjacent"]
|
||||
O --> F{"more i in 0..n-1?"}
|
||||
F -- no --> E(["return result"])
|
||||
F -- yes --> D{"nums[i] == nums[i-1]?"}
|
||||
D -- yes --> F
|
||||
D -- no --> P["left = i+1, right = n-1, target = -nums[i]"]
|
||||
P --> W{"left < right?"}
|
||||
W -- no --> F
|
||||
W -- yes --> C["current = nums[left] + nums[right]"]
|
||||
C --> Q{"current vs target"}
|
||||
Q -- equal --> A["append triplet, skip equal neighbours, left += 1, right -= 1"]
|
||||
A --> W
|
||||
Q -- "current < target" --> L["left += 1 — need a bigger sum"]
|
||||
L --> W
|
||||
Q -- "current > target" --> R["right -= 1 — need a smaller sum"]
|
||||
R --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def threeSum(self, nums: list[int]) -> list[list[int]]:
|
||||
nums.sort()
|
||||
result = []
|
||||
n = len(nums)
|
||||
|
||||
for i in range(n):
|
||||
# skip all zero
|
||||
if i > 0 and nums[i] == nums[i - 1]:
|
||||
continue
|
||||
|
||||
# two pointers
|
||||
left = i + 1
|
||||
right = n - 1
|
||||
target = -nums[i]
|
||||
|
||||
while left < right:
|
||||
current = nums[left] + nums[right]
|
||||
|
||||
if current == target:
|
||||
result.append([nums[i], nums[left], nums[right]])
|
||||
|
||||
# skip duplicates
|
||||
while left < right and nums[left] == nums[left + 1]:
|
||||
left += 1
|
||||
while left < right and nums[right] == nums[right - 1]:
|
||||
right -= 1
|
||||
|
||||
# shift pointers
|
||||
left += 1
|
||||
right -= 1
|
||||
|
||||
# since sorted, if current < target, then move left
|
||||
elif current < target:
|
||||
left += 1
|
||||
# since sorted, if current > target, then move right
|
||||
else:
|
||||
right -= 1
|
||||
|
||||
return result
|
||||
```
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: '167. Two Sum II - Input Array Is Sorted'
|
||||
description: Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number. Let these two numbers be numbers[index1] and numbers[index2] where 1 <= index1 < index2 <= numbers.length
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "Binary Search", slug: "binary-search" }
|
||||
video: "cQ1Oz4ckceM"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/42"
|
||||
sidebar:
|
||||
label: 'Two Sum II - Input Array Is Sorted'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Your solution must use only constant extra space.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `numbers = [2,7,11,15], target = 9`
|
||||
- Output: `[1,2]`
|
||||
- Explanation: The sum of `2` and `7` is `9`. Therefore, `index1 = 1`, `index2 = 2`. We return `[1, 2]`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `numbers = [2,3,4], target = 6`
|
||||
- Output: `[1,3]`
|
||||
- Explanation: The sum of `2` and `4` is `6`. Therefore `index1 = 1`, `index2 = 3`. We return `[1, 3]`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `numbers = [-1,0], target = -1`
|
||||
- Output: `[1,2]`
|
||||
- Explanation: The sum of `-1` and `0` is `-1`. Therefore `index1 = 1`, `index2 = 2`. We return `[1, 2]`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `2 <= numbers.length <= 3 * 10^4`
|
||||
- `-1000 <= numbers[i] <= 1000`
|
||||
- `numbers` is sorted in non-decreasing order.
|
||||
- `-1000 <= target <= 1000`
|
||||
- The tests are generated such that there is exactly one solution.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["twoSum(numbers, target)"]) --> I["i = 0, j = len - 1"]
|
||||
I --> W{"i < j?"}
|
||||
W -- no --> E(["return []"])
|
||||
W -- yes --> C["c = numbers[i] + numbers[j]"]
|
||||
C --> Q{"c vs target"}
|
||||
Q -- equal --> R(["return [i+1, j+1] — 1-indexed"])
|
||||
Q -- "c < target" --> L["i += 1 — grow the sum"]
|
||||
L --> W
|
||||
Q -- "c > target" --> H["j -= 1 — shrink the sum"]
|
||||
H --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def twoSum(self, numbers: List[int], target: int) -> List[int]:
|
||||
i = 0
|
||||
j = len(numbers) - 1
|
||||
|
||||
while i < j:
|
||||
c = numbers[i] + numbers[j]
|
||||
if c == target:
|
||||
return [i + 1, j + 1]
|
||||
elif c < target:
|
||||
i+=1
|
||||
else:
|
||||
j-=1
|
||||
return []
|
||||
```
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: '189. Rotate Array'
|
||||
description: Given an integer array nums, rotate the array to the right by k steps, where k is non-negative
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/47"
|
||||
sidebar:
|
||||
label: 'Rotate Array'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,2,3,4,5,6,7], k = 3`
|
||||
- Output: `[5,6,7,1,2,3,4]`
|
||||
- Explanation: rotate `1` steps to the right: `[7,1,2,3,4,5,6]` rotate `2` steps to the right: `[6,7,1,2,3,4,5]` rotate `3` steps to the right: `[5,6,7,1,2,3,4]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [-1,-100,3,99], k = 2`
|
||||
- Output: `[3,99,-1,-100]`
|
||||
- Explanation: rotate `1` steps to the right: `[99,-1,-100,3]` rotate `2` steps to the right: `[3,99,-1,-100]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `-2^31 <= nums[i] <= 2^31 - 1`
|
||||
- `0 <= k <= 10^5`
|
||||
- Try to come up with as many solutions as you can. There are at least three different ways to solve this problem.
|
||||
- Could you do it in-place with O(1) extra space?
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def rotate(self, nums: list[int], k: int) -> None:
|
||||
N = len(nums) - 1
|
||||
cut = k % len(nums)
|
||||
|
||||
def reverse(l, r):
|
||||
while l <= r:
|
||||
nums[l], nums[r] = nums[r], nums[l]
|
||||
l += 1
|
||||
r -= 1
|
||||
|
||||
reverse(0, N)
|
||||
reverse(0, cut - 1)
|
||||
reverse(cut, N)
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: '344. Reverse String'
|
||||
description: Write a function that reverses a string. The input string is given as an array of characters s
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "String", slug: "string" }
|
||||
video: "_d0T_2Lk2qA"
|
||||
sidebar:
|
||||
label: 'Reverse String'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
:::warning
|
||||
You must do this by modifying the input array in-place with O(1) extra memory.
|
||||
:::
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = ["h","e","l","l","o"]`
|
||||
- Output: `["o","l","l","e","h"]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = ["H","a","n","n","a","h"]`
|
||||
- Output: `["h","a","n","n","a","H"]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= s.length <= 10^5`
|
||||
- `s[i]` is a printable ascii character.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["reverseString(s)"]) --> I["i = 0, j = s.length - 1"]
|
||||
I --> W{"i < j?"}
|
||||
W -- no --> E(["done — s was reversed in place, nothing returned"])
|
||||
W -- yes --> A["swap s[i] and s[j]"]
|
||||
A --> B["i++; j--"]
|
||||
B --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {character[]} s
|
||||
* @return {void} Do not return anything, modify s in-place instead.
|
||||
*/
|
||||
var reverseString = function(s) {
|
||||
let i = 0;
|
||||
let j = s.length - 1;
|
||||
|
||||
while (i < j) {
|
||||
[s[i], s[j]] = [s[j], s[i]];
|
||||
j--;
|
||||
i++;
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: '392. Is Subsequence'
|
||||
description: Given two strings s and t, return true if s is a subsequence of t, or false otherwise
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
video: "99RVfqklbCE"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/45"
|
||||
sidebar:
|
||||
label: 'Is Subsequence'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Suppose there are lots of incoming s, say s1, s2, ..., sk where k >= 10^9, and you want to check one by one to see if t has its subsequence. In this scenario, how would you change your code?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `s = "abc", t = "ahbgdc"`
|
||||
- Output: `true`
|
||||
|
||||
### Example 2:
|
||||
- Input: `s = "axc", t = "ahbgdc"`
|
||||
- Output: `false`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `0 <= s.length <= 100`
|
||||
- `0 <= t.length <= 10^4`
|
||||
- `s` and `t` consist only of lowercase English letters.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["isSubsequence(s, t)"]) --> G{"len(s) > len(t)?"}
|
||||
G -- yes --> X(["return False"])
|
||||
G -- no --> I["i = 0 into s, j = 0 into t"]
|
||||
I --> W{"i < len(s) and j < len(t)?"}
|
||||
W -- no --> E(["return i == len(s) — every char of s was matched"])
|
||||
W -- yes --> Q{"s[i] == t[j]?"}
|
||||
Q -- yes --> A["i += 1 — consume the match"]
|
||||
Q -- no --> B["j += 1 — always advance t"]
|
||||
A --> B
|
||||
B --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def isSubsequence(self, s: str, t: str) -> bool:
|
||||
if len(s) > len(t):
|
||||
return False
|
||||
|
||||
i, j = 0, 0
|
||||
while i < len(s) and j < len(t):
|
||||
if s[i] == t[j]:
|
||||
i += 1
|
||||
j += 1
|
||||
|
||||
return i == len(s)
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: '42. Trapping Rain Water'
|
||||
description: Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Stack", slug: "stack" }
|
||||
- { name: "Monotonic Stack", slug: "monotonic-stack" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/211"
|
||||
sidebar:
|
||||
label: 'Trapping Rain Water'
|
||||
badge: 'Hard'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `height = [0,1,0,2,1,0,1,3,2,1,2,1]`
|
||||
- Output: `6`
|
||||
- Explanation: The above elevation map (black section) is represented by array `[0,1,0,2,1,0,1,3,2,1,2,1]`. In this case, `6` units of rain water (blue section) are being trapped.
|
||||
|
||||
### Example 2:
|
||||
- Input: `height = [4,2,0,3,2,5]`
|
||||
- Output: `9`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == height.length`
|
||||
- `1 <= n <= 2 * 10^4`
|
||||
- `0 <= height[i] <= 10^5`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def trap(self, height: list[int]) -> int:
|
||||
left, right = 0, len(height) - 1
|
||||
leftMax, rightMax = 0, 0
|
||||
total = 0
|
||||
|
||||
while left < right:
|
||||
leftMax = max(leftMax, height[left])
|
||||
rightMax = max(rightMax, height[right])
|
||||
|
||||
if height[left] < height[right]:
|
||||
total += leftMax - height[left]
|
||||
left += 1
|
||||
else:
|
||||
total += rightMax - height[right]
|
||||
right -= 1
|
||||
|
||||
return total
|
||||
```
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: '977. Squares of a Sorted Array'
|
||||
description: Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
video: "FPCZsG_AkUg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/46"
|
||||
sidebar:
|
||||
label: 'Squares of a Sorted Array'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Squaring each element and sorting the new array is very trivial, could you find an O(n) solution using a different approach?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [-4,-1,0,3,10]`
|
||||
- Output: `[0,1,9,16,100]`
|
||||
- Explanation: After squaring, the array becomes `[16,1,0,9,100]`. After sorting, it becomes `[0,1,9,16,100]`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [-7,-3,2,3,11]`
|
||||
- Output: `[4,9,9,49,121]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^4`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
- `nums` is sorted in non-decreasing order.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["sortedSquares(nums)"]) --> I["ans = Array(n), left = 0, right = n - 1"]
|
||||
I --> L{"more i from n-1 down to 0?"}
|
||||
L -- no --> E(["return ans"])
|
||||
L -- yes --> C{"abs(nums[left]) < abs(nums[right])?"}
|
||||
C -- yes --> R["square = nums[right]; right--"]
|
||||
C -- no --> F["square = nums[left]; left++"]
|
||||
R --> W["ans[i] = square * square — fill from the back, largest first"]
|
||||
F --> W
|
||||
W --> L
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @return {number[]}
|
||||
*/
|
||||
var sortedSquares = function(nums) {
|
||||
let n = nums.length;
|
||||
let ans = new Array(nums.length);
|
||||
let left = 0, right = nums.length - 1;
|
||||
|
||||
for (let i = n -1; i >= 0; i--){
|
||||
let square;
|
||||
if(Math.abs(nums[left]) < Math.abs(nums[right])){
|
||||
square = nums[right];
|
||||
right--;
|
||||
}else{
|
||||
square = nums[left];
|
||||
left++;
|
||||
}
|
||||
ans[i] = square*square;
|
||||
}
|
||||
return ans;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Two Pointers
|
||||
description: 'Two indices move through the data from different positions and meet at the answer.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: '146. LRU Cache'
|
||||
description: Design a data structure that follows the constraints of a Least Recently Used (LRU) cache
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Design", slug: "design" }
|
||||
- { name: "Doubly-Linked List", slug: "doubly-linked-list" }
|
||||
video: "7ABFKPK2hD4"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/87"
|
||||
sidebar:
|
||||
label: 'LRU Cache'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: ``
|
||||
- Output: ``
|
||||
- Explanation: Input ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] `[[2]`, [1, 1], [2, 2], `[1]`, [3, 3], `[2]`, [4, 4], `[1]`, `[3]`, [4]] Output [null, null, null, `1`, null, `-1`, null, `-1`, `3`, 4] Explanation LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, `1`); // cache is {1=1} lRUCache.put(2, `2`); // cache is {1=1, 2=2} lRUCache.get(1); // return `1` lRUCache.put(3, `3`); // LRU `key` was `2`, evicts `key 2`, cache is {1=1, 3=3} lRUCache.get(2); // returns `-1` (not found) lRUCache.put(4, `4`); // LRU `key` was `1`, evicts `key 1`, cache is {4=4, 3=3} lRUCache.get(1); // return `-1` (not found) lRUCache.get(3); // return `3` lRUCache.get(4); // return `4`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= capacity <= 3000`
|
||||
- `0 <= key <= 10^4`
|
||||
- `0 <= value <= 10^5`
|
||||
- At most 2 * 10^5 calls will be made to get and put.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph state["state — dict for O(1) lookup, doubly linked list for O(1) reorder"]
|
||||
H["head — least recent"] --- N["... nodes ..."] --- T["tail — most recent"]
|
||||
end
|
||||
subgraph get["get(key)"]
|
||||
G1{"key in cache?"} -- no --> G2(["return -1"])
|
||||
G1 -- yes --> G3["_remove_node then _add_node — move it beside the tail"]
|
||||
G3 --> G4(["return node.value"])
|
||||
end
|
||||
subgraph put["put(key, value)"]
|
||||
P1{"key in cache?"} -- yes --> P2["update value, _remove_node then _add_node"]
|
||||
P1 -- no --> P3["new Node, cache[key] = node, _add_node"]
|
||||
P3 --> P4{"len(cache) > capacity?"}
|
||||
P4 -- yes --> P5["evict head.next — the least recent — and del cache[lru.key]"]
|
||||
P4 -- no --> P6(["done"])
|
||||
P2 --> P6
|
||||
P5 --> P6
|
||||
end
|
||||
state --> get
|
||||
state --> put
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Node:
|
||||
def __init__(self, key=0, value=0):
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.prev = None
|
||||
self.next = None
|
||||
|
||||
|
||||
class LRUCache:
|
||||
def __init__(self, capacity: int):
|
||||
self.capacity = capacity
|
||||
self.cache = {}
|
||||
self.head = Node()
|
||||
self.tail = Node()
|
||||
self.head.next = self.tail # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.tail.prev = self.head # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
||||
def _remove_node(self, node):
|
||||
prev_node = node.prev
|
||||
next_node = node.next
|
||||
prev_node.next = next_node
|
||||
next_node.prev = prev_node
|
||||
|
||||
def _add_node(self, node):
|
||||
# Insert just before the tail (most recent at end)
|
||||
node.next = self.tail
|
||||
node.prev = self.tail.prev
|
||||
self.tail.prev.next = node # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.tail.prev = node
|
||||
|
||||
def get(self, key: int) -> int:
|
||||
if key not in self.cache:
|
||||
return -1
|
||||
|
||||
node = self.cache[key]
|
||||
self._remove_node(node)
|
||||
self._add_node(node)
|
||||
return node.value
|
||||
|
||||
def put(self, key: int, value: int) -> None:
|
||||
if key in self.cache:
|
||||
node = self.cache[key]
|
||||
node.value = value
|
||||
self._remove_node(node)
|
||||
self._add_node(node)
|
||||
else:
|
||||
new_node = Node(key, value)
|
||||
self.cache[key] = new_node
|
||||
self._add_node(new_node)
|
||||
|
||||
if len(self.cache) > self.capacity:
|
||||
lru = self.head.next # Oldest node is next to the head
|
||||
self._remove_node(lru)
|
||||
del self.cache[lru.key] # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
||||
|
||||
# Your LRUCache object will be instantiated and called as such:
|
||||
# obj = LRUCache(capacity)
|
||||
# param_1 = obj.get(key)
|
||||
# obj.put(key,value)
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: '2. Add Two Numbers'
|
||||
description: You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
video: "wgFPrzTjm7s"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/88"
|
||||
sidebar:
|
||||
label: 'Add Two Numbers'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `l1 = [2,4,3], l2 = [5,6,4]`
|
||||
- Output: `[7,0,8]`
|
||||
- Explanation: `342 + 465 = 807`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `l1 = [0], l2 = [0]`
|
||||
- Output: `[0]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]`
|
||||
- Output: `[8,9,9,9,0,0,0,1]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in each linked list is in the range [1, 100].
|
||||
- `0 <= Node.val <= 9`
|
||||
- It is guaranteed that the list represents a number that does not have leading zeros.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["addTwoNumbers(l1, l2)"]) --> I["dummy = ListNode(), carry = 0, curr = dummy — dummy.next will be the head"]
|
||||
I --> W{"l1 or l2 or carry?"}
|
||||
W -- no --> E(["return dummy.next"])
|
||||
W -- yes --> A["s = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry — a missing digit counts as 0"]
|
||||
A --> D["carry, val = divmod(s, 10)"]
|
||||
D --> N["curr.next = ListNode(val), curr = curr.next"]
|
||||
N --> V["l1 = l1.next if l1 else None, l2 = l2.next if l2 else None"]
|
||||
V --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def addTwoNumbers(
|
||||
self, l1: Optional[ListNode], l2: Optional[ListNode]
|
||||
) -> Optional[ListNode]:
|
||||
|
||||
dummy = ListNode()
|
||||
carry = 0
|
||||
curr = dummy
|
||||
while l1 or l2 or carry:
|
||||
s = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry
|
||||
carry, val = divmod(s, 10)
|
||||
|
||||
curr.next = ListNode(val)
|
||||
curr = curr.next
|
||||
|
||||
l1 = l1.next if l1 else None
|
||||
l2 = l2.next if l2 else None
|
||||
|
||||
return dummy.next
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Hybrid Structures
|
||||
description: 'Two structures are combined, such as a hash map and a linked list, to get the best properties of each.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: '141. Linked List Cycle'
|
||||
description: Given head, the head of a linked list, determine if the linked list has a cycle in it
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Two Pointers", slug: "two-pointers" }
|
||||
- { name: "Floyd's Cycle Finding Algorithm", slug: "floyds-cycle-finding-algorithm" }
|
||||
video: "gBTe7lFR3vc"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/79"
|
||||
sidebar:
|
||||
label: 'Linked List Cycle'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
Can you solve it using O(1) (i.e. constant) memory?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `head = [3,2,0,-4], pos = 1`
|
||||
- Output: `true`
|
||||
- Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).
|
||||
|
||||
### Example 2:
|
||||
- Input: `head = [1,2], pos = 0`
|
||||
- Output: `true`
|
||||
- Explanation: There is a cycle in the linked list, where the tail connects to the 0th node.
|
||||
|
||||
### Example 3:
|
||||
- Input: `head = [1], pos = -1`
|
||||
- Output: `false`
|
||||
- Explanation: There is no cycle in the linked list.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of the nodes in the list is in the range [0, 10^4].
|
||||
- `-10^5 <= Node.val <= 10^5`
|
||||
- pos is -1 or a valid index in the linked-list.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["hasCycle(head)"]) --> I["fast = slow = head"]
|
||||
I --> W{"fast and fast.next?"}
|
||||
W -- no --> E(["return False — ran off the end, no cycle"])
|
||||
W -- yes --> A["fast = fast.next.next — two steps"]
|
||||
A --> B["slow = slow.next — one step"]
|
||||
B --> Q{"fast is slow?"}
|
||||
Q -- yes --> R(["return True — the gap closed, so it loops"])
|
||||
Q -- no --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, x):
|
||||
# self.val = x
|
||||
# self.next = None
|
||||
|
||||
|
||||
class Solution:
|
||||
def hasCycle(self, head: Optional[ListNode]) -> bool:
|
||||
fast = slow = head
|
||||
|
||||
while fast and fast.next:
|
||||
fast = fast.next.next
|
||||
slow = slow.next
|
||||
if fast is slow:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: '206. Reverse Linked List'
|
||||
description: Given the head of a singly linked list, reverse the list, and return the reversed list
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
video: "G0_I-ZF0S38"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/77"
|
||||
sidebar:
|
||||
label: 'Reverse Linked List'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
::::warning
|
||||
A linked list can be reversed either iteratively or recursively. Could you implement both?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `head = [1,2,3,4,5]`
|
||||
- Output: `[5,4,3,2,1]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `head = [1,2]`
|
||||
- Output: `[2,1]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `head = []`
|
||||
- Output: `[]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the list is the range [0, 5000].
|
||||
- `-5000 <= Node.val <= 5000`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["reverseList(head)"]) --> I["prev = None, curr = head"]
|
||||
I --> W{"curr?"}
|
||||
W -- no --> E(["return prev — the old tail is the new head"])
|
||||
W -- yes --> A["next_ = curr.next — save it before overwriting"]
|
||||
A --> B["curr.next = prev — flip the link"]
|
||||
B --> C["prev = curr; curr = next_ — step forward"]
|
||||
C --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
|
||||
prev = None
|
||||
curr = head
|
||||
while curr:
|
||||
next_ = curr.next
|
||||
curr.next = prev
|
||||
prev = curr
|
||||
curr = next_
|
||||
return prev
|
||||
```
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: '21. Merge Two Sorted Lists'
|
||||
description: You are given the heads of two sorted linked lists list1 and list2
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Recursion", slug: "recursion" }
|
||||
video: "XIdigk956u0"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/78"
|
||||
sidebar:
|
||||
label: 'Merge Two Sorted Lists'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `list1 = [1,2,4], list2 = [1,3,4]`
|
||||
- Output: `[1,1,2,3,4,4]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `list1 = [], list2 = []`
|
||||
- Output: `[]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `list1 = [], list2 = [0]`
|
||||
- Output: `[0]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in both lists is in the range [0, 50].
|
||||
- `-100 <= Node.val <= 100`
|
||||
- Both list1 and list2 are sorted in non-decreasing order.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["mergeTwoLists(list1, list2)"]) --> G1{"list1 empty?"}
|
||||
G1 -- yes --> X1(["return list2"])
|
||||
G1 -- no --> G2{"list2 empty?"}
|
||||
G2 -- yes --> X2(["return list1"])
|
||||
G2 -- no --> H["head = the smaller first node; advance that list"]
|
||||
H --> C["current = head"]
|
||||
C --> W{"list1 and list2 both non-empty?"}
|
||||
W -- yes --> P["current.next = the smaller node; advance that list; current = current.next"]
|
||||
P --> W
|
||||
W -- no --> T["current.next = list1 or list2 — append the leftover tail"]
|
||||
T --> E(["return head"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
class Solution:
|
||||
def mergeTwoLists(
|
||||
self, list1: Optional[ListNode], list2: Optional[ListNode]
|
||||
) -> Optional[ListNode]:
|
||||
|
||||
if not list1:
|
||||
return list2
|
||||
if not list2:
|
||||
return list1
|
||||
|
||||
if list1.val < list2.val:
|
||||
head = list1
|
||||
list1 = list1.next
|
||||
else:
|
||||
head = list2
|
||||
list2 = list2.next
|
||||
|
||||
current = head
|
||||
while list1 and list2:
|
||||
if list1.val < list2.val:
|
||||
current.next = list1
|
||||
list1 = list1.next
|
||||
else:
|
||||
current.next = list2
|
||||
list2 = list2.next
|
||||
current = current.next
|
||||
|
||||
current.next = list1 or list2
|
||||
return head
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Linked List
|
||||
description: 'Each node points to the next node, and the pointers give the path through the data.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: '48. Rotate Image'
|
||||
description: You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise)
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
video: "fMSJSS7eO1w"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/91"
|
||||
sidebar:
|
||||
label: 'Rotate Image'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `matrix = [[1,2,3],[4,5,6],[7,8,9]]`
|
||||
- Output: `[[7,4,1],[8,5,2],[9,6,3]]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]`
|
||||
- Output: `[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == matrix.length == matrix[i].length`
|
||||
- `1 <= n <= 20`
|
||||
- `-1000 <= matrix[i][j] <= 1000`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["rotate(matrix)"]) --> N["n = len(matrix)"]
|
||||
N --> F{"more r in range(n)?"}
|
||||
F -- yes --> G{"more c in range(r + 1, n)?"}
|
||||
G -- yes --> T["swap matrix[r][c] and matrix[c][r] — transpose across the diagonal"]
|
||||
T --> G
|
||||
G -- no --> F
|
||||
F -- no --> H{"more r in range(n)?"}
|
||||
H -- yes --> R["matrix[r].reverse() — transpose then mirror is a clockwise turn"]
|
||||
R --> H
|
||||
H -- no --> E(["return None — rotated in place"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def rotate(self, matrix: List[List[int]]) -> None:
|
||||
n = len(matrix)
|
||||
for r in range(n):
|
||||
for c in range(r + 1, n):
|
||||
matrix[r][c], matrix[c][r] = matrix[c][r], matrix[r][c]
|
||||
|
||||
for r in range(n):
|
||||
matrix[r].reverse()
|
||||
```
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: '54. Spiral Matrix'
|
||||
description: Given an m x n matrix, return all elements of the matrix in spiral order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
- { name: "Simulation", slug: "simulation" }
|
||||
video: "BJnMZNwUk1M"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/92"
|
||||
sidebar:
|
||||
label: 'Spiral Matrix'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `matrix = [[1,2,3],[4,5,6],[7,8,9]]`
|
||||
- Output: `[1,2,3,6,9,8,7,4,5]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]`
|
||||
- Output: `[1,2,3,4,8,12,11,10,9,5,6,7]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == matrix.length`
|
||||
- `n == matrix[i].length`
|
||||
- `1 <= m, n <= 10`
|
||||
- `-100 <= matrix[i][j] <= 100`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["spiralOrder(matrix)"]) --> I["res = [], top = 0, bottom = len(matrix) - 1, left = 0, right = len(matrix[0]) - 1"]
|
||||
I --> W{"top <= bottom and left <= right?"}
|
||||
W -- no --> E(["return res"])
|
||||
W -- yes --> T["append matrix[top][c] for c in left..right, then top += 1 — top row, shrink from above"]
|
||||
T --> R["append matrix[r][right] for r in top..bottom, then right -= 1 — right column"]
|
||||
R --> B{"top <= bottom?"}
|
||||
B -- yes --> C["append matrix[bottom][c] for c in right down to left, then bottom -= 1 — bottom row"]
|
||||
C --> L{"left <= right?"}
|
||||
B -- no --> L
|
||||
L -- yes --> D["append matrix[r][left] for r in bottom down to top, then left += 1 — left column"]
|
||||
D --> W
|
||||
L -- no --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
|
||||
res = []
|
||||
top, bottom = 0, len(matrix) - 1
|
||||
left, right = 0, len(matrix[0]) - 1
|
||||
|
||||
while top <= bottom and left <= right:
|
||||
# 1. Top Row
|
||||
for c in range(left, right + 1):
|
||||
res.append(matrix[top][c])
|
||||
top += 1
|
||||
|
||||
# 2. Right Column
|
||||
for r in range(top, bottom + 1):
|
||||
res.append(matrix[r][right])
|
||||
right -= 1
|
||||
|
||||
# 3. Bottom Row
|
||||
if top <= bottom:
|
||||
for c in range(right, left - 1, -1):
|
||||
res.append(matrix[bottom][c])
|
||||
bottom -= 1
|
||||
|
||||
# 4. Left Column
|
||||
if left <= right:
|
||||
for r in range(bottom, top - 1, -1):
|
||||
res.append(matrix[r][left])
|
||||
left += 1
|
||||
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: '73. Set Matrix Zeroes'
|
||||
description: Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0's
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
video: "T41rL0L3Pnw"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/93"
|
||||
sidebar:
|
||||
label: 'Set Matrix Zeroes'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
You must do it in place.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `matrix = [[1,1,1],[1,0,1],[1,1,1]]`
|
||||
- Output: `[[1,0,1],[0,0,0],[1,0,1]]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]`
|
||||
- Output: `[[0,0,0,0],[0,4,5,0],[0,3,1,0]]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == matrix.length`
|
||||
- `n == matrix[0].length`
|
||||
- `1 <= m, n <= 200`
|
||||
- `-2^31 <= matrix[i][j] <= 2^31 - 1`
|
||||
- A straightforward solution using O(mn) space is probably a bad idea.
|
||||
- A simple improvement uses O(m + n) space, but still not the best solution.
|
||||
- Could you devise a constant space solution?
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["setZeroes(matrix)"]) --> I["m = len(matrix), n = len(matrix[0])"]
|
||||
I --> Z["row0_zero = any(matrix[0][c] == 0), col0_zero = any(matrix[r][0] == 0) — remember before they become markers"]
|
||||
Z --> F{"more r in range(1, m)?"}
|
||||
F -- yes --> G{"more c in range(1, n)?"}
|
||||
G -- yes --> Q{"matrix[r][c] == 0?"}
|
||||
Q -- yes --> M["matrix[r][0] = 0, matrix[0][c] = 0 — row 0 and column 0 are the marker lists"]
|
||||
M --> G
|
||||
Q -- no --> G
|
||||
G -- no --> F
|
||||
F -- no --> H{"more r in range(1, m)?"}
|
||||
H -- yes --> J{"more c in range(1, n)?"}
|
||||
J -- yes --> K{"matrix[r][0] == 0 or matrix[0][c] == 0?"}
|
||||
K -- yes --> W["matrix[r][c] = 0"]
|
||||
W --> J
|
||||
K -- no --> J
|
||||
J -- no --> H
|
||||
H -- no --> A{"row0_zero?"}
|
||||
A -- yes --> B["matrix[0][c] = 0 for c in range(n)"]
|
||||
B --> C{"col0_zero?"}
|
||||
A -- no --> C
|
||||
C -- yes --> D["matrix[r][0] = 0 for r in range(m)"]
|
||||
D --> E(["return None — zeroed in place"])
|
||||
C -- no --> E
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def setZeroes(self, matrix: List[List[int]]) -> None:
|
||||
"""
|
||||
Do not return anything, modify matrix in-place instead.
|
||||
"""
|
||||
m, n = len(matrix), len(matrix[0])
|
||||
|
||||
# 1. remember if row 0 or column 0 have their own zeros
|
||||
row0_zero = any(matrix[0][c] == 0 for c in range(n))
|
||||
col0_zero = any(matrix[r][0] == 0 for r in range(m))
|
||||
|
||||
# 2. use row 0 and column 0 as marker lists
|
||||
for r in range(1, m):
|
||||
for c in range(1, n):
|
||||
if matrix[r][c] == 0:
|
||||
matrix[r][0] = 0
|
||||
matrix[0][c] = 0
|
||||
|
||||
# 3. zero the inner cells from the markers
|
||||
for r in range(1, m):
|
||||
for c in range(1, n):
|
||||
if matrix[r][0] == 0 or matrix[0][c] == 0:
|
||||
matrix[r][c] = 0
|
||||
|
||||
# 4. handle row 0 and column 0 last
|
||||
if row0_zero:
|
||||
for c in range(n):
|
||||
matrix[0][c] = 0
|
||||
if col0_zero:
|
||||
for r in range(m):
|
||||
matrix[r][0] = 0
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Matrix Index Math
|
||||
description: 'A two-dimensional grid is read as a flat array through a formula, and the neighbors are found by arithmetic.'
|
||||
hidden: true
|
||||
---
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: '230. Kth Smallest Element in a BST'
|
||||
description: Given the root of a binary search tree, and an integer k, return the k^th smallest value (1-indexed) of all the values of the nodes in the tree
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Binary Search Tree", slug: "binary-search-tree" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "5LUXSvjmGCw"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/112"
|
||||
sidebar:
|
||||
label: 'Kth Smallest Element in a BST'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
If the BST is modified often (i.e., we can do insert and delete operations) and you need to find the kth smallest frequently, how would you optimize?
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [3,1,4,null,2], k = 1`
|
||||
- Output: `1`
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [5,3,6,2,4,null,null,1], k = 3`
|
||||
- Output: `3`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is n.
|
||||
- `1 <= k <= n <= 10^4`
|
||||
- `0 <= Node.val <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["kthSmallest(root, k)"]) --> I["n = 0, stack = [], cur = root"]
|
||||
I --> W{"cur or stack?"}
|
||||
W -- no --> E(["fall off the loop — unreachable, k <= n"])
|
||||
W -- yes --> L{"cur?"}
|
||||
L -- yes --> P["stack.append(cur), cur = cur.left — push the whole left spine"]
|
||||
P --> L
|
||||
L -- no --> O["cur = stack.pop(), n += 1 — nodes pop in ascending order"]
|
||||
O --> K{"n == k?"}
|
||||
K -- yes --> R(["return cur.val"])
|
||||
K -- no --> N["cur = cur.right"]
|
||||
N --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
|
||||
n = 0
|
||||
stack = []
|
||||
cur = root
|
||||
|
||||
while cur or stack:
|
||||
while cur:
|
||||
stack.append(cur)
|
||||
cur = cur.left
|
||||
|
||||
cur = stack.pop()
|
||||
n += 1
|
||||
if n == k:
|
||||
return cur.val
|
||||
cur = cur.right
|
||||
```
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: '235. Lowest Common Ancestor of a Binary Search Tree'
|
||||
description: Given a binary search tree (BST), find the lowest common ancestor (LCA) node of two given nodes in the BST
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Binary Search Tree", slug: "binary-search-tree" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
- { name: "Binary Lifting", slug: "binary-lifting" }
|
||||
- { name: "Lowest Common Ancestor", slug: "lowest-common-ancestor" }
|
||||
video: "gs2LMfuOR9k"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/113"
|
||||
sidebar:
|
||||
label: 'Lowest Common Ancestor of a Binary Search Tree'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8`
|
||||
- Output: `6`
|
||||
- Explanation: The LCA of nodes `2` and `8` is `6`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4`
|
||||
- Output: `2`
|
||||
- Explanation: The LCA of nodes `2` and `4` is `2`, since a node can be a descendant of itself according to the LCA definition.
|
||||
|
||||
### Example 3:
|
||||
- Input: `root = [2,1], p = 2, q = 1`
|
||||
- Output: `2`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is in the range [2, 10^5].
|
||||
- `-10^9 <= Node.val <= 10^9`
|
||||
- All Node.val are unique.
|
||||
- `p != q`
|
||||
- p and q will exist in the BST.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["lowestCommonAncestor(root, p, q)"]) --> I["cur = root"]
|
||||
I --> W{"cur?"}
|
||||
W -- no --> E(["fall off the loop — unreachable, p and q exist in the BST"])
|
||||
W -- yes --> A{"p.val < cur.val and q.val < cur.val?"}
|
||||
A -- yes --> L["cur = cur.left — both sit in the left subtree"]
|
||||
L --> W
|
||||
A -- no --> B{"p.val > cur.val and q.val > cur.val?"}
|
||||
B -- yes --> R["cur = cur.right — both sit in the right subtree"]
|
||||
R --> W
|
||||
B -- no --> C(["return cur — the paths to p and q split here"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, x):
|
||||
# self.val = x
|
||||
# self.left = None
|
||||
# self.right = None
|
||||
|
||||
|
||||
class Solution:
|
||||
def lowestCommonAncestor(
|
||||
self, root: "TreeNode", p: "TreeNode", q: "TreeNode"
|
||||
) -> "TreeNode":
|
||||
cur = root
|
||||
while cur:
|
||||
if p.val < cur.val and q.val < cur.val:
|
||||
cur = cur.left
|
||||
elif p.val > cur.val and q.val > cur.val:
|
||||
cur = cur.right
|
||||
else:
|
||||
return cur
|
||||
```
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: '98. Validate Binary Search Tree'
|
||||
description: Given the root of a binary tree, determine if it is a valid binary search tree (BST)
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Binary Search Tree", slug: "binary-search-tree" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "s6ATEkipzow"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/111"
|
||||
sidebar:
|
||||
label: 'Validate Binary Search Tree'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [2,1,3]`
|
||||
- Output: `true`
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [5,1,4,null,null,3,6]`
|
||||
- Output: `false`
|
||||
- Explanation: The `root` node's value is `5` but its right child's value is `4`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is in the range [1, 10^4].
|
||||
- `-2^31 <= Node.val <= 2^31 - 1`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["isValidBST(root)"]) --> C["valid(root, float('-inf'), float('inf')) — the root may hold any value"]
|
||||
C --> E(["return that result"])
|
||||
subgraph valid["valid(node, low, high)"]
|
||||
V1{"not node?"}
|
||||
V1 -- yes --> V2(["return True"])
|
||||
V1 -- no --> V3{"not (low < node.val < high)?"}
|
||||
V3 -- yes --> V4(["return False — node breaks a bound set by an ancestor"])
|
||||
V3 -- no --> V5["valid(node.left, low, node.val) and valid(node.right, node.val, high) — each child tightens one bound"]
|
||||
V5 --> V6(["return that result"])
|
||||
end
|
||||
C --> V1
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def isValidBST(self, root: Optional[TreeNode]) -> bool:
|
||||
def valid(node, low, high):
|
||||
if not node:
|
||||
return True
|
||||
|
||||
if not (low < node.val < high):
|
||||
return False
|
||||
|
||||
return valid(node.left, low, node.val) and valid(node.right, node.val, high)
|
||||
|
||||
return valid(root, float("-inf"), float("inf"))
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Binary Search Tree
|
||||
description: 'Each node holds a value that is larger than its left subtree and smaller than its right subtree.'
|
||||
hidden: true
|
||||
---
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: '215. Kth Largest Element in an Array'
|
||||
description: Given an integer array nums and an integer k, return the k^th largest element in the array
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Divide and Conquer", slug: "divide-and-conquer" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
- { name: "Heap (Priority Queue)", slug: "heap-priority-queue" }
|
||||
- { name: "Quickselect", slug: "quickselect" }
|
||||
video: "XEmy13g1Qxc"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/118"
|
||||
sidebar:
|
||||
label: 'Kth Largest Element in an Array'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [3,2,1,5,6,4], k = 2`
|
||||
- Output: `5`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [3,2,3,1,2,4,5,5,6], k = 4`
|
||||
- Output: `4`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= k <= nums.length <= 10^5`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["findKthLargest(nums, k)"]) --> F{"more i in range(len(nums))?"}
|
||||
F -- yes --> N["nums[i] = -nums[i] — negate so the min-heap acts as a max-heap"]
|
||||
N --> F
|
||||
F -- no --> H["heapq.heapify(nums)"]
|
||||
H --> W{"more _ in range(k - 1)?"}
|
||||
W -- yes --> P["heapq.heappop(nums) — discard the k-1 largest"]
|
||||
P --> W
|
||||
W -- no --> E(["return -heapq.heappop(nums)"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
import heapq
|
||||
|
||||
|
||||
class Solution:
|
||||
def findKthLargest(self, nums: List[int], k: int) -> int:
|
||||
for i in range(len(nums)):
|
||||
nums[i] = -nums[i]
|
||||
|
||||
heapq.heapify(nums)
|
||||
|
||||
for _ in range(k - 1):
|
||||
heapq.heappop(nums)
|
||||
|
||||
return -heapq.heappop(nums)
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: '23. Merge k Sorted Lists'
|
||||
description: You are given an array of k linked-lists lists, each linked-list is sorted in ascending order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Linked List", slug: "linked-list" }
|
||||
- { name: "Divide and Conquer", slug: "divide-and-conquer" }
|
||||
- { name: "Heap (Priority Queue)", slug: "heap-priority-queue" }
|
||||
- { name: "Merge Sort", slug: "merge-sort" }
|
||||
- { name: "Tournament Sort", slug: "tournament-sort" }
|
||||
video: "q5a5OiGbT6Q"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/120"
|
||||
sidebar:
|
||||
label: 'Merge k Sorted Lists'
|
||||
badge: 'Hard'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `lists = [[1,4,5],[1,3,4],[2,6]]`
|
||||
- Output: `[1,1,2,3,4,4,5,6]`
|
||||
- Explanation: The linked-lists are: [ 1->4->5, 1->3->4, 2->6 ] merging them into one sorted linked list: 1->1->2->3->4->4->5->6
|
||||
|
||||
### Example 2:
|
||||
- Input: `lists = []`
|
||||
- Output: `[]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `lists = [[]]`
|
||||
- Output: `[]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `k == lists.length`
|
||||
- `0 <= k <= 10^4`
|
||||
- `0 <= lists[i].length <= 500`
|
||||
- `-10^4 <= lists[i][j] <= 10^4`
|
||||
- `lists[i]` is sorted in ascending order.
|
||||
- The sum of lists[i].length will not exceed 10^4.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["mergeKLists(lists)"]) --> I["heap = []"]
|
||||
I --> F{"more i, node in enumerate(lists)?"}
|
||||
F -- yes --> C{"node?"}
|
||||
C -- yes --> P["heapq.heappush(heap, (node.val, i, node)) — i breaks ties so nodes are never compared"]
|
||||
P --> F
|
||||
C -- no --> F
|
||||
F -- no --> D["D = ListNode(), cur = D"]
|
||||
D --> W{"heap?"}
|
||||
W -- no --> E(["return D.next"])
|
||||
W -- yes --> O["val, i, node = heapq.heappop(heap) — the smallest head across all lists"]
|
||||
O --> L["cur.next = node, cur = node, node = node.next"]
|
||||
L --> N{"node?"}
|
||||
N -- yes --> Q["heapq.heappush(heap, (node.val, i, node))"]
|
||||
Q --> W
|
||||
N -- no --> W
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode:
|
||||
# def __init__(self, val=0, next=None):
|
||||
# self.val = val
|
||||
# self.next = next
|
||||
import heapq
|
||||
|
||||
|
||||
class Solution:
|
||||
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
|
||||
heap = []
|
||||
|
||||
# K log K
|
||||
for i, node in enumerate(lists):
|
||||
if node:
|
||||
heapq.heappush(heap, (node.val, i, node))
|
||||
|
||||
D = ListNode()
|
||||
cur = D
|
||||
|
||||
# n log k
|
||||
while heap:
|
||||
val, i, node = heapq.heappop(heap)
|
||||
cur.next = node
|
||||
cur = node
|
||||
node = node.next
|
||||
|
||||
if node:
|
||||
heapq.heappush(heap, (node.val, i, node))
|
||||
|
||||
# Time: O(N log k)
|
||||
# Space: O(n)
|
||||
return D.next
|
||||
```
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: '973. K Closest Points to Origin'
|
||||
description: Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0)
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Divide and Conquer", slug: "divide-and-conquer" }
|
||||
- { name: "Geometry", slug: "geometry" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
- { name: "Heap (Priority Queue)", slug: "heap-priority-queue" }
|
||||
- { name: "Quickselect", slug: "quickselect" }
|
||||
- { name: "K-D Tree", slug: "k-d-tree" }
|
||||
video: "rI2EBUEMfTk"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/119"
|
||||
sidebar:
|
||||
label: 'K Closest Points to Origin'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `points = [[1,3],[-2,2]], k = 1`
|
||||
- Output: `[[-2,2]]`
|
||||
- Explanation: The distance between `(1`, `3`) and the origin is sqrt(10). The distance between `(-2`, `2`) and the origin is sqrt(8). Since sqrt(8) < sqrt(10), `(-2`, `2`) is closer to the origin. We only want the closest `k = 1 points` from the origin, so the answer is just [[-2,2]].
|
||||
|
||||
### Example 2:
|
||||
- Input: `points = [[3,3],[5,-1],[-2,4]], k = 2`
|
||||
- Output: `[[3,3],[-2,4]]`
|
||||
- Explanation: The answer [[-2,4],[3,3]] would also be accepted.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= k <= points.length <= 10^4`
|
||||
- `-10^4 <= xi, yi <= 10^4`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["kClosest(points, k)"]) --> I["minHeap = []"]
|
||||
I --> F{"more x, y in points?"}
|
||||
F -- yes --> D["dist = x**2 + y**2 — squared distance orders the same as sqrt"]
|
||||
D --> A["minHeap.append([dist, x, y])"]
|
||||
A --> F
|
||||
F -- no --> H["heapq.heapify(minHeap), res = [] — O(n) build, smallest dist on top"]
|
||||
H --> W{"k > 0?"}
|
||||
W -- yes --> P["dist, x, y = heapq.heappop(minHeap)"]
|
||||
P --> R["res.append([x, y]), k -= 1"]
|
||||
R --> W
|
||||
W -- no --> E(["return res"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
import heapq
|
||||
|
||||
|
||||
class Solution:
|
||||
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
|
||||
minHeap = []
|
||||
for x, y in points:
|
||||
dist = (x**2) + (y**2)
|
||||
minHeap.append([dist, x, y])
|
||||
|
||||
heapq.heapify(minHeap)
|
||||
res = []
|
||||
|
||||
while k > 0:
|
||||
dist, x, y = heapq.heappop(minHeap)
|
||||
res.append([x, y])
|
||||
k -= 1
|
||||
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Heap / Priority Queue
|
||||
description: 'The largest or smallest item is always at the top, and it is removed in logarithmic time.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: '102. Binary Tree Level Order Traversal'
|
||||
description: Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level)
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "6ZnyEApgFYg"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/106"
|
||||
sidebar:
|
||||
label: 'Binary Tree Level Order Traversal'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [3,9,20,null,null,15,7]`
|
||||
- Output: `[[3],[9,20],[15,7]]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [1]`
|
||||
- Output: `[[1]]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `root = []`
|
||||
- Output: `[]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is in the range [0, 2000].
|
||||
- `-1000 <= Node.val <= 1000`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["levelOrder(root)"]) --> I["res = [], q = deque(), q.append(root)"]
|
||||
I --> W{"q non-empty?"}
|
||||
W -- no --> E(["return res"])
|
||||
W -- yes --> L["qLen = len(q), level = [] — qLen freezes the current level's size"]
|
||||
L --> F{"more i in range(qLen)?"}
|
||||
F -- no --> V{"level non-empty?"}
|
||||
V -- yes --> A["res.append(level)"]
|
||||
A --> W
|
||||
V -- no --> W
|
||||
F -- yes --> P["node = q.popleft()"]
|
||||
P --> N{"node is not None?"}
|
||||
N -- no --> F
|
||||
N -- yes --> C["level.append(node.val), q.append(node.left), q.append(node.right) — None children are filtered on the next level"]
|
||||
C --> F
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
from collections import deque
|
||||
|
||||
|
||||
class Solution:
|
||||
# T: O(n) S: O(n)
|
||||
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
|
||||
res = []
|
||||
|
||||
q = deque()
|
||||
q.append(root)
|
||||
|
||||
while q:
|
||||
qLen = len(q)
|
||||
level = []
|
||||
for i in range(qLen):
|
||||
node = q.popleft()
|
||||
if node:
|
||||
level.append(node.val)
|
||||
q.append(node.left)
|
||||
q.append(node.right)
|
||||
if level:
|
||||
res.append(level)
|
||||
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: '1448. Count Good Nodes in Binary Tree'
|
||||
description: Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "7cp5imvDzl4"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/108"
|
||||
sidebar:
|
||||
label: 'Count Good Nodes in Binary Tree'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [3,1,4,3,null,1,5]`
|
||||
- Output: `4`
|
||||
- Explanation: Nodes in blue are good. Root Node `(3`) is always a good node. Node `4` -> (3,4) is the maximum value in the path starting from the `root`. Node `5` -> (3,4,5) is the maximum value in the path Node `3` -> (3,1,3) is the maximum value in the path.
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [3,3,null,4,2]`
|
||||
- Output: `3`
|
||||
- Explanation: Node `2` -> `(3`, `3`, `2`) is not good, because `"3`" is higher than it.
|
||||
|
||||
### Example 3:
|
||||
- Input: `root = [1]`
|
||||
- Output: `1`
|
||||
- Explanation: Root is considered as good.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the binary tree is in the range [1, 10^5].
|
||||
- Each node's value is between [-10^4, 10^4].
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["goodNodes(root)"]) --> C["dfs(root, root.val) — the root is always good"]
|
||||
C --> E(["return that count"])
|
||||
subgraph dfs["dfs(node, max_val)"]
|
||||
D1{"node is None?"} -- yes --> D2(["return 0"])
|
||||
D1 -- no --> D3["is_good = 1 if node.val >= max_val else 0 — max_val is the largest value on the path so far"]
|
||||
D3 --> D4["new_max = max(max_val, node.val)"]
|
||||
D4 --> D5["l = dfs(node.left, new_max), r = dfs(node.right, new_max)"]
|
||||
D5 --> D6(["return is_good + l + r"])
|
||||
end
|
||||
C --> dfs
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def goodNodes(self, root: TreeNode) -> int:
|
||||
def dfs(node, max_val):
|
||||
if not node:
|
||||
return 0
|
||||
|
||||
is_good = 1 if node.val >= max_val else 0
|
||||
new_max = max(max_val, node.val)
|
||||
|
||||
return is_good + dfs(node.left, new_max) + dfs(node.right, new_max)
|
||||
|
||||
return dfs(root, root.val)
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: '199. Binary Tree Right Side View'
|
||||
description: Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "d4zLyf32e3I"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/107"
|
||||
sidebar:
|
||||
label: 'Binary Tree Right Side View'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [1,2,3,null,5,null,4]`
|
||||
- Output: `[1,3,4]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [1,2,3,4,null,null,null,5]`
|
||||
- Output: `[1,3,4,5]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `root = [1,null,3]`
|
||||
- Output: `[1,3]`
|
||||
|
||||
### Example 4:
|
||||
- Input: `root = []`
|
||||
- Output: `[]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is in the range [0, 100].
|
||||
- `-100 <= Node.val <= 100`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["rightSideView(root)"]) --> I["res = [], q = deque([root])"]
|
||||
I --> W{"q non-empty?"}
|
||||
W -- no --> E(["return res"])
|
||||
W -- yes --> L["rightSide = None, qLen = len(q) — qLen freezes the current level's size"]
|
||||
L --> F{"more i in range(qLen)?"}
|
||||
F -- no --> V{"rightSide is not None?"}
|
||||
V -- yes --> A["res.append(rightSide.val) — the last real node popped on this level is the rightmost"]
|
||||
A --> W
|
||||
V -- no --> W
|
||||
F -- yes --> P["node = q.popleft()"]
|
||||
P --> N{"node is not None?"}
|
||||
N -- no --> F
|
||||
N -- yes --> C["rightSide = node, q.append(node.left), q.append(node.right)"]
|
||||
C --> F
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
from collections import deque
|
||||
|
||||
|
||||
class Solution:
|
||||
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
|
||||
res = []
|
||||
q = deque([root])
|
||||
|
||||
while q:
|
||||
rightSide = None
|
||||
qLen = len(q)
|
||||
|
||||
for i in range(qLen):
|
||||
node = q.popleft()
|
||||
if node:
|
||||
rightSide = node
|
||||
q.append(node.left)
|
||||
q.append(node.right)
|
||||
|
||||
if rightSide:
|
||||
res.append(rightSide.val)
|
||||
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Tree BFS
|
||||
description: 'All nodes at one level are visited before the nodes at the next level.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: '104. Maximum Depth of Binary Tree'
|
||||
description: Given the root of a binary tree, return its maximum depth
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "hTM3phVI6YQ"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/97"
|
||||
sidebar:
|
||||
label: 'Maximum Depth of Binary Tree'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [3,9,20,null,null,15,7]`
|
||||
- Output: `3`
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [1,null,2]`
|
||||
- Output: `2`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is in the range [0, 10^4].
|
||||
- `-100 <= Node.val <= 100`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["maxDepth(root)"]) --> B{"root is None?"}
|
||||
B -- yes --> Z(["return 0"])
|
||||
B -- no --> L["l = maxDepth(root.left), r = maxDepth(root.right)"]
|
||||
L --> E(["return 1 + max(l, r) — this node adds one level above its deeper subtree"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def maxDepth(self, root: Optional[TreeNode]) -> int:
|
||||
if not root:
|
||||
return 0
|
||||
|
||||
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
|
||||
```
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: '226. Invert Binary Tree'
|
||||
description: Given the root of a binary tree, invert the tree, and return its root
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
video: "OnSn2XEQ4MY"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/96"
|
||||
sidebar:
|
||||
label: 'Invert Binary Tree'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [4,2,7,1,3,6,9]`
|
||||
- Output: `[4,7,2,9,6,3,1]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [2,1,3]`
|
||||
- Output: `[2,3,1]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `root = []`
|
||||
- Output: `[]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is in the range [0, 100].
|
||||
- `-100 <= Node.val <= 100`
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["invertTree(root)"]) --> B{"root is None?"}
|
||||
B -- yes --> Z(["return None"])
|
||||
B -- no --> L["left = invertTree(root.left), right = invertTree(root.right) — both subtrees are already inverted"]
|
||||
L --> X["root.left = right, root.right = left"]
|
||||
X --> E(["return root"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
|
||||
if not root:
|
||||
return None
|
||||
|
||||
left = self.invertTree(root.left)
|
||||
right = self.invertTree(root.right)
|
||||
|
||||
root.left = right
|
||||
root.right = left
|
||||
|
||||
return root
|
||||
```
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: '236. Lowest Common Ancestor of a Binary Tree'
|
||||
description: Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Tree", slug: "tree" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Binary Tree", slug: "binary-tree" }
|
||||
- { name: "Binary Lifting", slug: "binary-lifting" }
|
||||
- { name: "Lowest Common Ancestor", slug: "lowest-common-ancestor" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/98"
|
||||
sidebar:
|
||||
label: 'Lowest Common Ancestor of a Binary Tree'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1`
|
||||
- Output: `3`
|
||||
- Explanation: The LCA of nodes `5` and `1` is `3`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4`
|
||||
- Output: `5`
|
||||
- Explanation: The LCA of nodes `5` and `4` is `5`, since a node can be a descendant of itself according to the LCA definition.
|
||||
|
||||
### Example 3:
|
||||
- Input: `root = [1,2], p = 1, q = 2`
|
||||
- Output: `1`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the tree is in the range [2, 10^5].
|
||||
- `-10^9 <= Node.val <= 10^9`
|
||||
- All Node.val are unique.
|
||||
- `p != q`
|
||||
- p and q will exist in the tree.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
S(["lowestCommonAncestor(root, p, q)"]) --> B{"not root?"}
|
||||
B -- yes --> N(["return None"])
|
||||
B -- no --> M{"root == p or root == q?"}
|
||||
M -- yes --> R(["return root — a node is its own ancestor"])
|
||||
M -- no --> L["l = self.lowestCommonAncestor(root.left, p, q)"]
|
||||
L --> Q["r = self.lowestCommonAncestor(root.right, p, q)"]
|
||||
Q --> D{"l and r?"}
|
||||
D -- yes --> A(["return root — p and q were found on different sides"])
|
||||
D -- no --> E(["return l or r — pass up whichever side found something"])
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, x):
|
||||
# self.val = x
|
||||
# self.left = None
|
||||
# self.right = None
|
||||
|
||||
|
||||
class Solution:
|
||||
def lowestCommonAncestor(
|
||||
self, root: "TreeNode", p: "TreeNode", q: "TreeNode"
|
||||
) -> "TreeNode":
|
||||
|
||||
if not root:
|
||||
return None
|
||||
|
||||
if root == p or root == q:
|
||||
return root
|
||||
|
||||
l = self.lowestCommonAncestor(root.left, p, q)
|
||||
r = self.lowestCommonAncestor(root.right, p, q)
|
||||
|
||||
if l and r:
|
||||
return root
|
||||
else:
|
||||
return l or r
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Tree DFS
|
||||
description: 'Each branch is followed to its end before the next branch is examined.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: '208. Implement Trie (Prefix Tree)'
|
||||
description: A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Design", slug: "design" }
|
||||
- { name: "Trie", slug: "trie" }
|
||||
video: "oobqoCJlHA0"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/127"
|
||||
sidebar:
|
||||
label: 'Implement Trie (Prefix Tree)'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: ``
|
||||
- Output: ``
|
||||
- Explanation: Input ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] `[[]`, `["apple"]`, `["apple"]`, `["app"]`, `["app"]`, `["app"]`, ["app"]] Output [null, null, true, false, true, null, true] Explanation Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // return True trie.search("app"); // return False trie.startsWith("app"); // return True trie.insert("app"); trie.search("app"); // return True
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= word.length, prefix.length <= 2000`
|
||||
- `word` and `prefix` consist only of lowercase English letters.
|
||||
- At most 3 * 10^4 calls in total will be made to insert, search, and startsWith.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph state["state — root TrieNode; every node holds a children dict and an endOfWord flag"]
|
||||
R["root"] --- C["... children[c] ..."] --- L["endOfWord = True — a word ends here"]
|
||||
end
|
||||
subgraph insert["insert(word)"]
|
||||
I1["cur = root"] --> I2{"more c in word?"}
|
||||
I2 -- yes --> I3{"c not in cur.children?"}
|
||||
I3 -- yes --> I4["cur.children[c] = TrieNode()"]
|
||||
I4 --> I5["cur = cur.children[c]"]
|
||||
I3 -- no --> I5
|
||||
I5 --> I2
|
||||
I2 -- no --> I6["cur.endOfWord = True"]
|
||||
I6 --> I7(["done"])
|
||||
end
|
||||
subgraph search["search(word)"]
|
||||
S1["cur = root"] --> S2{"more c in word?"}
|
||||
S2 -- yes --> S3{"c not in cur.children?"}
|
||||
S3 -- yes --> S4(["return False"])
|
||||
S3 -- no --> S5["cur = cur.children[c]"]
|
||||
S5 --> S2
|
||||
S2 -- no --> S6(["return cur.endOfWord — the path exists, but is it a whole word?"])
|
||||
end
|
||||
subgraph startsWith["startsWith(prefix)"]
|
||||
P1["cur = root"] --> P2{"more c in prefix?"}
|
||||
P2 -- yes --> P3{"c not in cur.children?"}
|
||||
P3 -- yes --> P4(["return False"])
|
||||
P3 -- no --> P5["cur = cur.children[c]"]
|
||||
P5 --> P2
|
||||
P2 -- no --> P6(["return True — every prefix char had a path"])
|
||||
end
|
||||
state --> insert
|
||||
state --> search
|
||||
state --> startsWith
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class TrieNode:
|
||||
def __init__(self):
|
||||
self.children = {}
|
||||
self.endOfWord = False
|
||||
|
||||
|
||||
class Trie:
|
||||
def __init__(self):
|
||||
self.root = TrieNode()
|
||||
|
||||
def insert(self, word: str) -> None:
|
||||
cur = self.root
|
||||
|
||||
for c in word:
|
||||
if c not in cur.children:
|
||||
cur.children[c] = TrieNode()
|
||||
cur = cur.children[c]
|
||||
cur.endOfWord = True
|
||||
|
||||
def search(self, word: str) -> bool:
|
||||
cur = self.root
|
||||
|
||||
for c in word:
|
||||
if c not in cur.children:
|
||||
return False
|
||||
cur = cur.children[c]
|
||||
|
||||
return cur.endOfWord
|
||||
|
||||
def startsWith(self, prefix: str) -> bool:
|
||||
cur = self.root
|
||||
|
||||
for c in prefix:
|
||||
if c not in cur.children:
|
||||
return False
|
||||
cur = cur.children[c]
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# Your Trie object will be instantiated and called as such:
|
||||
# obj = Trie()
|
||||
# obj.insert(word)
|
||||
# param_2 = obj.search(word)
|
||||
# param_3 = obj.startsWith(prefix)
|
||||
```
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: '211. Design Add and Search Words Data Structure'
|
||||
description: Design a data structure that supports adding new words and finding if a string matches any previously added string
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Design", slug: "design" }
|
||||
- { name: "Trie", slug: "trie" }
|
||||
video: "BTf05gs_8iU"
|
||||
issue: "https://github.com/prdlk/leetcode/issues/128"
|
||||
sidebar:
|
||||
label: 'Design Add and Search Words Data Structure'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= word.length <= 25`
|
||||
- `word` in addWord consists of lowercase English letters.
|
||||
- `word` in search consist of '.' or lowercase English letters.
|
||||
- There will be at most 2 dots in `word` for search queries.
|
||||
- At most 10^4 calls will be made to addWord and search.
|
||||
|
||||
## Approach
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph state["state — root TrieNode; every node holds a children dict and an endOfWord flag"]
|
||||
R["root"] --- C["... children[c] ..."] --- L["endOfWord = True — a word ends here"]
|
||||
end
|
||||
subgraph addWord["addWord(word)"]
|
||||
A1["cur = root"] --> A2{"more c in word?"}
|
||||
A2 -- yes --> A3{"c not in cur.children?"}
|
||||
A3 -- yes --> A4["cur.children[c] = TrieNode()"]
|
||||
A4 --> A5["cur = cur.children[c]"]
|
||||
A3 -- no --> A5
|
||||
A5 --> A2
|
||||
A2 -- no --> A6["cur.endOfWord = True"]
|
||||
A6 --> A7(["done"])
|
||||
end
|
||||
subgraph search["search(word)"]
|
||||
S1["dfs(0, root) — match from index 0 at the root"] --> S2["cur = root"]
|
||||
S2 --> S3{"more i in range(j, len(word))?"}
|
||||
S3 -- no --> S4(["return cur.endOfWord"])
|
||||
S3 -- yes --> S5["c = word[i]"]
|
||||
S5 --> S6{"c == '.'?"}
|
||||
S6 -- yes --> S7{"more child in cur.children.values()?"}
|
||||
S7 -- yes --> S8{"dfs(i + 1, child)? — try the rest of the word from this child"}
|
||||
S8 -- yes --> S9(["return True"])
|
||||
S8 -- no --> S7
|
||||
S7 -- no --> S10(["return False — no child matched the wildcard"])
|
||||
S6 -- no --> S11{"c not in cur.children?"}
|
||||
S11 -- yes --> S12(["return False"])
|
||||
S11 -- no --> S13["cur = cur.children[c]"]
|
||||
S13 --> S3
|
||||
end
|
||||
state --> addWord
|
||||
state --> search
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class TrieNode:
|
||||
def __init__(self):
|
||||
self.children = {}
|
||||
self.endOfWord = False
|
||||
|
||||
|
||||
class WordDictionary:
|
||||
def __init__(self):
|
||||
self.root = TrieNode()
|
||||
|
||||
def addWord(self, word: str) -> None:
|
||||
cur = self.root
|
||||
for c in word:
|
||||
if c not in cur.children:
|
||||
cur.children[c] = TrieNode()
|
||||
cur = cur.children[c]
|
||||
cur.endOfWord = True
|
||||
|
||||
# Leverage Recursion
|
||||
def search(self, word: str) -> bool:
|
||||
def dfs(j, root):
|
||||
cur = root
|
||||
for i in range(j, len(word)):
|
||||
c = word[i]
|
||||
|
||||
if c == ".":
|
||||
for child in cur.children.values():
|
||||
if dfs(i + 1, child):
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
if c not in cur.children:
|
||||
return False
|
||||
cur = cur.children[c]
|
||||
return cur.endOfWord
|
||||
|
||||
return dfs(0, self.root)
|
||||
|
||||
|
||||
# Your WordDictionary object will be instantiated and called as such:
|
||||
# obj = WordDictionary()
|
||||
# obj.addWord(word)
|
||||
# param_3 = obj.search(word)
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Trie
|
||||
description: 'Each node holds one character, and a path from the root spells a word.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: '127. Word Ladder'
|
||||
description: 'A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:'
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Bidirectional Search", slug: "bidirectional-search" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/138"
|
||||
sidebar:
|
||||
label: 'Word Ladder'
|
||||
badge: 'Hard'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]`
|
||||
- Output: `5`
|
||||
- Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is `5` words long.
|
||||
|
||||
### Example 2:
|
||||
- Input: `beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]`
|
||||
- Output: `0`
|
||||
- Explanation: The `endWord` "cog" is not in `wordList`, therefore there is no valid transformation sequence.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= beginWord.length <= 10`
|
||||
- `endWord.length == beginWord.length`
|
||||
- `1 <= wordList.length <= 5000`
|
||||
- `wordList[i].length == beginWord.length`
|
||||
- `beginWord`, `endWord`, and `wordList[i]` consist of lowercase English letters.
|
||||
- `beginWord != endWord`
|
||||
- All the words in `wordList` are unique.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
import collections
|
||||
|
||||
|
||||
class Solution:
|
||||
def ladderLength(self, beginWord: str, endWord: str, wordList: list[str]) -> int:
|
||||
if endWord not in wordList:
|
||||
return 0
|
||||
|
||||
adj = collections.defaultdict(list)
|
||||
wordList.append(beginWord)
|
||||
|
||||
for word in wordList:
|
||||
for j in range(len(word)):
|
||||
pattern = word[:j] + "*" + word[j + 1 :]
|
||||
adj[pattern].append(word)
|
||||
|
||||
visit = set([beginWord])
|
||||
q = collections.deque([beginWord])
|
||||
res = 1
|
||||
|
||||
while q:
|
||||
for i in range(len(q)):
|
||||
word = q.popleft()
|
||||
if word == endWord:
|
||||
return res
|
||||
for j in range(len(word)):
|
||||
pattern = word[:j] + "*" + word[j + 1 :]
|
||||
for adjWord in adj[pattern]:
|
||||
if adjWord not in visit:
|
||||
visit.add(adjWord)
|
||||
q.append(adjWord)
|
||||
res += 1
|
||||
return 0
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: '994. Rotting Oranges'
|
||||
description: 'You are given an m x n grid where each cell can have one of three values:'
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/137"
|
||||
sidebar:
|
||||
label: 'Rotting Oranges'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `grid = [[2,1,1],[1,1,0],[0,1,1]]`
|
||||
- Output: `4`
|
||||
|
||||
### Example 2:
|
||||
- Input: `grid = [[2,1,1],[0,1,1],[1,0,1]]`
|
||||
- Output: `-1`
|
||||
- Explanation: The orange in the bottom left corner (row `2`, column `0`) is never rotten, because rotting only happens 4-directionally.
|
||||
|
||||
### Example 3:
|
||||
- Input: `grid = [[0,2]]`
|
||||
- Output: `0`
|
||||
- Explanation: Since there are already no fresh oranges at minute `0`, the answer is just `0`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == grid.length`
|
||||
- `n == grid[i].length`
|
||||
- `1 <= m, n <= 10`
|
||||
- grid[i][j] is 0, 1, or 2.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
from collections import deque
|
||||
|
||||
|
||||
class Solution:
|
||||
def orangesRotting(self, grid: list[list[int]]) -> int:
|
||||
q = deque()
|
||||
time, fresh = 0, 0
|
||||
|
||||
rows, cols = len(grid), len(grid[0])
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
if grid[r][c] == 1:
|
||||
fresh += 1
|
||||
if grid[r][c] == 2:
|
||||
q.append([r, c])
|
||||
|
||||
directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]
|
||||
|
||||
while q and fresh > 0:
|
||||
for i in range(len(q)):
|
||||
r, c = q.popleft()
|
||||
for dr, dc in directions:
|
||||
row, col = dr + r, dc + c
|
||||
# check if in bounds
|
||||
if (
|
||||
row < 0
|
||||
or col < 0
|
||||
or row == len(grid)
|
||||
or col == len(grid[0])
|
||||
or grid[row][col] != 1
|
||||
):
|
||||
continue
|
||||
grid[row][col] = 2
|
||||
q.append([row, col])
|
||||
fresh -= 1
|
||||
time += 1
|
||||
return time if fresh == 0 else -1
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Graph BFS
|
||||
description: 'Nodes are visited in order of distance from the start, which gives the shortest path in an unweighted graph.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: '133. Clone Graph'
|
||||
description: Given a reference of a node in a connected undirected graph
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Hash Table", slug: "hash-table" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Graph Theory", slug: "graph" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/131"
|
||||
sidebar:
|
||||
label: 'Clone Graph'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `adjList = [[2,4],[1,3],[2,4],[1,3]]`
|
||||
- Output: `[[2,4],[1,3],[2,4],[1,3]]`
|
||||
- Explanation: There are `4` nodes in the graph. 1st `node` (val = 1)'s neighbors are 2nd `node` (val = `2`) and 4th `node` (val = `4`). 2nd `node` (val = 2)'s neighbors are 1st `node` (val = `1`) and 3rd `node` (val = `3`). 3rd `node` (val = 3)'s neighbors are 2nd `node` (val = `2`) and 4th `node` (val = `4`). 4th `node` (val = 4)'s neighbors are 1st `node` (val = `1`) and 3rd `node` (val = `3`).
|
||||
|
||||
### Example 2:
|
||||
- Input: `adjList = [[]]`
|
||||
- Output: `[[]]`
|
||||
- Explanation: Note that the input contains one empty list. The graph consists of only one `node` with val = `1` and it does not have any neighbors.
|
||||
|
||||
### Example 3:
|
||||
- Input: `adjList = []`
|
||||
- Output: `[]`
|
||||
- Explanation: This an empty graph, it does not have any nodes.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- The number of nodes in the graph is in the range [0, 100].
|
||||
- `1 <= Node.val <= 100`
|
||||
- Node.val is unique for each node.
|
||||
- There are no repeated edges and no self-loops in the graph.
|
||||
- The Graph is connected and all nodes can be visited starting from the given node.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
"""
|
||||
# Definition for a Node.
|
||||
class Node:
|
||||
def __init__(self, val = 0, neighbors = None):
|
||||
self.val = val
|
||||
self.neighbors = neighbors if neighbors is not None else []
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Solution:
|
||||
def cloneGraph(self, node: Optional["Node"]) -> Optional["Node"]:
|
||||
oldToNew = {}
|
||||
|
||||
def dfs(node):
|
||||
if node in oldToNew:
|
||||
return oldToNew[node]
|
||||
|
||||
copy = Node(node.val)
|
||||
oldToNew[node] = copy
|
||||
|
||||
for n in node.neighbors:
|
||||
copy.neighbors.append(dfs(n))
|
||||
return copy
|
||||
|
||||
return dfs(node) if node else None
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: '200. Number of Islands'
|
||||
description: Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Union-Find", slug: "union-find" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/130"
|
||||
sidebar:
|
||||
label: 'Number of Islands'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ]`
|
||||
- Output: `1`
|
||||
|
||||
### Example 2:
|
||||
- Input: `grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ]`
|
||||
- Output: `3`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == grid.length`
|
||||
- `n == grid[i].length`
|
||||
- `1 <= m, n <= 300`
|
||||
- grid[i][j] is '0' or '1'.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def numIslands(self, grid: List[List[str]]) -> int:
|
||||
rows, cols = len(grid), len(grid[0])
|
||||
|
||||
def dfs(r, c):
|
||||
if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != "1":
|
||||
return
|
||||
else:
|
||||
grid[r][c] = "0"
|
||||
dfs(r, c + 1) # right
|
||||
dfs(r + 1, c) # bottom
|
||||
dfs(r, c - 1) # left
|
||||
dfs(r - 1, c) # top
|
||||
|
||||
num_islands = 0
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
if grid[r][c] == "1":
|
||||
num_islands += 1
|
||||
dfs(r, c)
|
||||
|
||||
return num_islands
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: '417. Pacific Atlantic Water Flow'
|
||||
description: There is an m x n rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean touches the island's right and bottom edges
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/132"
|
||||
sidebar:
|
||||
label: 'Pacific Atlantic Water Flow'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]`
|
||||
- Output: `[[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]`
|
||||
- Explanation: The following cells can flow to the Pacific and Atlantic oceans, as shown below: `[0,4]`: `[0,4]` -> Pacific Ocean `[0,4]` -> Atlantic Ocean `[1,3]`: `[1,3]` -> `[0,3]` -> Pacific Ocean `[1,3]` -> `[1,4]` -> Atlantic Ocean `[1,4]`: `[1,4]` -> `[1,3]` -> `[0,3]` -> Pacific Ocean `[1,4]` -> Atlantic Ocean `[2,2]`: `[2,2]` -> `[1,2]` -> `[0,2]` -> Pacific Ocean `[2,2]` -> `[2,3]` -> `[2,4]` -> Atlantic Ocean `[3,0]`: `[3,0]` -> Pacific Ocean `[3,0]` -> `[4,0]` -> Atlantic Ocean `[3,1]`: `[3,1]` -> `[3,0]` -> Pacific Ocean `[3,1]` -> `[4,1]` -> Atlantic Ocean `[4,0]`: `[4,0]` -> Pacific Ocean `[4,0]` -> Atlantic Ocean Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.
|
||||
|
||||
### Example 2:
|
||||
- Input: `heights = [[1]]`
|
||||
- Output: `[[0,0]]`
|
||||
- Explanation: The water can flow from the only cell to the Pacific and Atlantic oceans.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == heights.length`
|
||||
- `n == heights[r].length`
|
||||
- `1 <= m, n <= 200`
|
||||
- `0 <= heights[r][c] <= 10^5`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def pacificAtlantic(self, heights: list[list[int]]) -> list[list[int]]:
|
||||
rows, cols = len(heights), len(heights[0])
|
||||
pac, atl = set(), set()
|
||||
|
||||
def dfs(r, c, seen, prevHeight):
|
||||
if (
|
||||
(r, c) in seen
|
||||
or r < 0
|
||||
or c < 0
|
||||
or r == rows
|
||||
or c == cols
|
||||
or heights[r][c] < prevHeight
|
||||
):
|
||||
return
|
||||
seen.add((r, c))
|
||||
dfs(r + 1, c, seen, heights[r][c])
|
||||
dfs(r - 1, c, seen, heights[r][c])
|
||||
dfs(r, c + 1, seen, heights[r][c])
|
||||
dfs(r, c - 1, seen, heights[r][c])
|
||||
|
||||
for c in range(cols):
|
||||
dfs(0, c, pac, heights[0][c])
|
||||
dfs(rows - 1, c, atl, heights[rows - 1][c])
|
||||
|
||||
for r in range(rows):
|
||||
dfs(r, 0, pac, heights[r][0])
|
||||
dfs(r, cols - 1, atl, heights[r][cols - 1])
|
||||
|
||||
res = []
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
if (r, c) in pac and (r, c) in atl:
|
||||
res.append([r, c])
|
||||
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Graph DFS
|
||||
description: 'Each edge is followed to its end, and a visited set prevents a repeat visit.'
|
||||
hidden: true
|
||||
---
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: '207. Course Schedule'
|
||||
description: There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Graph Theory", slug: "graph" }
|
||||
- { name: "Topological Sort", slug: "topological-sort" }
|
||||
- { name: "Directed Acyclic Graph", slug: "directed-acyclic-graph" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/142"
|
||||
sidebar:
|
||||
label: 'Course Schedule'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `numCourses = 2, prerequisites = [[1,0]]`
|
||||
- Output: `true`
|
||||
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`. So it is possible.
|
||||
|
||||
### Example 2:
|
||||
- Input: `numCourses = 2, prerequisites = [[1,0],[0,1]]`
|
||||
- Output: `false`
|
||||
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`, and to take course `0` you should also have finished course `1`. So it is impossible.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= numCourses <= 2000`
|
||||
- `0 <= prerequisites.length <= 5000`
|
||||
- `prerequisites[i].length == 2`
|
||||
- `0 <= ai, bi < numCourses`
|
||||
- All the pairs `prerequisites[i]` are unique.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool:
|
||||
indegree = [0] * numCourses
|
||||
adj = [[] for x in range(numCourses)]
|
||||
for prereq in prerequisites:
|
||||
adj[prereq[1]].append(prereq[0])
|
||||
indegree[prereq[0]] += 1
|
||||
|
||||
queue = []
|
||||
|
||||
for i in range(numCourses):
|
||||
if indegree[i] == 0:
|
||||
queue.append(i)
|
||||
|
||||
visited = 0
|
||||
while queue:
|
||||
node = queue.pop(0)
|
||||
visited += 1
|
||||
for neighbor in adj[node]:
|
||||
indegree[neighbor] -= 1
|
||||
if indegree[neighbor] == 0:
|
||||
queue.append(neighbor)
|
||||
|
||||
return numCourses == visited
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: '210. Course Schedule II'
|
||||
description: There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Graph Theory", slug: "graph" }
|
||||
- { name: "Topological Sort", slug: "topological-sort" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/143"
|
||||
sidebar:
|
||||
label: 'Course Schedule II'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `numCourses = 2, prerequisites = [[1,0]]`
|
||||
- Output: `[0,1]`
|
||||
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`. So the correct course order is `[0,1]`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]`
|
||||
- Output: `[0,2,1,3]`
|
||||
- Explanation: There are a total of `4` courses to take. To take course `3` you should have finished both courses `1` and `2`. Both courses `1` and `2` should be taken after you finished course `0`. So one correct course order is `[0,1,2,3]`. Another correct ordering is `[0,2,1,3]`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `numCourses = 1, prerequisites = []`
|
||||
- Output: `[0]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= numCourses <= 2000`
|
||||
- `0 <= prerequisites.length <= numCourses * (numCourses - 1)`
|
||||
- `prerequisites[i].length == 2`
|
||||
- `0 <= ai, bi < numCourses`
|
||||
- `ai != bi`
|
||||
- All the pairs [ai, bi] are distinct.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def findOrder(self, numCourses: int, prerequisites: list[list[int]]) -> list[int]:
|
||||
prereq = {c: [] for c in range(numCourses)}
|
||||
for crs, pre in prerequisites:
|
||||
prereq[crs].append(pre)
|
||||
|
||||
# course has 3 possible states:
|
||||
# visited -> crs has been added to output
|
||||
# visiting -> crs not added to output, but added to cycle
|
||||
# unvisited -> crs not added to output nor cycle
|
||||
output = []
|
||||
visit, cycle = set(), set()
|
||||
|
||||
def dfs(crs):
|
||||
if crs in cycle:
|
||||
return False
|
||||
if crs in visit:
|
||||
return True
|
||||
|
||||
cycle.add(crs)
|
||||
for pre in prereq[crs]:
|
||||
if dfs(pre) == False:
|
||||
return False
|
||||
|
||||
cycle.remove(crs)
|
||||
visit.add(crs)
|
||||
output.append(crs)
|
||||
return True
|
||||
|
||||
for c in range(numCourses):
|
||||
if dfs(c) == False:
|
||||
return []
|
||||
|
||||
return output
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Topological Sort
|
||||
description: 'Topological sort or topological ordering of a directed graph is an ordering of nodes such that every node appears in the ordering before all the nodes it points to. '
|
||||
hidden: true
|
||||
---
|
||||
|
||||
## Directed Graph
|
||||
|
||||
Before we get started on topological order, let's talk about directed graphs first. A graph is directed when its edges have directions (these edges are also called arcs). Suppose that v, w are vertices in a directed graph. Let's get familiar with a few concepts.
|
||||
|
||||
- **in-edge** - an edge pointing to or coming into the node
|
||||
- **out-edge** - an edge pointing away or going out from the node (we will refer those nodes on the other end of the out-edges of v to v's neighbors)
|
||||
- **in-degree** - the number of in-edges coming into the node
|
||||
- **out-degree** - the number of out-edges going out from the node
|
||||
- **dependency** - "v depends on w" / "v is dependent on w" means that there is a directed path from w to v where v, w are vertices in a directed graph. With these new terms under our belt, it will make more sense when we talk about directed graphs.
|
||||
|
||||
## What is a topological order?
|
||||
|
||||
Topological sort is not unique. For example, for a graph like this:
|
||||
|
||||

|
||||
|
||||
Both `[4, 5, 2]` and `[5, 4, 2]` are valid topological orders.
|
||||
|
||||

|
||||
|
||||
Task 3 is completely independent of task 2 and 4, and it can be anywhere in the order as long as it is before task 1 which depends on it. All of the following ordering are valid topological orderings.
|
||||
|
||||
`[4, 2, 3, 1], [4, 3, 2, 1], [3, 4, 2, 1]`
|
||||
|
||||
|
||||
:::warning[Graphs with Cycles Do Not Have Topological Ordering]
|
||||
It should be obvious that if a graph with a cycle does not have a topological ordering. In the above example, 2 has to come before 5 which has come before 4 which has to come before 2 which has to come before 5... which is impossible.
|
||||
:::
|
||||
|
||||
## Kahn's Algorithm
|
||||
|
||||
To obtain a topological order, we can use Kahn's algorithm which is very similar to Breadth First Search.
|
||||
|
||||
In order to understand why we must use this algorithm we first look at the problem it is trying to solve.
|
||||
|
||||
Given a directed graph does there exist a way to remove the nodes such that each time we remove a node we guarantee that no other nodes point to that particular node?
|
||||
|
||||
For this algorithm, we systematically remove one node at a time, each time removing a node such that no other nodes point to that node (in-degree is 0). If no such node exists, then there must be a cycle, and there is no way to order the nodes such that "every node appears in the ordering before all the nodes it points to" (no solution). After removing the current node, for each neighboring node the current node points to, we check whether any nodes point to this node. If there isn't any, we push this node into the queue. Notice it is important to keep track of the number of nodes pointing to a node (in-degree) in the question as we only push the node into the queue once all nodes the current node depended on have been removed.
|
||||
|
||||
Here is a graphic to demonstrate the idea.
|
||||
|
||||
<Carousel>
|
||||
<CarouselImage src='/topo_kahn.001.png' />
|
||||
<CarouselImage src='/topo_kahn.002.png' />
|
||||
<CarouselImage src='/topo_kahn.003.png' />
|
||||
<CarouselImage src='/topo_kahn.004.png' />
|
||||
<CarouselImage src='/topo_kahn.005.png' />
|
||||
<CarouselImage src='/topo_kahn.006.png' />
|
||||
<CarouselImage src='/topo_kahn.007.png' />
|
||||
<CarouselImage src='/topo_kahn.009.png' />
|
||||
<CarouselImage src='/topo_kahn.010.png' />
|
||||
</Carousel>
|
||||
|
||||
## Example Code
|
||||
|
||||
:::tip
|
||||
Notice the topological sort algorithm is very similar to BFS. The main difference is that we only push nodes with 0 in-degree into the queue in topological sort whereas in BFS we push all the neighboring nodes into the queue.
|
||||
:::
|
||||
|
||||
```py
|
||||
from collections import deque
|
||||
|
||||
def find_indegree(graph):
|
||||
indegree = { node: 0 for node in graph } # dict
|
||||
for node in graph:
|
||||
for neighbor in graph[node]:
|
||||
indegree[neighbor] += 1
|
||||
return indegree
|
||||
|
||||
|
||||
def topo_sort(graph):
|
||||
res = []
|
||||
q = deque()
|
||||
indegree = find_indegree(graph)
|
||||
for node in indegree:
|
||||
if indegree[node] == 0:
|
||||
q.append(node)
|
||||
while len(q) > 0:
|
||||
node = q.popleft()
|
||||
res.append(node)
|
||||
for neighbor in graph[node]:
|
||||
indegree[neighbor] -= 1
|
||||
if indegree[neighbor] == 0:
|
||||
q.append(neighbor)
|
||||
return res if len(graph) == len(res) else None
|
||||
```
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: '323. Number of Connected Components in an Undirected Graph'
|
||||
description: You have a graph of n nodes. You are given an integer n and an array edges where edges[i] = [ai, bi] indicates that there is an edge between ai and bi in the graph
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Union-Find", slug: "union-find" }
|
||||
- { name: "Graph Theory", slug: "graph" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/147"
|
||||
sidebar:
|
||||
label: 'Number of Connected Components in an Undirected Graph'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `n = 5, edges = [[0,1],[1,2],[3,4]]`
|
||||
- Output: `2`
|
||||
|
||||
### Example 2:
|
||||
- Input: `n = 5, edges = [[0,1],[1,2],[2,3],[3,4]]`
|
||||
- Output: `1`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= n <= 2000`
|
||||
- `1 <= edges.length <= 5000`
|
||||
- `edges[i]` = [ai, bi]
|
||||
- `ai != bi`
|
||||
- There are no repeated edges.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def countComponents(self, n: int, edges: list[list[int]]) -> int:
|
||||
par = [i for i in range(n + 1)]
|
||||
rank = [1] * (n + 1)
|
||||
|
||||
def find(n):
|
||||
res = n
|
||||
while res != par[res]:
|
||||
par[res] = par[par[res]]
|
||||
res = par[res]
|
||||
return res
|
||||
|
||||
def union(n1, n2):
|
||||
p1, p2 = find(n1), find(n2)
|
||||
if p1 == p2:
|
||||
return 0
|
||||
if rank[p1] > rank[p2]:
|
||||
par[p2] = p1
|
||||
rank[p1] += rank[p2]
|
||||
else:
|
||||
par[p1] = p2
|
||||
rank[p2] += rank[p1]
|
||||
return 1
|
||||
|
||||
res = n
|
||||
for n1, n2 in edges:
|
||||
res -= union(n1, n2)
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: '684. Redundant Connection'
|
||||
description: In this problem, a tree is an undirected graph that is connected and has no cycles
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Depth-First Search", slug: "depth-first-search" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Union-Find", slug: "union-find" }
|
||||
- { name: "Graph Theory", slug: "graph" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/146"
|
||||
sidebar:
|
||||
label: 'Redundant Connection'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `edges = [[1,2],[1,3],[2,3]]`
|
||||
- Output: `[2,3]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]`
|
||||
- Output: `[1,4]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == edges.length`
|
||||
- `3 <= n <= 1000`
|
||||
- `edges[i].length == 2`
|
||||
- `1 <= ai < bi <= edges.length`
|
||||
- `ai != bi`
|
||||
- There are no repeated edges.
|
||||
- The given graph is connected.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def findRedundantConnection(self, edges: list[list[int]]) -> list[int]:
|
||||
N = len(edges)
|
||||
par = [i for i in range(N + 1)]
|
||||
rank = [1] * (N + 1)
|
||||
|
||||
def find(n):
|
||||
if n != par[n]:
|
||||
par[n] = find(par[n])
|
||||
return par[n]
|
||||
|
||||
def union(n1, n2):
|
||||
p1, p2 = find(n1), find(n2)
|
||||
if p1 == p2:
|
||||
return False
|
||||
|
||||
if rank[p1] > rank[p2]:
|
||||
par[p2] = p1
|
||||
rank[p1] += rank[p2]
|
||||
else:
|
||||
par[p1] = p2
|
||||
rank[p2] += rank[p1]
|
||||
|
||||
return True
|
||||
|
||||
for n1, n2 in edges:
|
||||
if not union(n1, n2):
|
||||
return [n1, n2]
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Union Find
|
||||
description: 'Union-Find (Disjoint Set Union) is a data structure that maintains a collection of disjoint sets and supports efficiently finding which set an element belongs to and merging two sets.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: '198. House Robber'
|
||||
description: You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/158"
|
||||
sidebar:
|
||||
label: 'House Robber'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,2,3,1]`
|
||||
- Output: `4`
|
||||
- Explanation: Rob house `1` (money = `1`) and then rob house `3` (money = `3`). Total amount you can rob = `1 + 3 = 4`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [2,7,9,3,1]`
|
||||
- Output: `12`
|
||||
- Explanation: Rob house `1` (money = `2`), rob house `3` (money = `9`) and rob house `5` (money = `1`). Total amount you can rob = `2 + 9 + 1 = 12`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 100`
|
||||
- `0 <= nums[i] <= 400`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def rob(self, nums: list[int]) -> int:
|
||||
n = len(nums)
|
||||
if n == 0:
|
||||
return 0
|
||||
if n <= 2:
|
||||
return max(nums)
|
||||
|
||||
dp = [0] * n
|
||||
dp[0] = nums[0]
|
||||
dp[1] = max(nums[0], nums[1])
|
||||
for i in range(2, n):
|
||||
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
|
||||
return dp[-1]
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: '322. Coin Change'
|
||||
description: You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Breadth-First Search", slug: "breadth-first-search" }
|
||||
- { name: "Knapsack Problem", slug: "knapsack-problem" }
|
||||
- { name: "Complete Knapsack", slug: "complete-knapsack" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/159"
|
||||
sidebar:
|
||||
label: 'Coin Change'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `coins = [1,2,5], amount = 11`
|
||||
- Output: `3`
|
||||
- Explanation: `11 = 5 + 5 + 1`
|
||||
|
||||
### Example 2:
|
||||
- Input: `coins = [2], amount = 3`
|
||||
- Output: `-1`
|
||||
|
||||
### Example 3:
|
||||
- Input: `coins = [1], amount = 0`
|
||||
- Output: `0`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= coins.length <= 12`
|
||||
- `1 <= coins[i] <= 2^31 - 1`
|
||||
- `0 <= amount <= 10^4`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
from math import inf
|
||||
|
||||
|
||||
class Solution:
|
||||
def coinChange(self, coins: list[int], amount: int) -> int:
|
||||
n = len(coins)
|
||||
memo = [[-1] * (amount + 1) for _ in range(n + 1)]
|
||||
|
||||
def min_coins(coins, i, amount, memo):
|
||||
if amount == 0:
|
||||
return 0
|
||||
if i == 0 or amount < 0:
|
||||
return inf
|
||||
if memo[i][amount] != -1:
|
||||
return memo[i][amount]
|
||||
|
||||
skip = min_coins(coins, i - 1, amount, memo)
|
||||
use = min_coins(coins, i, amount - coins[i - 1], memo)
|
||||
if use != inf:
|
||||
use += 1
|
||||
|
||||
memo[i][amount] = min(skip, use)
|
||||
return memo[i][amount]
|
||||
|
||||
result = min_coins(coins, n, amount, memo)
|
||||
return result if result != inf else -1 # pyright: ignore[reportReturnType]
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: '70. Climbing Stairs'
|
||||
description: You are climbing a staircase. It takes n steps to reach the top
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Memoization", slug: "memoization" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/157"
|
||||
sidebar:
|
||||
label: 'Climbing Stairs'
|
||||
badge: 'Easy'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `n = 2`
|
||||
- Output: `2`
|
||||
- Explanation: There are two ways to climb to the top. `1`. `1` step + `1` step `2`. `2` steps
|
||||
|
||||
### Example 2:
|
||||
- Input: `n = 3`
|
||||
- Output: `3`
|
||||
- Explanation: There are three ways to climb to the top. `1`. `1` step + `1` step + `1` step `2`. `1` step + `2` steps `3`. `2` steps + `1` step
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= n <= 45`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def climbStairs(self, n: int) -> int:
|
||||
if n == 1:
|
||||
return 1
|
||||
if n == 2:
|
||||
return 2
|
||||
|
||||
first, second = 1, 2
|
||||
|
||||
for _ in range(3, n + 1):
|
||||
third = first + second
|
||||
first, second = second, third
|
||||
|
||||
return second
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: 1-D Dynamic Programming
|
||||
description: 'The result for each state is stored in a list, and each new result is computed from the results before it.'
|
||||
hidden: true
|
||||
---
|
||||
Loaded 100 of 212 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user