mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-03 21:27:48 +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
@@ -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
|
||||
---
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: '39. Combination Sum'
|
||||
description: Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Backtracking", slug: "backtracking" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/150"
|
||||
sidebar:
|
||||
label: 'Combination Sum'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `candidates = [2,3,6,7], target = 7`
|
||||
- Output: `[[2,2,3],[7]]`
|
||||
- Explanation: `2` and `3` are `candidates`, and `2 + 2 + 3 = 7`. Note that `2` can be used multiple times. `7` is a candidate, and `7 = 7`. These are the only two combinations.
|
||||
|
||||
### Example 2:
|
||||
- Input: `candidates = [2,3,5], target = 8`
|
||||
- Output: `[[2,2,2,2],[2,3,3],[3,5]]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `candidates = [2], target = 1`
|
||||
- Output: `[]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= candidates.length <= 30`
|
||||
- `2 <= candidates[i] <= 40`
|
||||
- All elements of `candidates` are distinct.
|
||||
- `1 <= target <= 40`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def combinationSum(self, candidates: list[int], target: int) -> list[list[int]]:
|
||||
candidates.sort()
|
||||
result, path = [], []
|
||||
|
||||
def backtrack(start, remaining):
|
||||
if remaining == 0:
|
||||
result.append(path[:])
|
||||
return
|
||||
for i in range(start, len(candidates)):
|
||||
if candidates[i] > remaining:
|
||||
break
|
||||
|
||||
path.append(candidates[i])
|
||||
backtrack(i, remaining - candidates[i])
|
||||
path.pop()
|
||||
|
||||
backtrack(0, target)
|
||||
return result
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: '46. Permutations'
|
||||
description: Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Backtracking", slug: "backtracking" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/151"
|
||||
sidebar:
|
||||
label: 'Permutations'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,2,3]`
|
||||
- Output: `[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [0,1]`
|
||||
- Output: `[[0,1],[1,0]]`
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [1]`
|
||||
- Output: `[[1]]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 6`
|
||||
- `-10 <= nums[i] <= 10`
|
||||
- All the integers of `nums` are unique.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def permute(self, nums: list[int]) -> list[list[int]]:
|
||||
result, path = [], []
|
||||
used = [False] * len(nums)
|
||||
|
||||
def backtrack():
|
||||
if len(path) == len(nums):
|
||||
result.append(path[:])
|
||||
return
|
||||
for i in range(len(nums)):
|
||||
if used[i]:
|
||||
continue
|
||||
used[i] = True
|
||||
path.append(nums[i])
|
||||
backtrack()
|
||||
path.pop()
|
||||
used[i] = False
|
||||
|
||||
backtrack()
|
||||
return result
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: '78. Subsets'
|
||||
description: Given an integer array nums of unique elements, return all possible subsets (the power set)
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Backtracking", slug: "backtracking" }
|
||||
- { name: "Bit Manipulation", slug: "bit-manipulation" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/149"
|
||||
sidebar:
|
||||
label: 'Subsets'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [1,2,3]`
|
||||
- Output: `[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [0]`
|
||||
- Output: `[[],[0]]`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10`
|
||||
- `-10 <= nums[i] <= 10`
|
||||
- All the numbers of `nums` are unique.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def subsets(self, nums: list[int]) -> list[list[int]]:
|
||||
res = []
|
||||
|
||||
def backtrack(i, path):
|
||||
res.append(path[:])
|
||||
for num_idx in range(i, len(nums)):
|
||||
path.append(nums[num_idx])
|
||||
backtrack(num_idx + 1, path)
|
||||
path.pop()
|
||||
|
||||
backtrack(0, [])
|
||||
return res
|
||||
```
|
||||
@@ -0,0 +1,163 @@
|
||||
---
|
||||
title: Backtracking
|
||||
description: 'Backtracking is a depth-first search technique that builds a solution step by step and abandons (undoes) a partial choice as soon as it can’t lead to a valid complete solution.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
## What it is
|
||||
|
||||
Backtracking explores possible paths to a solution one step at a time. When a path reaches a dead end, you undo the last step and try a different option. Think of every problem as a decision tree. The root is an empty answer. Each level is a decision. Each branch is one option.
|
||||
|
||||
## When to use it
|
||||
|
||||
Use backtracking when you must generate **all** valid solutions or evaluate **all** combinations. Do not use it when you need only one solution or the optimal solution. For those, use greedy or dynamic programming. Backtracking has exponential time complexity.
|
||||
|
||||
## The four components
|
||||
|
||||
Every backtracking problem has the same parts:
|
||||
|
||||
1. **Base case** — when to save the current path and stop.
|
||||
2. **Choices** — the options at each step.
|
||||
3. **Constraints** — the rules that reject an invalid path early.
|
||||
4. **Backtrack step** — undo the last choice and try the next one.
|
||||
|
||||
## The template
|
||||
|
||||
```python
|
||||
def solve(data):
|
||||
result = []
|
||||
path = []
|
||||
|
||||
def backtrack(index):
|
||||
# 1. Base case
|
||||
if is_complete(index, path):
|
||||
result.append(path[:]) # copy — lists are passed by reference
|
||||
return
|
||||
|
||||
# 2. Choices
|
||||
for choice in get_choices(index, data):
|
||||
# 3. Constraints
|
||||
if not is_valid(choice, path):
|
||||
continue
|
||||
|
||||
path.append(choice) # make the choice
|
||||
backtrack(index + 1) # explore
|
||||
path.pop() # 4. Backtrack
|
||||
|
||||
backtrack(0)
|
||||
return result
|
||||
```
|
||||
|
||||
## Steps to apply the template
|
||||
|
||||
1. Draw the decision tree for a small input. Name each level and each branch.
|
||||
2. Write the base case. Ask: when is the path complete?
|
||||
3. Write the choices. Ask: what can I add at this level?
|
||||
4. Write the constraints. Ask: what makes a path invalid? Reject it before recursion.
|
||||
5. Add the backtrack step. Pop what you appended.
|
||||
6. Copy the path when you save it. Never append the same list reference.
|
||||
|
||||
## Four problems, one template
|
||||
|
||||
### Subsets
|
||||
|
||||
Choice: include or skip each number. No constraint. Base case: index equals list length.
|
||||
|
||||
```python
|
||||
def subsets(nums):
|
||||
result, path = [], []
|
||||
|
||||
def backtrack(i):
|
||||
if i == len(nums):
|
||||
result.append(path[:])
|
||||
return
|
||||
path.append(nums[i]) # include
|
||||
backtrack(i + 1)
|
||||
path.pop()
|
||||
backtrack(i + 1) # skip
|
||||
|
||||
backtrack(0)
|
||||
return result
|
||||
```
|
||||
|
||||
### Permutations
|
||||
|
||||
Same choices as subsets. Constraint: do not reuse a number already in the path. Base case: path length equals list length.
|
||||
|
||||
```python
|
||||
def permute(nums):
|
||||
result, path = [], []
|
||||
used = [False] * len(nums)
|
||||
|
||||
def backtrack():
|
||||
if len(path) == len(nums):
|
||||
result.append(path[:])
|
||||
return
|
||||
for i in range(len(nums)):
|
||||
if used[i]:
|
||||
continue
|
||||
used[i] = True
|
||||
path.append(nums[i])
|
||||
backtrack()
|
||||
path.pop()
|
||||
used[i] = False
|
||||
|
||||
backtrack()
|
||||
return result
|
||||
```
|
||||
|
||||
### Combinations
|
||||
|
||||
Same as subsets. Change one line: save the path only when its length equals `k`.
|
||||
|
||||
```python
|
||||
def combine(n, k):
|
||||
result, path = [], []
|
||||
|
||||
def backtrack(start):
|
||||
if len(path) == k:
|
||||
result.append(path[:])
|
||||
return
|
||||
for i in range(start, n + 1):
|
||||
path.append(i)
|
||||
backtrack(i + 1)
|
||||
path.pop()
|
||||
|
||||
backtrack(1)
|
||||
return result
|
||||
```
|
||||
|
||||
### N-Queens
|
||||
|
||||
Choice: which column to place the queen in the current row. Constraint: no shared column or diagonal. Base case: `n` queens placed. Backtrack: remove the last queen.
|
||||
|
||||
```python
|
||||
def solve_n_queens(n):
|
||||
result, cols = [], []
|
||||
used_cols, diag1, diag2 = set(), set(), set()
|
||||
|
||||
def backtrack(row):
|
||||
if row == n:
|
||||
result.append(cols[:])
|
||||
return
|
||||
for col in range(n):
|
||||
if col in used_cols or row - col in diag1 or row + col in diag2:
|
||||
continue
|
||||
cols.append(col)
|
||||
used_cols.add(col); diag1.add(row - col); diag2.add(row + col)
|
||||
backtrack(row + 1)
|
||||
cols.pop()
|
||||
used_cols.remove(col); diag1.remove(row - col); diag2.remove(row + col)
|
||||
|
||||
backtrack(0)
|
||||
return result
|
||||
```
|
||||
|
||||
## Common mistakes
|
||||
|
||||
- You append the `path` list itself instead of a copy. The result fills with empty lists.
|
||||
- You forget to pop. The path grows and never resets.
|
||||
- You check constraints after recursion instead of before. You waste time on dead paths.
|
||||
- You use backtracking when you need only one answer. Use greedy or DP instead.
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: '134. Gas Station'
|
||||
description: There are n gas stations along a circular route, where the amount of gas at the i^th station is gas[i]
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Greedy", slug: "greedy" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/176"
|
||||
sidebar:
|
||||
label: 'Gas Station'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `gas = [1,2,3,4,5], cost = [3,4,5,1,2]`
|
||||
- Output: `3`
|
||||
- Explanation: Start at station `3` (index `3`) and fill up with `4` unit of `gas`. Your tank = `0 + 4 = 4` Travel to station `4`. Your tank = `4 - 1 + 5 = 8` Travel to station `0`. Your tank = `8 - 2 + 1 = 7` Travel to station `1`. Your tank = `7 - 3 + 2 = 6` Travel to station `2`. Your tank = `6 - 4 + 3 = 5` Travel to station `3`. The `cost` is `5`. Your `gas` is just enough to travel back to station `3`. Therefore, return `3` as the starting index.
|
||||
|
||||
### Example 2:
|
||||
- Input: `gas = [2,3,4], cost = [3,4,3]`
|
||||
- Output: `-1`
|
||||
- Explanation: You can't start at station `0` or `1`, as there is not enough `gas` to travel to the next station. Let's start at station `2` and fill up with `4` unit of `gas`. Your tank = `0 + 4 = 4` Travel to station `0`. Your tank = `4 - 3 + 2 = 3` Travel to station `1`. Your tank = `3 - 3 + 3 = 3` You cannot travel back to station `2`, as it requires `4` unit of `gas` but you only have `3`. Therefore, you can't travel around the circuit once no matter where you start.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `n == gas.length == cost.length`
|
||||
- `1 <= n <= 10^5`
|
||||
- `0 <= gas[i], cost[i] <= 10^4`
|
||||
- The input is generated such that the answer is unique.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def canCompleteCircuit(self, gas: list[int], cost: list[int]) -> int:
|
||||
start, tank, total = 0, 0, 0
|
||||
|
||||
for i in range(len(gas)):
|
||||
drive = gas[i] - cost[i]
|
||||
tank = tank + drive
|
||||
|
||||
if tank < 0:
|
||||
tank = 0
|
||||
start = i + 1
|
||||
total = total + drive
|
||||
|
||||
return start if total >= 0 else -1
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: '53. Maximum Subarray'
|
||||
description: Given an integer array nums, find the subarray with the largest sum, and return its sum
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Divide and Conquer", slug: "divide-and-conquer" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/174"
|
||||
sidebar:
|
||||
label: 'Maximum Subarray'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
::::warning
|
||||
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
|
||||
::::
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [-2,1,-3,4,-1,2,1,-5,4]`
|
||||
- Output: `6`
|
||||
- Explanation: The subarray `[4,-1,2,1]` has the largest sum `6`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [1]`
|
||||
- Output: `1`
|
||||
- Explanation: The subarray `[1]` has the largest sum `1`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `nums = [5,4,-1,7,8]`
|
||||
- Output: `23`
|
||||
- Explanation: The subarray `[5,4,-1,7,8]` has the largest sum `23`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^5`
|
||||
- `-10^4 <= nums[i] <= 10^4`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def maxSubArray(self, nums: list[int]) -> int:
|
||||
best, total = nums[0], nums[0]
|
||||
|
||||
for i in range(1, len(nums)):
|
||||
if total < 0:
|
||||
total = nums[i]
|
||||
else:
|
||||
total = total + nums[i]
|
||||
best = max(best, total)
|
||||
|
||||
return best
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: '55. Jump Game'
|
||||
description: You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Greedy", slug: "greedy" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/175"
|
||||
sidebar:
|
||||
label: 'Jump Game'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `nums = [2,3,1,1,4]`
|
||||
- Output: `true`
|
||||
- Explanation: Jump `1` step from index `0` to `1`, then `3` steps to the last index.
|
||||
|
||||
### Example 2:
|
||||
- Input: `nums = [3,2,1,0,4]`
|
||||
- Output: `false`
|
||||
- Explanation: You will always arrive at index `3` no matter what. Its maximum jump length is `0`, which makes it impossible to reach the last index.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= nums.length <= 10^4`
|
||||
- `0 <= nums[i] <= 10^5`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def canJump(self, nums: list[int]) -> bool:
|
||||
best, last = nums[0], len(nums) - 1
|
||||
|
||||
for i in range(last):
|
||||
if i > best:
|
||||
return False
|
||||
else:
|
||||
pos = nums[i] + i
|
||||
best = max(best, pos)
|
||||
|
||||
return best >= last
|
||||
```
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Greedy
|
||||
description: 'The best local choice is made at each step and is not changed later.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
## When to use greedy algorithms?
|
||||
|
||||
- Solving optimization problems.
|
||||
- Ask to optimize some value and calculate it.
|
||||
- keywords (min/max, longest/shortest, largest/smallest, etc.)
|
||||
|
||||
## Optimization problems
|
||||
|
||||
1. Return the minimum number of swaps required to make s1 equal to s2.
|
||||
2. Return the minimum number of steps to make s and t anagrams of each other.
|
||||
3. Given three integers a, b, and c, return the longest possible happy string, if there are multiple such strings return any of them. if there is no such string, return empty string.
|
||||
|
||||
## What are greedy algorithms?
|
||||
|
||||
- Greedy algorithms follow this format:
|
||||
- identify what choices you have at every step
|
||||
- find the best choice
|
||||
- just do what looks best until done
|
||||
- assume that the best choice is the one that will lead to the best outcome
|
||||
|
||||
## How do we know greedy works?
|
||||
- Write out a formal proof
|
||||
- too complicated to write out
|
||||
- too much time
|
||||
- better to use intuition
|
||||
- try a bunch of ideas
|
||||
- find counterexamples
|
||||
|
||||
## Steps to use greedy algorithms
|
||||
|
||||
<Steps>
|
||||
<Step title="Identify the different steps and choices">
|
||||
- Choices have to be comparable
|
||||
- must determine if a choice is better than another
|
||||
</Step>
|
||||
<Step title="Check if greedy works">
|
||||
- try finding small cases where greedy is not optimal
|
||||
- if cases pass, try to deduce why greedy is optimal
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Why use greedy algorithms?
|
||||
|
||||
- simple to code
|
||||
- doesn't use complex algorithms (BFS, DFS, etc.)
|
||||
- runtime is fast, usually 0(n) or O(nlogn)
|
||||
- however, greedy algorithms are not always optimal
|
||||
|
||||
## The Greedy Skeleton
|
||||
|
||||
### Type 1: Walk and Carry
|
||||
|
||||
:::info
|
||||
No sort. Walk the list once. Carry one number. At each step, decide: keep it, or drop it.
|
||||
:::
|
||||
|
||||
```python
|
||||
def walk_and_carry(nums):
|
||||
carry = start_value
|
||||
best = start_value
|
||||
for n in nums:
|
||||
if carry_is_useless(carry):
|
||||
carry = fresh_start
|
||||
carry = update(carry, n)
|
||||
best = max(best, carry)
|
||||
return best
|
||||
```
|
||||
|
||||
### Type 2: Sort and Pick
|
||||
|
||||
:::info
|
||||
Used when order doesn't matter, so you pick the order. Meeting rooms, intervals, "most tasks by a deadline."
|
||||
:::
|
||||
|
||||
```python
|
||||
def greedy(items):
|
||||
# 1. Sort by the greedy key (deadline, end time, size, ratio, ...)
|
||||
items.sort(key=greedy_key)
|
||||
result = 0
|
||||
state = initial_state()
|
||||
for item in items:
|
||||
# 2. Take the item only if the locally best choice is valid
|
||||
if is_feasible(item, state):
|
||||
result += take(item, state)
|
||||
# 3. Correctness relies on an exchange argument, not just passing tests
|
||||
return result
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: '435. Non-overlapping Intervals'
|
||||
description: Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Greedy", slug: "greedy" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/183"
|
||||
sidebar:
|
||||
label: 'Non-overlapping Intervals'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `intervals = [[1,2],[2,3],[3,4],[1,3]]`
|
||||
- Output: `1`
|
||||
- Explanation: `[1,3]` can be removed and the rest of the `intervals` are non-overlapping.
|
||||
|
||||
### Example 2:
|
||||
- Input: `intervals = [[1,2],[1,2],[1,2]]`
|
||||
- Output: `2`
|
||||
- Explanation: You need to remove two `[1,2]` to make the rest of the `intervals` non-overlapping.
|
||||
|
||||
### Example 3:
|
||||
- Input: `intervals = [[1,2],[2,3]]`
|
||||
- Output: `0`
|
||||
- Explanation: You don't need to remove any of the `intervals` since they're already non-overlapping.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= intervals.length <= 10^5`
|
||||
- `intervals[i].length == 2`
|
||||
- `-5 * 10^4 <= starti < endi <= 5 * 10^4`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def eraseOverlapIntervals(self, intervals: list[list[int]]) -> int:
|
||||
intervals.sort(key=lambda x: x[0])
|
||||
result = []
|
||||
|
||||
for start, end in intervals:
|
||||
# Use '<' to avoid intervals which touch
|
||||
if result and start < result[-1][1]:
|
||||
result[-1][1] = min(result[-1][1], end)
|
||||
else:
|
||||
result.append([start, end])
|
||||
|
||||
# Difference between original and result is to remove
|
||||
return len(intervals) - len(result)
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: '56. Merge Intervals'
|
||||
description: Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Sorting", slug: "sorting" }
|
||||
- { name: "Quicksort", slug: "quicksort" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/182"
|
||||
sidebar:
|
||||
label: 'Merge Intervals'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `intervals = [[1,3],[2,6],[8,10],[15,18]]`
|
||||
- Output: `[[1,6],[8,10],[15,18]]`
|
||||
- Explanation: Since `intervals [1,3]` and `[2,6]` overlap, merge them into `[1,6]`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `intervals = [[1,4],[4,5]]`
|
||||
- Output: `[[1,5]]`
|
||||
- Explanation: Intervals `[1,4]` and `[4,5]` are considered overlapping.
|
||||
|
||||
### Example 3:
|
||||
- Input: `intervals = [[4,7],[1,4]]`
|
||||
- Output: `[[1,7]]`
|
||||
- Explanation: Intervals `[1,4]` and `[4,7]` are considered overlapping.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= intervals.length <= 10^4`
|
||||
- `intervals[i].length == 2`
|
||||
- `0 <= starti <= endi <= 10^4`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def merge(self, intervals: list[list[int]]) -> list[list[int]]:
|
||||
intervals.sort(key=lambda x: x[0])
|
||||
result = []
|
||||
|
||||
for start, end in intervals:
|
||||
if result and start <= result[-1][1]:
|
||||
result[-1][1] = max(result[-1][1], end)
|
||||
else:
|
||||
result.append([start, end])
|
||||
|
||||
return result
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: '57. Insert Interval'
|
||||
description: You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the i^th interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/181"
|
||||
sidebar:
|
||||
label: 'Insert Interval'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `intervals = [[1,3],[6,9]], newInterval = [2,5]`
|
||||
- Output: `[[1,5],[6,9]]`
|
||||
|
||||
### Example 2:
|
||||
- Input: `intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]`
|
||||
- Output: `[[1,2],[3,10],[12,16]]`
|
||||
- Explanation: Because the new interval `[4,8]` overlaps with [3,5],[6,7],[8,10].
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `0 <= intervals.length <= 10^4`
|
||||
- `intervals[i].length == 2`
|
||||
- `0 <= starti <= endi <= 10^5`
|
||||
- intervals is sorted by starti in ascending order.
|
||||
- `newInterval.length == 2`
|
||||
- `0 <= start <= end <= 10^5`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def insert(
|
||||
self, intervals: list[list[int]], newInterval: list[int]
|
||||
) -> list[list[int]]:
|
||||
result = []
|
||||
i, n = 0, len(intervals)
|
||||
start, end = newInterval
|
||||
|
||||
# Everything before
|
||||
while i < n and intervals[i][1] < start:
|
||||
result.append(intervals[i])
|
||||
i += 1
|
||||
|
||||
# Everything merged
|
||||
while i < n and intervals[i][0] <= end:
|
||||
start = min(start, intervals[i][0])
|
||||
end = max(end, intervals[i][1])
|
||||
i += 1
|
||||
result.append([start, end])
|
||||
|
||||
# Everything after
|
||||
result.extend(intervals[i:])
|
||||
return result
|
||||
```
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Intervals
|
||||
description: 'Ranges with a start and an end are sorted, and then merged, counted, or scheduled.'
|
||||
hidden: true
|
||||
---
|
||||
|
||||
An interval is a pair `[start, end]` marking a stretch on a line:
|
||||
- the minutes a meeting occupies
|
||||
- the range a task runs
|
||||
- the span a sensor was active
|
||||
|
||||
**What interval problems ask you?**
|
||||
|
||||
- Do any two collide?
|
||||
- What does the timeline look like once you merge everything that touches?
|
||||
- How many rooms do you need so no two meetings share one?
|
||||
|
||||
:::tip
|
||||
Every one of those questions reduces to a single primitive: deciding whether two intervals overlap, and if they do, how to merge them or what part they share. Once that primitive is clear, the rest of the pattern is bookkeeping.
|
||||
:::
|
||||
|
||||
## When two intervals overlap
|
||||
|
||||
It is easier to state when two intervals do not overlap. Interval `a` sits entirely before interval `b` when `a` finishes before `b` begins, that is `a.end < b.start`. By symmetry they also miss each other when `b` ends before `a` starts. If neither of those holds, the intervals overlap.
|
||||
|
||||

|
||||
|
||||
Negating `a.end < b.start` or `b.end < a.start` gives the positive test directly: two intervals overlap exactly when `a.start <= b.end` and `b.start <= a.end`. That pair of comparisons is the whole overlap check.
|
||||
|
||||
## The Merged Region
|
||||
|
||||
When two intervals overlap, you can merge them into one interval that covers both. Its start is the earlier of the two starts, and its end is the later of the two ends: `[min(a.start, b.start), max(a.end, b.end)]`. The merged interval begins as soon as either interval starts, and it ends only once both have stopped.
|
||||
|
||||

|
||||
|
||||
## The Shared Region
|
||||
|
||||
When two intervals overlap, the part they share is itself an interval. Its start is the later of the two starts, and its end is the earlier of the two ends: `[max(a.start, b.start), min(a.end, b.end)]`. The overlap begins only once both intervals have started, and it ends the moment either one stops.
|
||||
|
||||

|
||||
|
||||
:::tip
|
||||
Merge grows, overlap shrinks. Merge uses `[min, max]` and covers both intervals. Overlap uses `[max, min]` and keeps only the part both share.
|
||||
:::
|
||||
|
||||
## Sort by Start first
|
||||
|
||||
Left unsorted, checking a list of intervals means comparing every pair, which is `O(n^2)`. Sorting the intervals by start time removes that cost. Once the list is in start order, any interval can only overlap ones that come after it and begin before it ends, so a single left-to-right pass is enough to merge, count, or detect collisions.
|
||||
|
||||
Sorting by start is the standard first line of almost every interval solution, and the problems below all lean on it.
|
||||
|
||||
That gives the template every interval problem in this section builds on: sort by start, then sweep once, comparing each interval against the last one you kept.
|
||||
|
||||
|
||||
:::note[Input]
|
||||
`[[1,3], [8,10], [2,6], [4,5], [10,12]]`
|
||||
:::
|
||||
|
||||
:::note[After sort]
|
||||
`[[1,3], [2,6], [4,5], [8,10], [10,12]]`
|
||||
:::
|
||||
|
||||
```python
|
||||
# Time: O(n log n)
|
||||
# Space: O(n)
|
||||
def interval_pattern(intervals):
|
||||
# 1. Sort by start time
|
||||
intervals.sort(key=lambda x: x[0])
|
||||
result = []
|
||||
for start, end in intervals:
|
||||
# 2. Overlap with the last kept interval? start <= last end
|
||||
if result and start <= result[-1][1]:
|
||||
# Merge: extend the end of the last interval
|
||||
result[-1][1] = max(result[-1][1], end)
|
||||
else:
|
||||
# 3. No overlap: keep this interval as-is
|
||||
result.append([start, end])
|
||||
return result
|
||||
```
|
||||
|
||||
<Expandable title="Show trace table">
|
||||
|
||||
| i | `[start, end]` | `result[-1]` | `start <= result[-1][1]`? | Action | `result` after |
|
||||
|---|---|---|---|---|---|
|
||||
| 0 | `[1,3]` | — | — (empty) | append | `[[1,3]]` |
|
||||
| 1 | `[2,6]` | `[1,3]` | `2 <= 3` ✅ | end = `max(3,6)` = 6 | `[[1,6]]` |
|
||||
| 2 | `[4,5]` | `[1,6]` | `4 <= 6` ✅ | end = `max(6,5)` = 6 | `[[1,6]]` |
|
||||
| 3 | `[8,10]` | `[1,6]` | `8 <= 6` ❌ | append | `[[1,6], [8,10]]` |
|
||||
| 4 | `[10,12]` | `[8,10]` | `10 <= 10` ✅ | end = `max(10,12)` = 12 | `[[1,6], [8,12]]` |
|
||||
|
||||
|
||||
:::tip[Output]
|
||||
`[[1,6], [8,12]]`
|
||||
- **Row 2:** `[4,5]` sits inside `[1,6]`. Setting `end = 5` without `max` would cut the interval short. `max` keeps 6.
|
||||
- **Row 4:** `[8,10]` and `[10,12]` only touch at 10. The `<=` check merges them. With `<`, you would get two intervals.
|
||||
:::
|
||||
|
||||
</Expandable>
|
||||
|
||||
|
||||
## Insert into a sorted list
|
||||
|
||||
Sometimes the list is already sorted and has no overlaps, and you must add one new interval. You could append it, sort, and run the template. That works, but it pays `O(n log n)` for a sort you do not need.
|
||||
|
||||
A sorted list with no overlaps splits into three parts around the new interval, in this order:
|
||||
|
||||
1. Intervals that end **before** the new one starts. Keep them as they are.
|
||||
2. Intervals that **overlap** the new one. Merge each into it with `[min, max]`.
|
||||
3. Intervals that start **after** the new one ends. Keep them as they are.
|
||||
|
||||
Walk the list once and handle each part in turn. No sort, so the cost drops to `O(n)`.
|
||||
|
||||
:::note[Intervals]
|
||||
`[[1,2], [3,5], [6,7], [8,10], [12,16]]`
|
||||
:::
|
||||
|
||||
:::note[New interval]
|
||||
`[4,8]`
|
||||
:::
|
||||
|
||||
```python
|
||||
# Time: O(n)
|
||||
# Space: O(n)
|
||||
def insert_interval(intervals, new_interval):
|
||||
result = []
|
||||
i, n = 0, len(intervals)
|
||||
start, end = new_interval
|
||||
|
||||
# 1. Ends before new starts: keep
|
||||
while i < n and intervals[i][1] < start:
|
||||
result.append(intervals[i])
|
||||
i += 1
|
||||
|
||||
# 2. Overlaps new: grow new to cover it
|
||||
while i < n and intervals[i][0] <= end:
|
||||
start = min(start, intervals[i][0])
|
||||
end = max(end, intervals[i][1])
|
||||
i += 1
|
||||
result.append([start, end])
|
||||
|
||||
# 3. Starts after new ends: keep
|
||||
result.extend(intervals[i:])
|
||||
return result
|
||||
```
|
||||
|
||||
<Expandable title="Show trace table">
|
||||
|
||||
|
||||
| i | interval | Part | Check | Action | `[start, end]` | `result` after |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 0 | `[1,2]` | 1 | `2 < 4` ✅ | keep | `[4,8]` | `[[1,2]]` |
|
||||
| 1 | `[3,5]` | 1 | `5 < 4` ❌ | go to part 2 | `[4,8]` | `[[1,2]]` |
|
||||
| 1 | `[3,5]` | 2 | `3 <= 8` ✅ | start = `min(4,3)` = 3 | `[3,8]` | `[[1,2]]` |
|
||||
| 2 | `[6,7]` | 2 | `6 <= 8` ✅ | end = `max(8,7)` = 8 | `[3,8]` | `[[1,2]]` |
|
||||
| 3 | `[8,10]` | 2 | `8 <= 8` ✅ | end = `max(8,10)` = 10 | `[3,10]` | `[[1,2]]` |
|
||||
| 4 | `[12,16]` | 2 | `12 <= 10` ❌ | append new | `[3,10]` | `[[1,2], [3,10]]` |
|
||||
| 4 | `[12,16]` | 3 | — | extend rest | — | `[[1,2], [3,10], [12,16]]` |
|
||||
|
||||
:::tip[Output]
|
||||
`[[1,2], [3,10], [12,16]]`
|
||||
- **Row i=1:** The same interval shows up twice. The part 1 check fails, so that loop stops, and part 2 reads the same interval again. `i` does not move.
|
||||
- **Row i=3:** `[8,10]` only touches `[3,8]` at 8. The `<=` merges them, since one shared point counts as an overlap.
|
||||
- **Part 1 uses `<`, part 2 uses `<=`:** An interval that ends exactly at `start` touches the new interval. It must fail part 1 so part 2 can merge it.
|
||||
:::
|
||||
|
||||
</Expandable>
|
||||
|
||||
:::warning
|
||||
If the input is already sorted, do not sort again. Interviewers add "sorted" to the problem on purpose. Name the `O(n log n)` cost of re-sorting before they do, then offer the `O(n)` three-part walk.
|
||||
:::
|
||||
|
||||
### Footnote
|
||||
|
||||
The positive overlap test comes straight from De Morgan's law. Negating `(a.end < b.start)` or `(b.end < a.start)` gives `(a.end >= b.start)` and `(b.end >= a.start)`, which is the `a.start <= b.end` and `b.start <= a.end` condition rewritten.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: '1143. Longest Common Subsequence'
|
||||
description: Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "String", slug: "string" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Longest Common Subsequence", slug: "longest-common-subsequence" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/168"
|
||||
sidebar:
|
||||
label: 'Longest Common Subsequence'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `text1 = "abcde", text2 = "ace"`
|
||||
- Output: `3`
|
||||
- Explanation: The longest common subsequence is "ace" and its length is `3`.
|
||||
|
||||
### Example 2:
|
||||
- Input: `text1 = "abc", text2 = "abc"`
|
||||
- Output: `3`
|
||||
- Explanation: The longest common subsequence is "abc" and its length is `3`.
|
||||
|
||||
### Example 3:
|
||||
- Input: `text1 = "abc", text2 = "def"`
|
||||
- Output: `0`
|
||||
- Explanation: There is no such common subsequence, so the result is `0`.
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= text1.length, text2.length <= 1000`
|
||||
- `text1` and `text2` consist of only lowercase English characters.
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
|
||||
dp = [[0 for j in range(len(text2) + 1)] for i in range(len(text1) + 1)]
|
||||
|
||||
for i in range(len(text1) - 1, -1, -1):
|
||||
for j in range(len(text2) - 1, -1, -1):
|
||||
if text1[i] == text2[j]:
|
||||
dp[i][j] = 1 + dp[i + 1][j + 1]
|
||||
else:
|
||||
dp[i][j] = max(dp[i][j + 1], dp[i + 1][j])
|
||||
|
||||
return dp[0][0]
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: '62. Unique Paths'
|
||||
description: There is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Math", slug: "math" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Combinatorics", slug: "combinatorics" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/166"
|
||||
sidebar:
|
||||
label: 'Unique Paths'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `m = 3, n = 7`
|
||||
- Output: `28`
|
||||
|
||||
### Example 2:
|
||||
- Input: `m = 3, n = 2`
|
||||
- Output: `3`
|
||||
- Explanation: From the top-left corner, there are a total of `3` ways to reach the bottom-right corner: `1`. Right -> Down -> Down `2`. Down -> Down -> Right `3`. Down -> Right -> Down
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `1 <= m, n <= 100`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def uniquePaths(self, m: int, n: int) -> int:
|
||||
dp = [[0 for _ in range(n)] for _ in range(m)]
|
||||
|
||||
for c in range(n):
|
||||
dp[0][c] = 1
|
||||
|
||||
for r in range(m):
|
||||
dp[r][0] = 1
|
||||
|
||||
for r in range(1, m):
|
||||
for c in range(1, n):
|
||||
dp[r][c] = dp[r - 1][c] + dp[r][c - 1]
|
||||
|
||||
return dp[-1][-1]
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: '64. Minimum Path Sum'
|
||||
description: Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right, which minimizes the sum of all numbers along its path
|
||||
icon: dot
|
||||
topics:
|
||||
- { name: "Array", slug: "array" }
|
||||
- { name: "Dynamic Programming", slug: "dynamic-programming" }
|
||||
- { name: "Matrix", slug: "matrix" }
|
||||
issue: "https://github.com/prdlk/leetcode/issues/167"
|
||||
sidebar:
|
||||
label: 'Minimum Path Sum'
|
||||
badge: 'Medium'
|
||||
---
|
||||
|
||||
### Example 1:
|
||||
- Input: `grid = [[1,3,1],[1,5,1],[4,2,1]]`
|
||||
- Output: `7`
|
||||
- Explanation: Because the path `1` → `3` → `1` → `1` → `1` minimizes the sum.
|
||||
|
||||
### Example 2:
|
||||
- Input: `grid = [[1,2,3],[4,5,6]]`
|
||||
- Output: `12`
|
||||
|
||||
### Constraints:
|
||||
|
||||
- `m == grid.length`
|
||||
- `n == grid[i].length`
|
||||
- `1 <= m, n <= 200`
|
||||
- `0 <= grid[i][j] <= 200`
|
||||
|
||||
## Solution
|
||||
|
||||
```py
|
||||
class Solution:
|
||||
def minPathSum(self, grid: list[list[int]]) -> int:
|
||||
if not grid or not grid[0]:
|
||||
return 0
|
||||
|
||||
m = len(grid)
|
||||
n = len(grid[0])
|
||||
dp = [[0 for _ in range(n)] for _ in range(m)]
|
||||
|
||||
for c in range(n):
|
||||
dp[0][c] = dp[0][c - 1] + grid[0][c] if c else grid[0][c]
|
||||
|
||||
for r in range(m):
|
||||
dp[r][0] = dp[r - 1][0] + grid[r][0] if r else grid[r][0]
|
||||
|
||||
for r in range(1, m):
|
||||
for c in range(1, n):
|
||||
dp[r][c] = min(dp[r - 1][c], dp[r][c - 1]) + grid[r][c]
|
||||
|
||||
return dp[-1][-1]
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Multi-D / Grid DP
|
||||
description: 'The result for each state is stored in a table with two or more dimensions, and each cell is computed from its neighbors.'
|
||||
hidden: true
|
||||
---
|
||||
Reference in new issue
Block a user