diff --git a/README.md b/README.md index a3162960..dd70d8cb 100644 --- a/README.md +++ b/README.md @@ -387,10 +387,26 @@ 4. [单调栈:42.接雨水](./problems/0042.接雨水.md) 5. [单调栈:84.柱状图中最大的矩形](./problems/0084.柱状图中最大的矩形.md) -(持续更新中....) ## 图论 +通知:开始更新图论内容,图论部分还没有其他语言版本,欢迎录友们提交PR,成为contributor + +### 深搜广搜 + +* [图论:深度优先搜索理论基础](./problems/图论深搜理论基础.md) +* [图论:797.所有可能的路径](./problems/0797.所有可能的路径.md) +* [图论:广度优先搜索理论基础](./problems/图论广索理论基础.md) +* [图论:200.岛屿数量.深搜版](./problems/0200.岛屿数量.深搜版.md) +* [图论:200.岛屿数量.广搜版](./problems/0200.岛屿数量.广搜版.md) +* [图论:695.岛屿的最大面积](./problems/0695.岛屿的最大面积.md) +* [图论:1020.飞地的数量](./problems/1020.飞地的数量.md) +* [图论:130.被围绕的区域](./problems/0130.被围绕的区域.md) +* [图论:417.太平洋大西洋水流问题](./problems/0417.太平洋大西洋水流问题.md) + +(持续更新中....) + + ## 十大排序 ## 数论 @@ -492,7 +508,7 @@ 大家好,我是程序员Carl,哈工大师兄,《代码随想录》作者,先后在腾讯和百度从事后端技术研发,CSDN博客专家。对算法和C++后端技术有一定的见解,利用工作之余重新刷leetcode。 -加入「代码随想录」刷题小分队(微信群),可以扫下方二维码加我微信。 +加入「代码随想录」刷题小分队(微信群),可以扫下方二维码,加代码随想录客服微信。 如果是已工作,备注:姓名-城市-岗位-组队刷题。如果学生,备注:姓名-学校-年级-组队刷题。**备注没有自我介绍不通过哦** diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md index d506bb88..cf5e4520 100644 --- a/problems/0017.电话号码的字母组合.md +++ b/problems/0017.电话号码的字母组合.md @@ -287,98 +287,153 @@ class Solution { ``` ## Python -**回溯** +回溯 ```python class Solution: def __init__(self): - self.answers: List[str] = [] - self.answer: str = '' - self.letter_map = { - '2': 'abc', - '3': 'def', - '4': 'ghi', - '5': 'jkl', - '6': 'mno', - '7': 'pqrs', - '8': 'tuv', - '9': 'wxyz' - } - - def letterCombinations(self, digits: str) -> List[str]: - self.answers.clear() - if not digits: return [] + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + self.result = [] + self.s = "" + + def backtracking(self, digits, index): + if index == len(digits): + self.result.append(self.s) + return + digit = int(digits[index]) # 将索引处的数字转换为整数 + letters = self.letterMap[digit] # 获取对应的字符集 + for i in range(len(letters)): + self.s += letters[i] # 处理字符 + self.backtracking(digits, index + 1) # 递归调用,注意索引加1,处理下一个数字 + self.s = self.s[:-1] # 回溯,删除最后添加的字符 + + def letterCombinations(self, digits): + if len(digits) == 0: + return self.result self.backtracking(digits, 0) - return self.answers - - def backtracking(self, digits: str, index: int) -> None: - # 回溯函数没有返回值 - # Base Case - if index == len(digits): # 当遍历穷尽后的下一层时 - self.answers.append(self.answer) - return - # 单层递归逻辑 - letters: str = self.letter_map[digits[index]] - for letter in letters: - self.answer += letter # 处理 - self.backtracking(digits, index + 1) # 递归至下一层 - self.answer = self.answer[:-1] # 回溯 + return self.result + ``` -**回溯简化** +回溯精简(版本一) ```python class Solution: def __init__(self): - self.answers: List[str] = [] - self.letter_map = { - '2': 'abc', - '3': 'def', - '4': 'ghi', - '5': 'jkl', - '6': 'mno', - '7': 'pqrs', - '8': 'tuv', - '9': 'wxyz' - } - - def letterCombinations(self, digits: str) -> List[str]: - self.answers.clear() - if not digits: return [] - self.backtracking(digits, 0, '') - return self.answers + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + self.result = [] - def backtracking(self, digits: str, index: int, answer: str) -> None: - # 回溯函数没有返回值 - # Base Case - if index == len(digits): # 当遍历穷尽后的下一层时 - self.answers.append(answer) - return - # 单层递归逻辑 - letters: str = self.letter_map[digits[index]] + def getCombinations(self, digits, index, s): + if index == len(digits): + self.result.append(s) + return + digit = int(digits[index]) + letters = self.letterMap[digit] for letter in letters: - self.backtracking(digits, index + 1, answer + letter) # 递归至下一层 + 回溯 + self.getCombinations(digits, index + 1, s + letter) + + def letterCombinations(self, digits): + if len(digits) == 0: + return self.result + self.getCombinations(digits, 0, "") + return self.result + ``` -**使用itertools** +回溯精简(版本二) ```python class Solution: - def letterCombinations(self, digits: str) -> List[str]: - import itertools - if not digits: - return list() - - phoneMap = { - "2": "abc", - "3": "def", - "4": "ghi", - "5": "jkl", - "6": "mno", - "7": "pqrs", - "8": "tuv", - "9": "wxyz", - } + def __init__(self): + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + + def getCombinations(self, digits, index, s, result): + if index == len(digits): + result.append(s) + return + digit = int(digits[index]) + letters = self.letterMap[digit] + for letter in letters: + self.getCombinations(digits, index + 1, s + letter, result) + + def letterCombinations(self, digits): + result = [] + if len(digits) == 0: + return result + self.getCombinations(digits, 0, "", result) + return result + - groups = (phoneMap[digit] for digit in digits) - return ["".join(combination) for combination in itertools.product(*groups)] ``` +回溯优化使用列表 +```python +class Solution: + def __init__(self): + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + + def getCombinations(self, digits, index, path, result): + if index == len(digits): + result.append(''.join(path)) + return + digit = int(digits[index]) + letters = self.letterMap[digit] + for letter in letters: + path.append(letter) + self.getCombinations(digits, index + 1, path, result) + path.pop() + + def letterCombinations(self, digits): + result = [] + if len(digits) == 0: + return result + self.getCombinations(digits, 0, [], result) + return result + + + +``` + + ## Go diff --git a/problems/0019.删除链表的倒数第N个节点.md b/problems/0019.删除链表的倒数第N个节点.md index c6f5bfc7..84eac96b 100644 --- a/problems/0019.删除链表的倒数第N个节点.md +++ b/problems/0019.删除链表的倒数第N个节点.md @@ -412,6 +412,28 @@ struct ListNode* removeNthFromEnd(struct ListNode* head, int n) { ``` +C#: +```csharp +public class Solution { + public ListNode RemoveNthFromEnd(ListNode head, int n) { + ListNode dummpHead = new ListNode(0); + dummpHead.next = head; + var fastNode = dummpHead; + var slowNode = dummpHead; + while(n-- != 0 && fastNode != null) + { + fastNode = fastNode.next; + } + while(fastNode.next != null) + { + fastNode = fastNode.next; + slowNode = slowNode.next; + } + slowNode.next = slowNode.next.next; + return dummpHead.next; + } +} +```
diff --git a/problems/0072.编辑距离.md b/problems/0072.编辑距离.md
index cc4ab00c..703e8913 100644
--- a/problems/0072.编辑距离.md
+++ b/problems/0072.编辑距离.md
@@ -40,6 +40,8 @@ exection -> execution (插入 'u')
* 0 <= word1.length, word2.length <= 500
* word1 和 word2 由小写英文字母组成
+# 算法公开课
+**《代码随想录》算法视频公开课:[动态规划终极绝杀! LeetCode:72.编辑距离](https://www.bilibili.com/video/BV1we4y157wB/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
## 思路
diff --git a/problems/0077.组合.md b/problems/0077.组合.md
index 3f222a17..444e15ce 100644
--- a/problems/0077.组合.md
+++ b/problems/0077.组合.md
@@ -381,68 +381,42 @@ class Solution {
```
### Python
-
+未剪枝优化
```python
-class Solution(object):
- def combine(self, n, k):
- """
- :type n: int
- :type k: int
- :rtype: List[List[int]]
- """
- result = []
- path = []
- def backtracking(n, k, startidx):
- if len(path) == k:
- result.append(path[:])
- return
-
- # 剪枝, 最后k - len(path)个节点直接构造结果,无需递归
- last_startidx = n - (k - len(path)) + 1
-
- for x in range(startidx, last_startidx + 1):
- path.append(x)
- backtracking(n, k, x + 1) # 递归
- path.pop() # 回溯
-
- backtracking(n, k, 1)
+class Solution:
+ def combine(self, n: int, k: int) -> List[List[int]]:
+ result = [] # 存放结果集
+ self.backtracking(n, k, 1, [], result)
return result
+ def backtracking(self, n, k, startIndex, path, result):
+ if len(path) == k:
+ result.append(path[:])
+ return
+ for i in range(startIndex, n + 1): # 需要优化的地方
+ path.append(i) # 处理节点
+ self.backtracking(n, k, i + 1, path, result)
+ path.pop() # 回溯,撤销处理的节点
+
```
+
+剪枝优化:
+
```python
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
- res = []
- path = []
- def backtrack(n, k, StartIndex):
- if len(path) == k:
- res.append(path[:])
- return
- for i in range(StartIndex, n + 1):
- path.append(i)
- backtrack(n, k, i+1)
- path.pop()
- backtrack(n, k, 1)
- return res
-```
+ result = [] # 存放结果集
+ self.backtracking(n, k, 1, [], result)
+ return result
+ def backtracking(self, n, k, startIndex, path, result):
+ if len(path) == k:
+ result.append(path[:])
+ return
+ for i in range(startIndex, n - (k - len(path)) + 2): # 优化的地方
+ path.append(i) # 处理节点
+ self.backtracking(n, k, i + 1, path, result)
+ path.pop() # 回溯,撤销处理的节点
-剪枝:
-
-```python
-class Solution:
- def combine(self, n: int, k: int) -> List[List[int]]:
- res=[] #存放符合条件结果的集合
- path=[] #用来存放符合条件结果
- def backtrack(n,k,startIndex):
- if len(path) == k:
- res.append(path[:])
- return
- for i in range(startIndex,n-(k-len(path))+2): #优化的地方
- path.append(i) #处理节点
- backtrack(n,k,i+1) #递归
- path.pop() #回溯,撤销处理的节点
- backtrack(n,k,1)
- return res
```
### Go
diff --git a/problems/0077.组合优化.md b/problems/0077.组合优化.md
index 0c816bc1..3926d006 100644
--- a/problems/0077.组合优化.md
+++ b/problems/0077.组合优化.md
@@ -183,18 +183,21 @@ Python:
```python
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
- res=[] #存放符合条件结果的集合
- path=[] #用来存放符合条件结果
- def backtrack(n,k,startIndex):
- if len(path) == k:
- res.append(path[:])
- return
- for i in range(startIndex,n-(k-len(path))+2): #优化的地方
- path.append(i) #处理节点
- backtrack(n,k,i+1) #递归
- path.pop() #回溯,撤销处理的节点
- backtrack(n,k,1)
- return res
+ result = [] # 存放结果集
+ self.backtracking(n, k, 1, [], result)
+ return result
+ def backtracking(self, n, k, startIndex, path, result):
+ if len(path) == k:
+ result.append(path[:])
+ return
+ for i in range(startIndex, n - (k - len(path)) + 2): # 优化的地方
+ path.append(i) # 处理节点
+ self.backtracking(n, k, i + 1, path, result)
+ path.pop() # 回溯,撤销处理的节点
+
+
+
+
```
Go:
```Go
diff --git a/problems/0078.子集.md b/problems/0078.子集.md
index 07418fbc..21009f6a 100644
--- a/problems/0078.子集.md
+++ b/problems/0078.子集.md
@@ -208,28 +208,20 @@ class Solution {
## Python
```python
class Solution:
- def __init__(self):
- self.path: List[int] = []
- self.paths: List[List[int]] = []
+ def subsets(self, nums):
+ result = []
+ path = []
+ self.backtracking(nums, 0, path, result)
+ return result
- def subsets(self, nums: List[int]) -> List[List[int]]:
- self.paths.clear()
- self.path.clear()
- self.backtracking(nums, 0)
- return self.paths
-
- def backtracking(self, nums: List[int], start_index: int) -> None:
- # 收集子集,要先于终止判断
- self.paths.append(self.path[:])
- # Base Case
- if start_index == len(nums):
- return
-
- # 单层递归逻辑
- for i in range(start_index, len(nums)):
- self.path.append(nums[i])
- self.backtracking(nums, i+1)
- self.path.pop() # 回溯
+ def backtracking(self, nums, startIndex, path, result):
+ result.append(path[:]) # 收集子集,要放在终止添加的上面,否则会漏掉自己
+ # if startIndex >= len(nums): # 终止条件可以不加
+ # return
+ for i in range(startIndex, len(nums)):
+ path.append(nums[i])
+ self.backtracking(nums, i + 1, path, result)
+ path.pop()
```
## Go
diff --git a/problems/0090.子集II.md b/problems/0090.子集II.md
index 63f75d29..3238ee52 100644
--- a/problems/0090.子集II.md
+++ b/problems/0090.子集II.md
@@ -238,86 +238,84 @@ class Solution {
}
```
-### Python
-```python
-class Solution:
- def __init__(self):
- self.paths = []
- self.path = []
- def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
- nums.sort()
- self.backtracking(nums, 0)
- return self.paths
-
- def backtracking(self, nums: List[int], start_index: int) -> None:
- # ps.空集合仍符合要求
- self.paths.append(self.path[:])
- # Base Case
- if start_index == len(nums):
- return
-
- # 单层递归逻辑
- for i in range(start_index, len(nums)):
- if i > start_index and nums[i] == nums[i-1]:
- # 当前后元素值相同时,跳入下一个循环,去重
- continue
- self.path.append(nums[i])
- self.backtracking(nums, i+1)
- self.path.pop()
-```
#### Python3
-不使用used数组
+回溯 利用used数组去重
```python
class Solution:
- def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
- res = []
- path = []
- nums.sort() # 去重需要先对数组进行排序
-
- def backtracking(nums, startIndex):
- # 终止条件
- res.append(path[:])
- if startIndex == len(nums):
- return
-
- # for循环
- for i in range(startIndex, len(nums)):
- # 数层去重
- if i > startIndex and nums[i] == nums[i-1]: # 去重
- continue
- path.append(nums[i])
- backtracking(nums, i+1)
- path.pop()
-
- backtracking(nums, 0)
- return res
-```
-
-使用used数组
-```python
-class Solution:
- def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
+ def subsetsWithDup(self, nums):
result = []
path = []
- nums.sort()
- used = [0] * len(nums)
- def backtrack(nums, startIdx):
- result.append(path[:])
- for i in range(startIdx, len(nums)):
- if i > startIdx and nums[i] == nums[i-1] and used[i-1] == 0:
- continue
- used[i] = 1
- path.append(nums[i])
- backtrack(nums, i+1)
- path.pop()
- used[i] = 0
- backtrack(nums, 0)
+ used = [False] * len(nums)
+ nums.sort() # 去重需要排序
+ self.backtracking(nums, 0, used, path, result)
return result
+
+ def backtracking(self, nums, startIndex, used, path, result):
+ result.append(path[:]) # 收集子集
+ for i in range(startIndex, len(nums)):
+ # used[i - 1] == True,说明同一树枝 nums[i - 1] 使用过
+ # used[i - 1] == False,说明同一树层 nums[i - 1] 使用过
+ # 而我们要对同一树层使用过的元素进行跳过
+ if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]:
+ continue
+ path.append(nums[i])
+ used[i] = True
+ self.backtracking(nums, i + 1, used, path, result)
+ used[i] = False
+ path.pop()
+
```
+回溯 利用集合去重
+
+```python
+class Solution:
+ def subsetsWithDup(self, nums):
+ result = []
+ path = []
+ nums.sort() # 去重需要排序
+ self.backtracking(nums, 0, path, result)
+ return result
+
+ def backtracking(self, nums, startIndex, path, result):
+ result.append(path[:]) # 收集子集
+ uset = set()
+ for i in range(startIndex, len(nums)):
+ if nums[i] in uset:
+ continue
+ uset.add(nums[i])
+ path.append(nums[i])
+ self.backtracking(nums, i + 1, path, result)
+ path.pop()
+
+```
+
+回溯 利用递归的时候下一个startIndex是i+1而不是0去重
+
+```python
+class Solution:
+ def subsetsWithDup(self, nums):
+ result = []
+ path = []
+ nums.sort() # 去重需要排序
+ self.backtracking(nums, 0, path, result)
+ return result
+
+ def backtracking(self, nums, startIndex, path, result):
+ result.append(path[:]) # 收集子集
+ for i in range(startIndex, len(nums)):
+ # 而我们要对同一树层使用过的元素进行跳过
+ if i > startIndex and nums[i] == nums[i - 1]:
+ continue
+ path.append(nums[i])
+ self.backtracking(nums, i + 1, path, result)
+ path.pop()
+
+
+```
### Go
```Go
diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md
index 161fb96e..55e57dde 100644
--- a/problems/0093.复原IP地址.md
+++ b/problems/0093.复原IP地址.md
@@ -316,6 +316,47 @@ class Solution {
return true;
}
}
+//方法一:但使用stringBuilder,故优化时间、空间复杂度,因为向字符串插入字符时无需复制整个字符串,从而减少了操作的时间复杂度,也不用开新空间存subString,从而减少了空间复杂度。
+class Solution {
+ List
diff --git a/problems/0122.买卖股票的最佳时机II.md b/problems/0122.买卖股票的最佳时机II.md
index 0d8ad608..89c654fa 100644
--- a/problems/0122.买卖股票的最佳时机II.md
+++ b/problems/0122.买卖股票的最佳时机II.md
@@ -322,13 +322,10 @@ function maxProfit(prices: number[]): number {
```Rust
impl Solution {
- fn max(a: i32, b: i32) -> i32 {
- if a > b { a } else { b }
- }
pub fn max_profit(prices: Vec
diff --git a/problems/0123.买卖股票的最佳时机III.md b/problems/0123.买卖股票的最佳时机III.md
index af6870d4..6ac9a576 100644
--- a/problems/0123.买卖股票的最佳时机III.md
+++ b/problems/0123.买卖股票的最佳时机III.md
@@ -242,9 +242,9 @@ class Solution {
for (int i = 1; i < len; i++) {
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
- dp[i][2] = Math.max(dp[i - 1][2], dp[i][1] + prices[i]);
- dp[i][3] = Math.max(dp[i - 1][3], dp[i][2] - prices[i]);
- dp[i][4] = Math.max(dp[i - 1][4], dp[i][3] + prices[i]);
+ dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
+ dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
+ dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
}
return dp[len - 1][4];
diff --git a/problems/0130.被围绕的区域.md b/problems/0130.被围绕的区域.md
index 7afa71b4..abb68e19 100644
--- a/problems/0130.被围绕的区域.md
+++ b/problems/0130.被围绕的区域.md
@@ -18,7 +18,7 @@
## 思路
-这道题目和1020. 飞地的数量正好反过来了,[1020. 飞地的数量](https://leetcode.cn/problems/number-of-enclaves/solution/by-carlsun-2-7lt9/)是求 地图中间的空格数,而本题是要把地图中间的'O'都改成'X'。
+这道题目和1020. 飞地的数量正好反过来了,[1020. 飞地的数量](https://programmercarl.com/1020.%E9%A3%9E%E5%9C%B0%E7%9A%84%E6%95%B0%E9%87%8F.html)是求 地图中间的空格数,而本题是要把地图中间的'O'都改成'X'。
那么两题在思路上也是差不多的。
diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md
index dfec7853..636cf59c 100644
--- a/problems/0131.分割回文串.md
+++ b/problems/0131.分割回文串.md
@@ -352,12 +352,9 @@ class Solution {
```
## Python
-**回溯+正反序判断回文串**
+回溯 基本版
```python
class Solution:
- def __init__(self):
- self.paths = []
- self.path = []
def partition(self, s: str) -> List[List[str]]:
'''
@@ -366,52 +363,14 @@ class Solution:
当切割线迭代至字符串末尾,说明找到一种方法
类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线)
'''
- self.path.clear()
- self.paths.clear()
- self.backtracking(s, 0)
- return self.paths
+ result = []
+ self.backtracking(s, 0, [], result)
+ return result
- def backtracking(self, s: str, start_index: int) -> None:
+ def backtracking(self, s, start_index, path, result ):
# Base Case
- if start_index >= len(s):
- self.paths.append(self.path[:])
- return
-
- # 单层递归逻辑
- for i in range(start_index, len(s)):
- # 此次比其他组合题目多了一步判断:
- # 判断被截取的这一段子串([start_index, i])是否为回文串
- temp = s[start_index:i+1]
- if temp == temp[::-1]: # 若反序和正序相同,意味着这是回文串
- self.path.append(temp)
- self.backtracking(s, i+1) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
- self.path.pop()
- else:
- continue
-```
-**回溯+函数判断回文串**
-```python
-class Solution:
- def __init__(self):
- self.paths = []
- self.path = []
-
- def partition(self, s: str) -> List[List[str]]:
- '''
- 递归用于纵向遍历
- for循环用于横向遍历
- 当切割线迭代至字符串末尾,说明找到一种方法
- 类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线)
- '''
- self.path.clear()
- self.paths.clear()
- self.backtracking(s, 0)
- return self.paths
-
- def backtracking(self, s: str, start_index: int) -> None:
- # Base Case
- if start_index >= len(s):
- self.paths.append(self.path[:])
+ if start_index == len(s):
+ result.append(path[:])
return
# 单层递归逻辑
@@ -419,11 +378,10 @@ class Solution:
# 此次比其他组合题目多了一步判断:
# 判断被截取的这一段子串([start_index, i])是否为回文串
if self.is_palindrome(s, start_index, i):
- self.path.append(s[start_index:i+1])
- self.backtracking(s, i+1) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
- self.path.pop() # 回溯
- else:
- continue
+ path.append(s[start_index:i+1])
+ self.backtracking(s, i+1, path, result) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
+ path.pop() # 回溯
+
def is_palindrome(self, s: str, start: int, end: int) -> bool:
i: int = start
@@ -433,9 +391,88 @@ class Solution:
return False
i += 1
j -= 1
- return True
+ return True
```
+回溯+优化判定回文函数
+```python
+class Solution:
+ def partition(self, s: str) -> List[List[str]]:
+ result = []
+ self.backtracking(s, 0, [], result)
+ return result
+
+ def backtracking(self, s, start_index, path, result ):
+ # Base Case
+ if start_index == len(s):
+ result.append(path[:])
+ return
+
+ # 单层递归逻辑
+ for i in range(start_index, len(s)):
+ # 若反序和正序相同,意味着这是回文串
+ if s[start_index: i + 1] == s[start_index: i + 1][::-1]:
+ path.append(s[start_index:i+1])
+ self.backtracking(s, i+1, path, result) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
+ path.pop() # 回溯
+
+```
+回溯+高效判断回文子串
+```python
+class Solution:
+ def partition(self, s: str) -> List[List[str]]:
+ result = []
+ isPalindrome = [[False] * len(s) for _ in range(len(s))] # 初始化isPalindrome矩阵
+ self.computePalindrome(s, isPalindrome)
+ self.backtracking(s, 0, [], result, isPalindrome)
+ return result
+
+ def backtracking(self, s, startIndex, path, result, isPalindrome):
+ if startIndex >= len(s):
+ result.append(path[:])
+ return
+
+ for i in range(startIndex, len(s)):
+ if isPalindrome[startIndex][i]: # 是回文子串
+ substring = s[startIndex:i + 1]
+ path.append(substring)
+ self.backtracking(s, i + 1, path, result, isPalindrome) # 寻找i+1为起始位置的子串
+ path.pop() # 回溯过程,弹出本次已经填在的子串
+
+ def computePalindrome(self, s, isPalindrome):
+ for i in range(len(s) - 1, -1, -1): # 需要倒序计算,保证在i行时,i+1行已经计算好了
+ for j in range(i, len(s)):
+ if j == i:
+ isPalindrome[i][j] = True
+ elif j - i == 1:
+ isPalindrome[i][j] = (s[i] == s[j])
+ else:
+ isPalindrome[i][j] = (s[i] == s[j] and isPalindrome[i+1][j-1])
+```
+回溯+使用all函数判断回文子串
+```python
+class Solution:
+ def partition(self, s: str) -> List[List[str]]:
+ result = []
+ self.partition_helper(s, 0, [], result)
+ return result
+
+ def partition_helper(self, s, start_index, path, result):
+ if start_index == len(s):
+ result.append(path[:])
+ return
+
+ for i in range(start_index + 1, len(s) + 1):
+ sub = s[start_index:i]
+ if self.isPalindrome(sub):
+ path.append(sub)
+ self.partition_helper(s, i, path, result)
+ path.pop()
+
+ def isPalindrome(self, s):
+ return all(s[i] == s[len(s) - 1 - i] for i in range(len(s) // 2))
+
+```
## Go
```go
var (
diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md
index f432bf0b..ad9acfbc 100644
--- a/problems/0134.加油站.md
+++ b/problems/0134.加油站.md
@@ -249,44 +249,74 @@ class Solution {
```
### Python
+暴力法
```python
-# 解法1
-class Solution:
- def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
- n = len(gas)
- cur_sum = 0
- min_sum = float('inf')
-
- for i in range(n):
- cur_sum += gas[i] - cost[i]
- min_sum = min(min_sum, cur_sum)
-
- if cur_sum < 0: return -1
- if min_sum >= 0: return 0
-
- for j in range(n - 1, 0, -1):
- min_sum += gas[j] - cost[j]
- if min_sum >= 0:
- return j
-
- return -1
-```
-```python
-# 解法2
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
- start = 0
- curSum = 0
- totalSum = 0
+ for i in range(len(cost)):
+ rest = gas[i] - cost[i] # 记录剩余油量
+ index = (i + 1) % len(cost) # 下一个加油站的索引
+
+ while rest > 0 and index != i: # 模拟以i为起点行驶一圈(如果有rest==0,那么答案就不唯一了)
+ rest += gas[index] - cost[index] # 更新剩余油量
+ index = (index + 1) % len(cost) # 更新下一个加油站的索引
+
+ if rest >= 0 and index == i: # 如果以i为起点跑一圈,剩余油量>=0,并且回到起始位置
+ return i # 返回起始位置i
+
+ return -1 # 所有起始位置都无法环绕一圈,返回-1
+
+```
+贪心(版本一)
+```python
+class Solution:
+ def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
+ curSum = 0 # 当前累计的剩余油量
+ minFuel = float('inf') # 从起点出发,油箱里的油量最小值
+
+ for i in range(len(gas)):
+ rest = gas[i] - cost[i]
+ curSum += rest
+ if curSum < minFuel:
+ minFuel = curSum
+
+ if curSum < 0:
+ return -1 # 情况1:整个行程的总消耗大于总供给,无法完成一圈
+
+ if minFuel >= 0:
+ return 0 # 情况2:从起点出发到任何一个加油站时油箱的剩余油量都不会小于0,可以从起点出发完成一圈
+
+ for i in range(len(gas) - 1, -1, -1):
+ rest = gas[i] - cost[i]
+ minFuel += rest
+ if minFuel >= 0:
+ return i # 情况3:找到一个位置使得从该位置出发油箱的剩余油量不会小于0,返回该位置的索引
+
+ return -1 # 无法完成一圈
+
+```
+贪心(版本二)
+```python
+class Solution:
+ def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
+ curSum = 0 # 当前累计的剩余油量
+ totalSum = 0 # 总剩余油量
+ start = 0 # 起始位置
+
for i in range(len(gas)):
curSum += gas[i] - cost[i]
totalSum += gas[i] - cost[i]
- if curSum < 0:
- curSum = 0
- start = i + 1
- if totalSum < 0: return -1
+
+ if curSum < 0: # 当前累计剩余油量curSum小于0
+ start = i + 1 # 起始位置更新为i+1
+ curSum = 0 # curSum重新从0开始累计
+
+ if totalSum < 0:
+ return -1 # 总剩余油量totalSum小于0,说明无法环绕一圈
return start
+
+
```
### Go
diff --git a/problems/0135.分发糖果.md b/problems/0135.分发糖果.md
index 1ba1563f..cf3ccc8e 100644
--- a/problems/0135.分发糖果.md
+++ b/problems/0135.分发糖果.md
@@ -178,13 +178,21 @@ class Solution {
class Solution:
def candy(self, ratings: List[int]) -> int:
candyVec = [1] * len(ratings)
+
+ # 从前向后遍历,处理右侧比左侧评分高的情况
for i in range(1, len(ratings)):
if ratings[i] > ratings[i - 1]:
candyVec[i] = candyVec[i - 1] + 1
- for j in range(len(ratings) - 2, -1, -1):
- if ratings[j] > ratings[j + 1]:
- candyVec[j] = max(candyVec[j], candyVec[j + 1] + 1)
- return sum(candyVec)
+
+ # 从后向前遍历,处理左侧比右侧评分高的情况
+ for i in range(len(ratings) - 2, -1, -1):
+ if ratings[i] > ratings[i + 1]:
+ candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1)
+
+ # 统计结果
+ result = sum(candyVec)
+ return result
+
```
### Go
diff --git a/problems/0139.单词拆分.md b/problems/0139.单词拆分.md
index 230942ef..0d88ba36 100644
--- a/problems/0139.单词拆分.md
+++ b/problems/0139.单词拆分.md
@@ -337,10 +337,53 @@ class Solution {
Python:
+回溯
+```python
+class Solution:
+ def backtracking(self, s: str, wordSet: set[str], startIndex: int) -> bool:
+ # 边界情况:已经遍历到字符串末尾,返回True
+ if startIndex >= len(s):
+ return True
+
+ # 遍历所有可能的拆分位置
+ for i in range(startIndex, len(s)):
+ word = s[startIndex:i + 1] # 截取子串
+ if word in wordSet and self.backtracking(s, wordSet, i + 1):
+ # 如果截取的子串在字典中,并且后续部分也可以被拆分成单词,返回True
+ return True
+
+ # 无法进行有效拆分,返回False
+ return False
+
+ def wordBreak(self, s: str, wordDict: List[str]) -> bool:
+ wordSet = set(wordDict) # 转换为哈希集合,提高查找效率
+ return self.backtracking(s, wordSet, 0)
+
+```
+DP(版本一)
+```python
+class Solution:
+ def wordBreak(self, s: str, wordDict: List[str]) -> bool:
+ wordSet = set(wordDict)
+ n = len(s)
+ dp = [False] * (n + 1) # dp[i] 表示字符串的前 i 个字符是否可以被拆分成单词
+ dp[0] = True # 初始状态,空字符串可以被拆分成单词
+
+ for i in range(1, n + 1): # 遍历背包
+ for j in range(i): # 遍历单词
+ if dp[j] and s[j:i] in wordSet:
+ dp[i] = True # 如果 s[0:j] 可以被拆分成单词,并且 s[j:i] 在单词集合中存在,则 s[0:i] 可以被拆分成单词
+ break
+
+ return dp[n]
+
+
+```
+DP(版本二)
+
```python
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
- '''排列'''
dp = [False]*(len(s) + 1)
dp[0] = True
# 遍历背包
@@ -351,17 +394,6 @@ class Solution:
dp[j] = dp[j] or (dp[j - len(word)] and word == s[j - len(word):j])
return dp[len(s)]
```
-```python
-class Solution: # 和视频中写法一致(和最上面C++写法一致)
- def wordBreak(self, s: str, wordDict: List[str]) -> bool:
- dp = [False]*(len(s)+1)
- dp[0]=True
- for j in range(1,len(s)+1):
- for i in range(j):
- word = s[i:j]
- if word in wordDict and dp[i]: dp[j]=True
- return dp[-1]
-```
@@ -464,7 +496,24 @@ function wordBreak(s: string, wordDict: string[]): boolean {
};
```
+Rust:
+```rust
+impl Solution {
+ pub fn word_break(s: String, word_dict: Vec
diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md
index e80a715a..f87d2cd9 100644
--- a/problems/0142.环形链表II.md
+++ b/problems/0142.环形链表II.md
@@ -437,6 +437,34 @@ object Solution {
}
```
+C#:
+```CSharp
+public class Solution
+{
+ public ListNode DetectCycle(ListNode head)
+ {
+ ListNode fast = head;
+ ListNode slow = head;
+ while (fast != null && fast.next != null)
+ {
+ slow = slow.next;
+ fast = fast.next.next;
+ if (fast == slow)
+ {
+ fast = head;
+ while (fast != slow)
+ {
+ fast = fast.next;
+ slow = slow.next;
+ }
+ return fast;
+ }
+ }
+ return null;
+ }
+}
+```
+
diff --git a/problems/0200.岛屿数量.深搜版.md b/problems/0200.岛屿数量.深搜版.md
index 8680d2b1..6d42162a 100644
--- a/problems/0200.岛屿数量.深搜版.md
+++ b/problems/0200.岛屿数量.深搜版.md
@@ -41,7 +41,7 @@
### 深度优先搜索
-以下代码使用dfs实现,如果对dfs不太了解的话,建议先看这篇题解:[797.所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/solution/by-carlsun-2-66pf/),
+以下代码使用dfs实现,如果对dfs不太了解的话,建议先看这篇题解:[797.所有可能的路径](https://programmercarl.com/0797.%E6%89%80%E6%9C%89%E5%8F%AF%E8%83%BD%E7%9A%84%E8%B7%AF%E5%BE%84.html),
C++代码如下:
diff --git a/problems/0203.移除链表元素.md b/problems/0203.移除链表元素.md
index 6a0de282..f8751658 100644
--- a/problems/0203.移除链表元素.md
+++ b/problems/0203.移除链表元素.md
@@ -596,6 +596,41 @@ class Solution {
}
```
+C#
+```CSharp
+/**
+ * Definition for singly-linked list.
+ * public class ListNode {
+ * public int val;
+ * public ListNode next;
+ * public ListNode(int val=0, ListNode next=null) {
+ * this.val = val;
+ * this.next = next;
+ * }
+ * }
+ */
+public class Solution
+{
+ public ListNode RemoveElements(ListNode head, int val)
+ {
+ ListNode dummyHead = new ListNode(0,head);
+ ListNode temp = dummyHead;
+ while(temp.next != null)
+ {
+ if(temp.next.val == val)
+ {
+ temp.next = temp.next.next;
+ }
+ else
+ {
+ temp = temp.next;
+ }
+ }
+ return dummyHead.next;
+ }
+}
+```
+
diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md
index f08d77ea..319b2eba 100644
--- a/problems/0216.组合总和III.md
+++ b/problems/0216.组合总和III.md
@@ -362,28 +362,25 @@ class Solution {
```py
class Solution:
- def __init__(self):
- self.res = []
- self.sum_now = 0
- self.path = []
+ def combinationSum3(self, k: int, n: int) -> List[List[int]]:
+ result = [] # 存放结果集
+ self.backtracking(n, k, 0, 1, [], result)
+ return result
- def combinationSum3(self, k: int, n: int) -> [[int]]:
- self.backtracking(k, n, 1)
- return self.res
+ def backtracking(self, targetSum, k, currentSum, startIndex, path, result):
+ if currentSum > targetSum: # 剪枝操作
+ return # 如果path的长度等于k但currentSum不等于targetSum,则直接返回
+ if len(path) == k:
+ if currentSum == targetSum:
+ result.append(path[:])
+ return
+ for i in range(startIndex, 9 - (k - len(path)) + 2): # 剪枝
+ currentSum += i # 处理
+ path.append(i) # 处理
+ self.backtracking(targetSum, k, currentSum, i + 1, path, result) # 注意i+1调整startIndex
+ currentSum -= i # 回溯
+ path.pop() # 回溯
- def backtracking(self, k: int, n: int, start_num: int):
- if self.sum_now > n: # 剪枝
- return
- if len(self.path) == k: # len(path)==k时不管sum是否等于n都会返回
- if self.sum_now == n:
- self.res.append(self.path[:])
- return
- for i in range(start_num, 10 - (k - len(self.path)) + 1):
- self.path.append(i)
- self.sum_now += i
- self.backtracking(k, n, i + 1)
- self.path.pop()
- self.sum_now -= i
```
## Go
diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md
index 41a1ede2..94c79404 100644
--- a/problems/0225.用队列实现栈.md
+++ b/problems/0225.用队列实现栈.md
@@ -367,7 +367,7 @@ class MyStack {
```
优化,使用一个 Queue 实现,但用卡哥的逻辑实现
-```
+```Java
class MyStack {
Queue
diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md
index 01d34949..c58c3bf6 100644
--- a/problems/0300.最长上升子序列.md
+++ b/problems/0300.最长上升子序列.md
@@ -31,6 +31,11 @@
* 1 <= nums.length <= 2500
* -10^4 <= nums[i] <= 104
+## 算法公开课
+
+**《代码随想录》算法视频公开课:[动态规划之子序列问题,元素不连续!| LeetCode:300.最长递增子序列](https://www.bilibili.com/video/BV1ng411J7xP),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
+
+
## 思路
首先通过本题大家要明确什么是子序列,“子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序”。
@@ -143,6 +148,8 @@ class Solution {
```
Python:
+
+DP
```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
@@ -157,7 +164,31 @@ class Solution:
result = max(result, dp[i]) #取长的子序列
return result
```
+贪心
+```python
+class Solution:
+ def lengthOfLIS(self, nums: List[int]) -> int:
+ if len(nums) <= 1:
+ return len(nums)
+
+ tails = [nums[0]] # 存储递增子序列的尾部元素
+ for num in nums[1:]:
+ if num > tails[-1]:
+ tails.append(num) # 如果当前元素大于递增子序列的最后一个元素,直接加入到子序列末尾
+ else:
+ # 使用二分查找找到当前元素在递增子序列中的位置,并替换对应位置的元素
+ left, right = 0, len(tails) - 1
+ while left < right:
+ mid = (left + right) // 2
+ if tails[mid] < num:
+ left = mid + 1
+ else:
+ right = mid
+ tails[left] = num
+
+ return len(tails) # 返回递增子序列的长度
+```
Go:
```go
// 动态规划求解
diff --git a/problems/0309.最佳买卖股票时机含冷冻期.md b/problems/0309.最佳买卖股票时机含冷冻期.md
index a56d9b84..67f6d564 100644
--- a/problems/0309.最佳买卖股票时机含冷冻期.md
+++ b/problems/0309.最佳买卖股票时机含冷冻期.md
@@ -248,23 +248,51 @@ class Solution {
```
Python:
-
+版本一
```python
+from typing import List
+
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n == 0:
return 0
- dp = [[0] * 4 for _ in range(n)]
- dp[0][0] = -prices[0] #持股票
+ dp = [[0] * 4 for _ in range(n)] # 创建动态规划数组,4个状态分别表示持有股票、不持有股票且处于冷冻期、不持有股票且不处于冷冻期、不持有股票且当天卖出后处于冷冻期
+ dp[0][0] = -prices[0] # 初始状态:第一天持有股票的最大利润为买入股票的价格
for i in range(1, n):
- dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i])
- dp[i][1] = max(dp[i-1][1], dp[i-1][3])
- dp[i][2] = dp[i-1][0] + prices[i]
- dp[i][3] = dp[i-1][2]
- return max(dp[n-1][3], dp[n-1][1], dp[n-1][2])
-```
+ dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i]) # 当前持有股票的最大利润等于前一天持有股票的最大利润或者前一天不持有股票且不处于冷冻期的最大利润减去当前股票的价格
+ dp[i][1] = max(dp[i-1][1], dp[i-1][3]) # 当前不持有股票且处于冷冻期的最大利润等于前一天持有股票的最大利润加上当前股票的价格
+ dp[i][2] = dp[i-1][0] + prices[i] # 当前不持有股票且不处于冷冻期的最大利润等于前一天不持有股票的最大利润或者前一天处于冷冻期的最大利润
+ dp[i][3] = dp[i-1][2] # 当前不持有股票且当天卖出后处于冷冻期的最大利润等于前一天不持有股票且不处于冷冻期的最大利润
+ return max(dp[n-1][3], dp[n-1][1], dp[n-1][2]) # 返回最后一天不持有股票的最大利润
+```
+版本二
+```python
+class Solution:
+ def maxProfit(self, prices: List[int]) -> int:
+ n = len(prices)
+ if n < 2:
+ return 0
+
+ # 定义三种状态的动态规划数组
+ dp = [[0] * 3 for _ in range(n)]
+ dp[0][0] = -prices[0] # 持有股票的最大利润
+ dp[0][1] = 0 # 不持有股票,且处于冷冻期的最大利润
+ dp[0][2] = 0 # 不持有股票,不处于冷冻期的最大利润
+
+ for i in range(1, n):
+ # 当前持有股票的最大利润等于前一天持有股票的最大利润或者前一天不持有股票且不处于冷冻期的最大利润减去当前股票的价格
+ dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])
+ # 当前不持有股票且处于冷冻期的最大利润等于前一天持有股票的最大利润加上当前股票的价格
+ dp[i][1] = dp[i-1][0] + prices[i]
+ # 当前不持有股票且不处于冷冻期的最大利润等于前一天不持有股票的最大利润或者前一天处于冷冻期的最大利润
+ dp[i][2] = max(dp[i-1][2], dp[i-1][1])
+
+ # 返回最后一天不持有股票的最大利润
+ return max(dp[-1][1], dp[-1][2])
+
+```
Go:
```go
// 最佳买卖股票时机含冷冻期 动态规划
diff --git a/problems/0322.零钱兑换.md b/problems/0322.零钱兑换.md
index 0e3947da..1f3f4df2 100644
--- a/problems/0322.零钱兑换.md
+++ b/problems/0322.零钱兑换.md
@@ -217,36 +217,76 @@ class Solution {
Python:
+
+先遍历物品 后遍历背包
```python
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
- '''版本一'''
- # 初始化
- dp = [float("inf")]*(amount + 1)
- dp[0] = 0
- # 遍历物品
- for coin in coins:
- # 遍历背包
- for j in range(coin, amount + 1):
- dp[j] = min(dp[j], dp[j - coin] + 1)
- return dp[amount] if dp[amount] != float("inf") else -1
+ dp = [float('inf')] * (amount + 1) # 创建动态规划数组,初始值为正无穷大
+ dp[0] = 0 # 初始化背包容量为0时的最小硬币数量为0
+
+ for coin in coins: # 遍历硬币列表,相当于遍历物品
+ for i in range(coin, amount + 1): # 遍历背包容量
+ if dp[i - coin] != float('inf'): # 如果dp[i - coin]不是初始值,则进行状态转移
+ dp[i] = min(dp[i - coin] + 1, dp[i]) # 更新最小硬币数量
+
+ if dp[amount] == float('inf'): # 如果最终背包容量的最小硬币数量仍为正无穷大,表示无解
+ return -1
+ return dp[amount] # 返回背包容量为amount时的最小硬币数量
- def coinChange1(self, coins: List[int], amount: int) -> int:
- '''版本二'''
- # 初始化
- dp = [float("inf")]*(amount + 1)
- dp[0] = 0
- # 遍历物品
- for j in range(1, amount + 1):
- # 遍历背包
- for coin in coins:
- if j >= coin:
- dp[j] = min(dp[j], dp[j - coin] + 1)
- return dp[amount] if dp[amount] != float("inf") else -1
```
+先遍历背包 后遍历物品
+```python
+class Solution:
+ def coinChange(self, coins: List[int], amount: int) -> int:
+ dp = [float('inf')] * (amount + 1) # 创建动态规划数组,初始值为正无穷大
+ dp[0] = 0 # 初始化背包容量为0时的最小硬币数量为0
+
+ for i in range(1, amount + 1): # 遍历背包容量
+ for j in range(len(coins)): # 遍历硬币列表,相当于遍历物品
+ if i - coins[j] >= 0 and dp[i - coins[j]] != float('inf'): # 如果dp[i - coins[j]]不是初始值,则进行状态转移
+ dp[i] = min(dp[i - coins[j]] + 1, dp[i]) # 更新最小硬币数量
+
+ if dp[amount] == float('inf'): # 如果最终背包容量的最小硬币数量仍为正无穷大,表示无解
+ return -1
+ return dp[amount] # 返回背包容量为amount时的最小硬币数量
+
+```
+先遍历物品 后遍历背包(优化版)
+```python
+class Solution:
+ def coinChange(self, coins: List[int], amount: int) -> int:
+ dp = [float('inf')] * (amount + 1)
+ dp[0] = 0
+
+ for coin in coins:
+ for i in range(coin, amount + 1): # 进行优化,从能装得下的背包开始计算,则不需要进行比较
+ # 更新凑成金额 i 所需的最少硬币数量
+ dp[i] = min(dp[i], dp[i - coin] + 1)
+
+ return dp[amount] if dp[amount] != float('inf') else -1
+```
+先遍历背包 后遍历物品(优化版)
+```python
+class Solution:
+ def coinChange(self, coins: List[int], amount: int) -> int:
+ dp = [float('inf')] * (amount + 1)
+ dp[0] = 0
+
+ for i in range(1, amount + 1): # 遍历背包容量
+ for coin in coins: # 遍历物品
+ if i - coin >= 0:
+ # 更新凑成金额 i 所需的最少硬币数量
+ dp[i] = min(dp[i], dp[i - coin] + 1)
+
+ return dp[amount] if dp[amount] != float('inf') else -1
+
+
+
+```
Go:
```go
@@ -315,23 +355,52 @@ func min(a, b int) int {
Rust:
```rust
-pub fn coin_change(coins: Vec 参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md
index 571bc2ac..4cf69d6f 100644
--- a/problems/1005.K次取反后最大化的数组和.md
+++ b/problems/1005.K次取反后最大化的数组和.md
@@ -131,17 +131,23 @@ class Solution {
```
### Python
+贪心
```python
class Solution:
def largestSumAfterKNegations(self, A: List[int], K: int) -> int:
- A = sorted(A, key=abs, reverse=True) # 将A按绝对值从大到小排列
- for i in range(len(A)):
- if K > 0 and A[i] < 0:
+ A.sort(key=lambda x: abs(x), reverse=True) # 第一步:按照绝对值降序排序数组A
+
+ for i in range(len(A)): # 第二步:执行K次取反操作
+ if A[i] < 0 and K > 0:
A[i] *= -1
K -= 1
- if K > 0:
- A[-1] *= (-1)**K #取A最后一个数只需要写-1
- return sum(A)
+
+ if K % 2 == 1: # 第三步:如果K还有剩余次数,将绝对值最小的元素取反
+ A[-1] *= -1
+
+ result = sum(A) # 第四步:计算数组A的元素和
+ return result
+
```
### Go
@@ -189,9 +195,9 @@ var largestSumAfterKNegations = function(nums, k) {
nums[nums.length-1] = - nums[nums.length-1]
k--;
}
- return nums.reduce((a, b) => {
- a + b
- })
+
+ // 使用箭头函数的隐式返回值时,需使用简写省略花括号,否则要在 a + b 前加上 return
+ return nums.reduce((a, b) => a + b)
};
// 版本二 (优化: 一次遍历)
diff --git a/problems/1020.飞地的数量.md b/problems/1020.飞地的数量.md
index f97678e8..e92b2412 100644
--- a/problems/1020.飞地的数量.md
+++ b/problems/1020.飞地的数量.md
@@ -42,7 +42,8 @@
然后我们再去遍历这个地图,遇到有陆地的地方,去采用深搜或者广搜,边统计所有陆地。
-如果对深搜或者广搜不够了解,建议先看这里:[深度优先搜索精讲](https://leetcode.cn/problems/all-paths-from-source-to-target/solution/by-carlsun-2-66pf/),[广度优先搜索精讲](https://leetcode.cn/circle/discuss/V3FulB/)
+如果对深搜或者广搜不够了解,建议先看这里:[深度优先搜索精讲](https://programmercarl.com/图论深搜理论基础.html),[广度优先搜索精讲](https://programmercarl.com/图论广搜理论基础.html)。
+
采用深度优先搜索的代码如下:
diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md
index 7b60abdd..7142d75c 100644
--- a/problems/1035.不相交的线.md
+++ b/problems/1035.不相交的线.md
@@ -17,6 +17,11 @@

+## 算法公开课
+
+**《代码随想录》算法视频公开课:[动态规划之子序列问题,换汤不换药 | LeetCode:1035.不相交的线](https://www.bilibili.com/video/BV1h84y1x7MP),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
+
+
## 思路
相信不少录友看到这道题目都没啥思路,我们来逐步分析一下。
diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md
index 210ce737..a978b802 100644
--- a/problems/1049.最后一块石头的重量II.md
+++ b/problems/1049.最后一块石头的重量II.md
@@ -224,18 +224,79 @@ class Solution {
```
### Python:
+卡哥版
```python
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
- sumweight = sum(stones)
- target = sumweight // 2
- dp = [0] * (target + 1)
- for i in range(len(stones)):
- for j in range(target, stones[i] - 1, -1):
- dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])
- return sumweight - 2 * dp[target]
-```
+ dp = [0] * 15001
+ total_sum = sum(stones)
+ target = total_sum // 2
+ for stone in stones: # 遍历物品
+ for j in range(target, stone - 1, -1): # 遍历背包
+ dp[j] = max(dp[j], dp[j - stone] + stone)
+
+ return total_sum - dp[target] - dp[target]
+
+```
+二维DP版
+```python
+class Solution:
+ def lastStoneWeightII(self, stones: List[int]) -> int:
+ total_sum = sum(stones)
+ target = total_sum // 2
+
+ # 创建二维dp数组,行数为石头的数量加1,列数为target加1
+ # dp[i][j]表示前i个石头能否组成总重量为j
+ dp = [[False] * (target + 1) for _ in range(len(stones) + 1)]
+
+ # 初始化第一列,表示总重量为0时,前i个石头都能组成
+ for i in range(len(stones) + 1):
+ dp[i][0] = True
+
+ for i in range(1, len(stones) + 1):
+ for j in range(1, target + 1):
+ # 如果当前石头重量大于当前目标重量j,则无法选择该石头
+ if stones[i - 1] > j:
+ dp[i][j] = dp[i - 1][j]
+ else:
+ # 可选择该石头或不选择该石头
+ dp[i][j] = dp[i - 1][j] or dp[i - 1][j - stones[i - 1]]
+
+ # 找到最大的重量i,使得dp[len(stones)][i]为True
+ # 返回总重量减去两倍的最接近总重量一半的重量
+ for i in range(target, -1, -1):
+ if dp[len(stones)][i]:
+ return total_sum - 2 * i
+
+ return 0
+
+
+```
+一维DP版
+```python
+class Solution:
+ def lastStoneWeightII(self, stones):
+ total_sum = sum(stones)
+ target = total_sum // 2
+ dp = [False] * (target + 1)
+ dp[0] = True
+
+ for stone in stones:
+ for j in range(target, stone - 1, -1):
+ # 判断当前重量是否可以通过选择之前的石头得到或选择当前石头和之前的石头得到
+ dp[j] = dp[j] or dp[j - stone]
+
+ for i in range(target, -1, -1):
+ if dp[i]:
+ # 返回剩余石头的重量,即总重量减去两倍的最接近总重量一半的重量
+ return total_sum - 2 * i
+
+ return 0
+
+
+
+```
### Go:
```go
func lastStoneWeightII(stones []int) int {
@@ -379,8 +440,23 @@ object Solution {
}
```
+### Rust
-
+```rust
+impl Solution {
+ pub fn last_stone_weight_ii(stones: Vec 参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
diff --git a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md
index 7e58a870..e5266cd9 100644
--- a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md
+++ b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md
@@ -240,7 +240,7 @@ class Solution {
while (left - 1 >= 0 && nums[left - 1] == nums[index]) { // 防止数组越界。逻辑短路,两个条件顺序不能换
left--;
}
- // 向左滑动,找右边界
+ // 向右滑动,找右边界
while (right + 1 < nums.length && nums[right + 1] == nums[index]) { // 防止数组越界。
right++;
}
diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md
index e1e51923..4d9466c3 100644
--- a/problems/0039.组合总和.md
+++ b/problems/0039.组合总和.md
@@ -273,75 +273,101 @@ class Solution {
## Python
-**回溯**
+回溯(版本一)
```python
class Solution:
- def __init__(self):
- self.path = []
- self.paths = []
- def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
- '''
- 因为本题没有组合数量限制,所以只要元素总和大于target就算结束
- '''
- self.path.clear()
- self.paths.clear()
- self.backtracking(candidates, target, 0, 0)
- return self.paths
-
- def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
- # Base Case
- if sum_ == target:
- self.paths.append(self.path[:]) # 因为是shallow copy,所以不能直接传入self.path
+ def backtracking(self, candidates, target, total, startIndex, path, result):
+ if total > target:
return
- if sum_ > target:
+ if total == target:
+ result.append(path[:])
return
- # 单层递归逻辑
- for i in range(start_index, len(candidates)):
- sum_ += candidates[i]
- self.path.append(candidates[i])
- self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取,所以不是i+1
- sum_ -= candidates[i] # 回溯
- self.path.pop() # 回溯
+ for i in range(startIndex, len(candidates)):
+ total += candidates[i]
+ path.append(candidates[i])
+ self.backtracking(candidates, target, total, i, path, result) # 不用i+1了,表示可以重复读取当前的数
+ total -= candidates[i]
+ path.pop()
+
+ def combinationSum(self, candidates, target):
+ result = []
+ self.backtracking(candidates, target, 0, 0, [], result)
+ return result
+
```
-**剪枝回溯**
+回溯剪枝(版本一)
```python
class Solution:
- def __init__(self):
- self.path = []
- self.paths = []
- def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
- '''
- 因为本题没有组合数量限制,所以只要元素总和大于target就算结束
- '''
- self.path.clear()
- self.paths.clear()
-
- # 为了剪枝需要提前进行排序
- candidates.sort()
- self.backtracking(candidates, target, 0, 0)
- return self.paths
-
- def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
- # Base Case
- if sum_ == target:
- self.paths.append(self.path[:]) # 因为是shallow copy,所以不能直接传入self.path
+ def backtracking(self, candidates, target, total, startIndex, path, result):
+ if total == target:
+ result.append(path[:])
return
- # 单层递归逻辑
- # 如果本层 sum + condidates[i] > target,就提前结束遍历,剪枝
- for i in range(start_index, len(candidates)):
- if sum_ + candidates[i] > target:
- return
- sum_ += candidates[i]
- self.path.append(candidates[i])
- self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取,所以不是i-1
- sum_ -= candidates[i] # 回溯
- self.path.pop() # 回溯
+
+ for i in range(startIndex, len(candidates)):
+ if total + candidates[i] > target:
+ break
+ total += candidates[i]
+ path.append(candidates[i])
+ self.backtracking(candidates, target, total, i, path, result)
+ total -= candidates[i]
+ path.pop()
+
+ def combinationSum(self, candidates, target):
+ result = []
+ candidates.sort() # 需要排序
+ self.backtracking(candidates, target, 0, 0, [], result)
+ return result
+
+```
+
+回溯(版本二)
+
+```python
+class Solution:
+ def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
+ result =[]
+ self.backtracking(candidates, target, 0, [], result)
+ return result
+ def backtracking(self, candidates, target, startIndex, path, result):
+ if target == 0:
+ result.append(path[:])
+ return
+ if target < 0:
+ return
+ for i in range(startIndex, len(candidates)):
+ path.append(candidates[i])
+ self.backtracking(candidates, target - candidates[i], i, path, result)
+ path.pop()
+
+```
+
+回溯剪枝(版本二)
+
+```python
+class Solution:
+ def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
+ result =[]
+ candidates.sort()
+ self.backtracking(candidates, target, 0, [], result)
+ return result
+ def backtracking(self, candidates, target, startIndex, path, result):
+ if target == 0:
+ result.append(path[:])
+ return
+
+ for i in range(startIndex, len(candidates)):
+ if target - candidates[i] < 0:
+ break
+ path.append(candidates[i])
+ self.backtracking(candidates, target - candidates[i], i, path, result)
+ path.pop()
+
```
## Go
diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md
index b708650a..9094020e 100644
--- a/problems/0040.组合总和II.md
+++ b/problems/0040.组合总和II.md
@@ -356,93 +356,92 @@ class Solution {
```
## Python
-**回溯+巧妙去重(省去使用used**
+回溯
```python
class Solution:
- def __init__(self):
- self.paths = []
- self.path = []
- def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
- '''
- 类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序
- '''
- self.paths.clear()
- self.path.clear()
- # 必须提前进行数组排序,避免重复
- candidates.sort()
- self.backtracking(candidates, target, 0, 0)
- return self.paths
- def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
- # Base Case
- if sum_ == target:
- self.paths.append(self.path[:])
+ def backtracking(self, candidates, target, total, startIndex, path, result):
+ if total == target:
+ result.append(path[:])
return
-
- # 单层递归逻辑
- for i in range(start_index, len(candidates)):
- # 剪枝,同39.组合总和
- if sum_ + candidates[i] > target:
- return
-
- # 跳过同一树层使用过的元素
- if i > start_index and candidates[i] == candidates[i-1]:
+
+ for i in range(startIndex, len(candidates)):
+ if i > startIndex and candidates[i] == candidates[i - 1]:
continue
-
- sum_ += candidates[i]
- self.path.append(candidates[i])
- self.backtracking(candidates, target, sum_, i+1)
- self.path.pop() # 回溯,为了下一轮for loop
- sum_ -= candidates[i] # 回溯,为了下一轮for loop
+
+ if total + candidates[i] > target:
+ break
+
+ total += candidates[i]
+ path.append(candidates[i])
+ self.backtracking(candidates, target, total, i + 1, path, result)
+ total -= candidates[i]
+ path.pop()
+
+ def combinationSum2(self, candidates, target):
+ result = []
+ candidates.sort()
+ self.backtracking(candidates, target, 0, 0, [], result)
+ return result
+
```
-**回溯+去重(使用used)**
+回溯 使用used
```python
class Solution:
- def __init__(self):
- self.paths = []
- self.path = []
- self.used = []
- def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
- '''
- 类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序
- 本题需要使用used,用来标记区别同一树层的元素使用重复情况:注意区分递归纵向遍历遇到的重复元素,和for循环遇到的重复元素,这两者的区别
- '''
- self.paths.clear()
- self.path.clear()
- self.usage_list = [False] * len(candidates)
- # 必须提前进行数组排序,避免重复
- candidates.sort()
- self.backtracking(candidates, target, 0, 0)
- return self.paths
- def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
- # Base Case
- if sum_ == target:
- self.paths.append(self.path[:])
+ def backtracking(self, candidates, target, total, startIndex, used, path, result):
+ if total == target:
+ result.append(path[:])
return
-
- # 单层递归逻辑
- for i in range(start_index, len(candidates)):
- # 剪枝,同39.组合总和
- if sum_ + candidates[i] > target:
- return
-
- # 检查同一树层是否出现曾经使用过的相同元素
- # 若数组中前后元素值相同,但前者却未被使用(used == False),说明是for loop中的同一树层的相同元素情况
- if i > 0 and candidates[i] == candidates[i-1] and self.usage_list[i-1] == False:
+
+ for i in range(startIndex, len(candidates)):
+ # 对于相同的数字,只选择第一个未被使用的数字,跳过其他相同数字
+ if i > startIndex and candidates[i] == candidates[i - 1] and not used[i - 1]:
continue
- sum_ += candidates[i]
- self.path.append(candidates[i])
- self.usage_list[i] = True
- self.backtracking(candidates, target, sum_, i+1)
- self.usage_list[i] = False # 回溯,为了下一轮for loop
- self.path.pop() # 回溯,为了下一轮for loop
- sum_ -= candidates[i] # 回溯,为了下一轮for loop
-```
+ if total + candidates[i] > target:
+ break
+ total += candidates[i]
+ path.append(candidates[i])
+ used[i] = True
+ self.backtracking(candidates, target, total, i + 1, used, path, result)
+ used[i] = False
+ total -= candidates[i]
+ path.pop()
+
+ def combinationSum2(self, candidates, target):
+ used = [False] * len(candidates)
+ result = []
+ candidates.sort()
+ self.backtracking(candidates, target, 0, 0, used, [], result)
+ return result
+
+```
+回溯优化
+```python
+class Solution:
+ def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
+ candidates.sort()
+ results = []
+ self.combinationSumHelper(candidates, target, 0, [], results)
+ return results
+
+ def combinationSumHelper(self, candidates, target, index, path, results):
+ if target == 0:
+ results.append(path[:])
+ return
+ for i in range(index, len(candidates)):
+ if i > index and candidates[i] == candidates[i - 1]:
+ continue
+ if candidates[i] > target:
+ break
+ path.append(candidates[i])
+ self.combinationSumHelper(candidates, target - candidates[i], i + 1, path, results)
+ path.pop()
+```
## Go
主要在于如何在回溯中去重
diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md
index 8a939582..2f0349b2 100644
--- a/problems/0045.跳跃游戏II.md
+++ b/problems/0045.跳跃游戏II.md
@@ -205,66 +205,81 @@ class Solution {
```
### Python
-
+贪心(版本一)
```python
class Solution:
- def jump(self, nums: List[int]) -> int:
- if len(nums) == 1: return 0
- ans = 0
- curDistance = 0
- nextDistance = 0
- for i in range(len(nums)):
- nextDistance = max(i + nums[i], nextDistance)
- if i == curDistance:
- if curDistance != len(nums) - 1:
- ans += 1
- curDistance = nextDistance
- if nextDistance >= len(nums) - 1: break
- return ans
-```
-
-```python
-# 贪心版本二
-class Solution:
- def jump(self, nums: List[int]) -> int:
+ def jump(self, nums):
if len(nums) == 1:
return 0
- curDistance, nextDistance = 0, 0
- step = 0
- for i in range(len(nums)-1):
- nextDistance = max(nextDistance, nums[i]+i)
- if i == curDistance:
- curDistance = nextDistance
- step += 1
- return step
-```
-```python
-# 贪心版本三 - 类似‘55-跳跃游戏’写法
-class Solution:
- def jump(self, nums) -> int:
- if len(nums)==1: return 0
- i = 0
- count = 0
- cover = 0
- while i<=cover:
- for i in range(i,cover+1):
- cover = max(nums[i]+i,cover)
- if cover>=len(nums)-1: return count+1
- count+=1
+ cur_distance = 0 # 当前覆盖最远距离下标
+ ans = 0 # 记录走的最大步数
+ next_distance = 0 # 下一步覆盖最远距离下标
+
+ for i in range(len(nums)):
+ next_distance = max(nums[i] + i, next_distance) # 更新下一步覆盖最远距离下标
+ if i == cur_distance: # 遇到当前覆盖最远距离下标
+ ans += 1 # 需要走下一步
+ cur_distance = next_distance # 更新当前覆盖最远距离下标(相当于加油了)
+ if next_distance >= len(nums) - 1: # 当前覆盖最远距离达到数组末尾,不用再做ans++操作,直接结束
+ break
+
+ return ans
+
```
+贪心(版本二)
```python
-# 动态规划做法
+class Solution:
+ def jump(self, nums):
+ cur_distance = 0 # 当前覆盖的最远距离下标
+ ans = 0 # 记录走的最大步数
+ next_distance = 0 # 下一步覆盖的最远距离下标
+
+ for i in range(len(nums) - 1): # 注意这里是小于len(nums) - 1,这是关键所在
+ next_distance = max(nums[i] + i, next_distance) # 更新下一步覆盖的最远距离下标
+ if i == cur_distance: # 遇到当前覆盖的最远距离下标
+ cur_distance = next_distance # 更新当前覆盖的最远距离下标
+ ans += 1
+
+ return ans
+
+```
+贪心(版本三) 类似‘55-跳跃游戏’写法
+
+```python
+class Solution:
+ def jump(self, nums) -> int:
+ if len(nums)==1: # 如果数组只有一个元素,不需要跳跃,步数为0
+ return 0
+
+ i = 0 # 当前位置
+ count = 0 # 步数计数器
+ cover = 0 # 当前能够覆盖的最远距离
+
+ while i <= cover: # 当前位置小于等于当前能够覆盖的最远距离时循环
+ for i in range(i, cover+1): # 遍历从当前位置到当前能够覆盖的最远距离之间的所有位置
+ cover = max(nums[i]+i, cover) # 更新当前能够覆盖的最远距离
+ if cover >= len(nums)-1: # 如果当前能够覆盖的最远距离达到或超过数组的最后一个位置,直接返回步数+1
+ return count+1
+ count += 1 # 每一轮遍历结束后,步数+1
+
+
+```
+动态规划
+```python
class Solution:
def jump(self, nums: List[int]) -> int:
- result = [10**4+1]*len(nums)
- result[0]=0
- for i in range(len(nums)):
- for j in range(nums[i]+1):
- if i+j
diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md
index 4474f1c6..6dd3cd49 100644
--- a/problems/0151.翻转字符串里的单词.md
+++ b/problems/0151.翻转字符串里的单词.md
@@ -467,9 +467,57 @@ class Solution:
return " ".join(words)
```
-
Go:
+版本一:
+
+```go
+func reverseWords(s string) string {
+ b := []byte(s)
+
+ // 移除前面、中间、后面存在的多余空格
+ slow := 0
+ for i := 0; i < len(b); i++ {
+ if b[i] != ' ' {
+ if slow != 0 {
+ b[slow] = ' '
+ slow++
+ }
+ for i < len(b) && b[i] != ' ' { // 复制逻辑
+ b[slow] = b[i]
+ slow++
+ i++
+ }
+ }
+ }
+ b = b[0:slow]
+
+ // 翻转整个字符串
+ reverse(b)
+ // 翻转每个单词
+ last := 0
+ for i := 0; i <= len(b); i++ {
+ if i == len(b) || b[i] == ' ' {
+ reverse(b[last:i])
+ last = i + 1
+ }
+ }
+ return string(b)
+}
+
+func reverse(b []byte) {
+ left := 0
+ right := len(b) - 1
+ for left < right {
+ b[left], b[right] = b[right], b[left]
+ left++
+ right--
+ }
+}
+```
+
+版本二:
+
```go
import (
"fmt"
diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md
index 6e682ec3..80902559 100644
--- a/problems/0198.打家劫舍.md
+++ b/problems/0198.打家劫舍.md
@@ -141,7 +141,36 @@ class Solution {
}
}
-// 空间优化 dp数组只存与计算相关的两次数据
+// 使用滚动数组思想,优化空间
+// 分析本题可以发现,所求结果仅依赖于前两种状态,此时可以使用滚动数组思想将空间复杂度降低为3个空间
+class Solution {
+ public int rob(int[] nums) {
+
+ int len = nums.length;
+
+ if (len == 0) return 0;
+ else if (len == 1) return nums[0];
+ else if (len == 2) return Math.max(nums[0],nums[1]);
+
+
+ int[] result = new int[3]; //存放选择的结果
+ result[0] = nums[0];
+ result[1] = Math.max(nums[0],nums[1]);
+
+
+ for(int i=2;i
diff --git a/problems/0213.打家劫舍II.md b/problems/0213.打家劫舍II.md
index 6395f3a8..ee62b574 100644
--- a/problems/0213.打家劫舍II.md
+++ b/problems/0213.打家劫舍II.md
@@ -130,40 +130,93 @@ class Solution {
```
Python:
+
```Python
class Solution:
def rob(self, nums: List[int]) -> int:
- #在198入门级的打家劫舍问题上分两种情况考虑
- #一是不偷第一间房,二是不偷最后一间房
- if len(nums)==1:#题目中提示nums.length>=1,所以不需要考虑len(nums)==0的情况
+ if len(nums) == 0:
+ return 0
+ if len(nums) == 1:
return nums[0]
- val1=self.roblist(nums[1:])#不偷第一间房
- val2=self.roblist(nums[:-1])#不偷最后一间房
- return max(val1,val2)
+
+ result1 = self.robRange(nums, 0, len(nums) - 2) # 情况二
+ result2 = self.robRange(nums, 1, len(nums) - 1) # 情况三
+ return max(result1, result2)
+ # 198.打家劫舍的逻辑
+ def robRange(self, nums: List[int], start: int, end: int) -> int:
+ if end == start:
+ return nums[start]
+
+ prev_max = nums[start]
+ curr_max = max(nums[start], nums[start + 1])
+
+ for i in range(start + 2, end + 1):
+ temp = curr_max
+ curr_max = max(prev_max + nums[i], curr_max)
+ prev_max = temp
+
+ return curr_max
- def roblist(self,nums):
- l=len(nums)
- dp=[0]*l
- dp[0]=nums[0]
- for i in range(1,l):
- if i==1:
- dp[i]=max(dp[i-1],nums[i])
- else:
- dp[i]=max(dp[i-1],dp[i-2]+nums[i])
- return dp[-1]
```
+2维DP
```python
-class Solution: # 二维dp数组写法
+class Solution:
def rob(self, nums: List[int]) -> int:
- if len(nums)<3: return max(nums)
- return max(self.default(nums[:-1]),self.default(nums[1:]))
- def default(self,nums):
- dp = [[0,0] for _ in range(len(nums))]
+ if len(nums) < 3:
+ return max(nums)
+
+ # 情况二:不抢劫第一个房屋
+ result1 = self.robRange(nums[:-1])
+
+ # 情况三:不抢劫最后一个房屋
+ result2 = self.robRange(nums[1:])
+
+ return max(result1, result2)
+
+ def robRange(self, nums):
+ dp = [[0, 0] for _ in range(len(nums))]
dp[0][1] = nums[0]
- for i in range(1,len(nums)):
- dp[i][0] = max(dp[i-1])
- dp[i][1] = dp[i-1][0] + nums[i]
+
+ for i in range(1, len(nums)):
+ dp[i][0] = max(dp[i - 1])
+ dp[i][1] = dp[i - 1][0] + nums[i]
+
return max(dp[-1])
+
+
+
+```
+
+优化版
+```python
+class Solution:
+ def rob(self, nums: List[int]) -> int:
+ if not nums: # 如果没有房屋,返回0
+ return 0
+
+ if len(nums) == 1: # 如果只有一个房屋,返回该房屋的金额
+ return nums[0]
+
+ # 情况二:不抢劫第一个房屋
+ prev_max = 0 # 上一个房屋的最大金额
+ curr_max = 0 # 当前房屋的最大金额
+ for num in nums[1:]:
+ temp = curr_max # 临时变量保存当前房屋的最大金额
+ curr_max = max(prev_max + num, curr_max) # 更新当前房屋的最大金额
+ prev_max = temp # 更新上一个房屋的最大金额
+ result1 = curr_max
+
+ # 情况三:不抢劫最后一个房屋
+ prev_max = 0 # 上一个房屋的最大金额
+ curr_max = 0 # 当前房屋的最大金额
+ for num in nums[:-1]:
+ temp = curr_max # 临时变量保存当前房屋的最大金额
+ curr_max = max(prev_max + num, curr_max) # 更新当前房屋的最大金额
+ prev_max = temp # 更新上一个房屋的最大金额
+ result2 = curr_max
+
+ return max(result1, result2)
+
```
Go:
@@ -248,6 +301,35 @@ function robRange(nums: number[], start: number, end: number): number {
}
```
+Rust:
+
+```rust
+impl Solution {
+ pub fn rob(nums: Vec
diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md
index d6c6b9c9..4b21008e 100644
--- a/problems/0491.递增子序列.md
+++ b/problems/0491.递增子序列.md
@@ -205,6 +205,30 @@ public:
### Java
+```Java
+//using set, aligned with the unimproved method
+class Solution {
+ List
> result = new ArrayList<>();
+ List
> findSubsequences(int[] nums) {
+ backTracking(nums, 0);
+ return result;
+ }
+ private void backTracking(int[] nums, int startIndex){
+ if(path.size() >= 2)
+ result.add(new ArrayList<>(path));
+ HashSet
diff --git a/problems/0503.下一个更大元素II.md b/problems/0503.下一个更大元素II.md
index bf651209..3fd4b3b6 100644
--- a/problems/0503.下一个更大元素II.md
+++ b/problems/0503.下一个更大元素II.md
@@ -164,6 +164,7 @@ class Solution {
Python:
```python
+# 方法 1:
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
dp = [-1] * len(nums)
@@ -174,6 +175,26 @@ class Solution:
stack.pop()
stack.append(i%len(nums))
return dp
+
+# 方法 2:
+class Solution:
+ def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
+ stack = []
+ # 创建答案数组
+ ans = [-1] * len(nums1)
+ for i in range(len(nums2)):
+ while len(stack) > 0 and nums2[i] > nums2[stack[-1]]:
+ # 判断 num1 是否有 nums2[stack[-1]]。如果没有这个判断会出现指针异常
+ if nums2[stack[-1]] in nums1:
+ # 锁定 num1 检索的 index
+ index = nums1.index(nums2[stack[-1]])
+ # 更新答案数组
+ ans[index] = nums2[i]
+ # 弹出小元素
+ # 这个代码一定要放在 if 外面。否则单调栈的逻辑就不成立了
+ stack.pop()
+ stack.append(i)
+ return ans
```
Go:
```go
diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md
index 479b36a0..7ace0723 100644
--- a/problems/0509.斐波那契数.md
+++ b/problems/0509.斐波那契数.md
@@ -203,18 +203,9 @@ class Solution {
```
### Python
+动态规划(版本一)
```python
-class Solution:
- def fib(self, n: int) -> int:
- if n < 2:
- return n
- a, b, c = 0, 1, 0
- for i in range(1, n):
- c = a + b
- a, b = b, c
- return c
-# 动态规划 (注释版。无修饰)
class Solution:
def fib(self, n: int) -> int:
@@ -238,7 +229,48 @@ class Solution:
# 返回答案
return dp[n]
-# 递归实现
+```
+动态规划(版本二)
+```python
+
+class Solution:
+ def fib(self, n: int) -> int:
+ if n <= 1:
+ return n
+
+ dp = [0, 1]
+
+ for i in range(2, n + 1):
+ total = dp[0] + dp[1]
+ dp[0] = dp[1]
+ dp[1] = total
+
+ return dp[1]
+
+
+```
+动态规划(版本三)
+```python
+class Solution:
+ def fib(self, n: int) -> int:
+ if n <= 1:
+ return n
+
+ prev1, prev2 = 0, 1
+
+ for _ in range(2, n + 1):
+ curr = prev1 + prev2
+ prev1, prev2 = prev2, curr
+
+ return prev2
+
+
+
+
+```
+递归(版本一)
+```python
+
class Solution:
def fib(self, n: int) -> int:
if n < 2:
diff --git a/problems/0518.零钱兑换II.md b/problems/0518.零钱兑换II.md
index c208754f..8da35114 100644
--- a/problems/0518.零钱兑换II.md
+++ b/problems/0518.零钱兑换II.md
@@ -282,17 +282,18 @@ func change(amount int, coins []int) int {
Rust:
```rust
-pub fn change(amount: i32, coins: Vec
-首先先计算节点的入度,代码如下:
+首先先计算节点的入度,这里不少录友在计算入度的时候就搞蒙了,分不清 edges[i][j] 表示的都是什么。
+
+例如题目示例一给的是:edges = [[1,2],[1,3],[2,3]]
+
+那大家很自然就想 对应二维数组的数值是: edges[1][2] ,edges[1][3],edges[2][3],但又想不出来 edges[1][2] 数值又是什么呢? 越想约懵。
+
+其实 edges = [[1,2],[1,3],[2,3]],表示的是
+
+edges[0][0] = 1,edges[0][1] = 2,
+
+edges[1][0] = 1,edges[1][1] = 3,
+
+edges[2][0] = 2,edges[2][1] = 3,
+
+二维数组大家都学过,但是往往和图结合在一起的时候,就非常容易搞混,哪里是数组,哪里是下标了。
+
+搞清楚之后,我们如何统计入度呢?
+
+即 edges[i][1] 表示的节点都是 箭头指向的节点,即这个几点有一个入度! (如果想统计出度,那么就是 edges[i][0])。
+
+所以,统计入度的代码如下:
```cpp
int inDegree[N] = {0}; // 记录节点入度
@@ -94,7 +113,7 @@ if (vec.size() > 0) {
vector