mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 00:43:04 +08:00
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
主要在于如何在回溯中去重
|
||||
|
||||
|
@ -215,68 +215,27 @@ class Solution {
|
||||
```
|
||||
|
||||
### Python
|
||||
**回溯**
|
||||
回溯 使用used
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.path = []
|
||||
self.paths = []
|
||||
def permute(self, nums):
|
||||
result = []
|
||||
self.backtracking(nums, [], [False] * len(nums), result)
|
||||
return result
|
||||
|
||||
def permute(self, nums: List[int]) -> List[List[int]]:
|
||||
'''
|
||||
因为本题排列是有序的,这意味着同一层的元素可以重复使用,但同一树枝上不能重复使用(usage_list)
|
||||
所以处理排列问题每层都需要从头搜索,故不再使用start_index
|
||||
'''
|
||||
usage_list = [False] * len(nums)
|
||||
self.backtracking(nums, usage_list)
|
||||
return self.paths
|
||||
|
||||
def backtracking(self, nums: List[int], usage_list: List[bool]) -> None:
|
||||
# Base Case本题求叶子节点
|
||||
if len(self.path) == len(nums):
|
||||
self.paths.append(self.path[:])
|
||||
def backtracking(self, nums, path, used, result):
|
||||
if len(path) == len(nums):
|
||||
result.append(path[:])
|
||||
return
|
||||
|
||||
# 单层递归逻辑
|
||||
for i in range(0, len(nums)): # 从头开始搜索
|
||||
# 若遇到self.path里已收录的元素,跳过
|
||||
if usage_list[i] == True:
|
||||
for i in range(len(nums)):
|
||||
if used[i]:
|
||||
continue
|
||||
usage_list[i] = True
|
||||
self.path.append(nums[i])
|
||||
self.backtracking(nums, usage_list) # 纵向传递使用信息,去重
|
||||
self.path.pop()
|
||||
usage_list[i] = False
|
||||
```
|
||||
**回溯+丢掉usage_list**
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.path = []
|
||||
self.paths = []
|
||||
used[i] = True
|
||||
path.append(nums[i])
|
||||
self.backtracking(nums, path, used, result)
|
||||
path.pop()
|
||||
used[i] = False
|
||||
|
||||
def permute(self, nums: List[int]) -> List[List[int]]:
|
||||
'''
|
||||
因为本题排列是有序的,这意味着同一层的元素可以重复使用,但同一树枝上不能重复使用
|
||||
所以处理排列问题每层都需要从头搜索,故不再使用start_index
|
||||
'''
|
||||
self.backtracking(nums)
|
||||
return self.paths
|
||||
|
||||
def backtracking(self, nums: List[int]) -> None:
|
||||
# Base Case本题求叶子节点
|
||||
if len(self.path) == len(nums):
|
||||
self.paths.append(self.path[:])
|
||||
return
|
||||
|
||||
# 单层递归逻辑
|
||||
for i in range(0, len(nums)): # 从头开始搜索
|
||||
# 若遇到self.path里已收录的元素,跳过
|
||||
if nums[i] in self.path:
|
||||
continue
|
||||
self.path.append(nums[i])
|
||||
self.backtracking(nums)
|
||||
self.path.pop()
|
||||
```
|
||||
|
||||
### Go
|
||||
|
@ -223,28 +223,25 @@ class Solution {
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
|
||||
# res用来存放结果
|
||||
if not nums: return []
|
||||
res = []
|
||||
used = [0] * len(nums)
|
||||
def backtracking(nums, used, path):
|
||||
# 终止条件
|
||||
if len(path) == len(nums):
|
||||
res.append(path.copy())
|
||||
return
|
||||
for i in range(len(nums)):
|
||||
if not used[i]:
|
||||
if i>0 and nums[i] == nums[i-1] and not used[i-1]:
|
||||
continue
|
||||
used[i] = 1
|
||||
path.append(nums[i])
|
||||
backtracking(nums, used, path)
|
||||
path.pop()
|
||||
used[i] = 0
|
||||
# 记得给nums排序
|
||||
backtracking(sorted(nums),used,[])
|
||||
return res
|
||||
def permuteUnique(self, nums):
|
||||
nums.sort() # 排序
|
||||
result = []
|
||||
self.backtracking(nums, [], [False] * len(nums), result)
|
||||
return result
|
||||
|
||||
def backtracking(self, nums, path, used, result):
|
||||
if len(path) == len(nums):
|
||||
result.append(path[:])
|
||||
return
|
||||
for i in range(len(nums)):
|
||||
if (i > 0 and nums[i] == nums[i - 1] and not used[i - 1]) or used[i]:
|
||||
continue
|
||||
used[i] = True
|
||||
path.append(nums[i])
|
||||
self.backtracking(nums, path, used, result)
|
||||
path.pop()
|
||||
used[i] = False
|
||||
|
||||
```
|
||||
|
||||
### Go
|
||||
|
@ -351,48 +351,47 @@ class Solution {
|
||||
```python
|
||||
class Solution:
|
||||
def solveNQueens(self, n: int) -> List[List[str]]:
|
||||
if not n: return []
|
||||
board = [['.'] * n for _ in range(n)]
|
||||
res = []
|
||||
def isVaild(board,row, col):
|
||||
#判断同一列是否冲突
|
||||
for i in range(len(board)):
|
||||
if board[i][col] == 'Q':
|
||||
return False
|
||||
# 判断左上角是否冲突
|
||||
i = row -1
|
||||
j = col -1
|
||||
while i>=0 and j>=0:
|
||||
if board[i][j] == 'Q':
|
||||
return False
|
||||
i -= 1
|
||||
j -= 1
|
||||
# 判断右上角是否冲突
|
||||
i = row - 1
|
||||
j = col + 1
|
||||
while i>=0 and j < len(board):
|
||||
if board[i][j] == 'Q':
|
||||
return False
|
||||
i -= 1
|
||||
j += 1
|
||||
return True
|
||||
result = [] # 存储最终结果的二维字符串数组
|
||||
|
||||
chessboard = ['.' * n for _ in range(n)] # 初始化棋盘
|
||||
self.backtracking(n, 0, chessboard, result) # 回溯求解
|
||||
return [[''.join(row) for row in solution] for solution in result] # 返回结果集
|
||||
|
||||
def backtracking(self, n: int, row: int, chessboard: List[str], result: List[List[str]]) -> None:
|
||||
if row == n:
|
||||
result.append(chessboard[:]) # 棋盘填满,将当前解加入结果集
|
||||
return
|
||||
|
||||
for col in range(n):
|
||||
if self.isValid(row, col, chessboard):
|
||||
chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col+1:] # 放置皇后
|
||||
self.backtracking(n, row + 1, chessboard, result) # 递归到下一行
|
||||
chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col+1:] # 回溯,撤销当前位置的皇后
|
||||
|
||||
def isValid(self, row: int, col: int, chessboard: List[str]) -> bool:
|
||||
# 检查列
|
||||
for i in range(row):
|
||||
if chessboard[i][col] == 'Q':
|
||||
return False # 当前列已经存在皇后,不合法
|
||||
|
||||
# 检查 45 度角是否有皇后
|
||||
i, j = row - 1, col - 1
|
||||
while i >= 0 and j >= 0:
|
||||
if chessboard[i][j] == 'Q':
|
||||
return False # 左上方向已经存在皇后,不合法
|
||||
i -= 1
|
||||
j -= 1
|
||||
|
||||
# 检查 135 度角是否有皇后
|
||||
i, j = row - 1, col + 1
|
||||
while i >= 0 and j < len(chessboard):
|
||||
if chessboard[i][j] == 'Q':
|
||||
return False # 右上方向已经存在皇后,不合法
|
||||
i -= 1
|
||||
j += 1
|
||||
|
||||
return True # 当前位置合法
|
||||
|
||||
def backtracking(board, row, n):
|
||||
# 如果走到最后一行,说明已经找到一个解
|
||||
if row == n:
|
||||
temp_res = []
|
||||
for temp in board:
|
||||
temp_str = "".join(temp)
|
||||
temp_res.append(temp_str)
|
||||
res.append(temp_res)
|
||||
for col in range(n):
|
||||
if not isVaild(board, row, col):
|
||||
continue
|
||||
board[row][col] = 'Q'
|
||||
backtracking(board, row+1, n)
|
||||
board[row][col] = '.'
|
||||
backtracking(board, 0, n)
|
||||
return res
|
||||
```
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -362,104 +362,82 @@ class Solution {
|
||||
|
||||
## python
|
||||
|
||||
python2:
|
||||
```python
|
||||
class Solution(object):
|
||||
def restoreIpAddresses(self, s):
|
||||
"""
|
||||
:type s: str
|
||||
:rtype: List[str]
|
||||
"""
|
||||
ans = []
|
||||
path = []
|
||||
def backtrack(path, startIndex):
|
||||
if len(s) > 12: return []
|
||||
if len(path) == 4:
|
||||
if startIndex == len(s):
|
||||
ans.append(".".join(path[:]))
|
||||
return
|
||||
for i in range(startIndex+1, min(startIndex+4, len(s)+1)): # 剪枝
|
||||
string = s[startIndex:i]
|
||||
if not 0 <= int(string) <= 255:
|
||||
continue
|
||||
if not string == "0" and not string.lstrip('0') == string:
|
||||
continue
|
||||
path.append(string)
|
||||
backtrack(path, i)
|
||||
path.pop()
|
||||
|
||||
backtrack([], 0)
|
||||
return ans
|
||||
```
|
||||
|
||||
python3:
|
||||
回溯(版本一)
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.result = []
|
||||
|
||||
def restoreIpAddresses(self, s: str) -> List[str]:
|
||||
'''
|
||||
本质切割问题使用回溯搜索法,本题只能切割三次,所以纵向递归总共四层
|
||||
因为不能重复分割,所以需要start_index来记录下一层递归分割的起始位置
|
||||
添加变量point_num来记录逗号的数量[0,3]
|
||||
'''
|
||||
self.result.clear()
|
||||
if len(s) > 12: return []
|
||||
self.backtracking(s, 0, 0)
|
||||
return self.result
|
||||
result = []
|
||||
self.backtracking(s, 0, 0, "", result)
|
||||
return result
|
||||
|
||||
def backtracking(self, s: str, start_index: int, point_num: int) -> None:
|
||||
# Base Case
|
||||
if point_num == 3:
|
||||
if self.is_valid(s, start_index, len(s)-1):
|
||||
self.result.append(s[:])
|
||||
def backtracking(self, s, start_index, point_num, current, result):
|
||||
if point_num == 3: # 逗点数量为3时,分隔结束
|
||||
if self.is_valid(s, start_index, len(s) - 1): # 判断第四段子字符串是否合法
|
||||
current += s[start_index:] # 添加最后一段子字符串
|
||||
result.append(current)
|
||||
return
|
||||
# 单层递归逻辑
|
||||
for i in range(start_index, len(s)):
|
||||
# [start_index, i]就是被截取的子串
|
||||
if self.is_valid(s, start_index, i):
|
||||
s = s[:i+1] + '.' + s[i+1:]
|
||||
self.backtracking(s, i+2, point_num+1) # 在填入.后,下一子串起始后移2位
|
||||
s = s[:i+1] + s[i+2:] # 回溯
|
||||
else:
|
||||
# 若当前被截取的子串大于255或者大于三位数,直接结束本层循环
|
||||
break
|
||||
|
||||
def is_valid(self, s: str, start: int, end: int) -> bool:
|
||||
if start > end: return False
|
||||
# 若数字是0开头,不合法
|
||||
if s[start] == '0' and start != end:
|
||||
return False
|
||||
if not 0 <= int(s[start:end+1]) <= 255:
|
||||
return False
|
||||
return True
|
||||
```
|
||||
|
||||
python3; 简单拼接版本(类似Leetcode131写法):
|
||||
for i in range(start_index, len(s)):
|
||||
if self.is_valid(s, start_index, i): # 判断 [start_index, i] 这个区间的子串是否合法
|
||||
sub = s[start_index:i + 1]
|
||||
self.backtracking(s, i + 1, point_num + 1, current + sub + '.', result)
|
||||
else:
|
||||
break
|
||||
|
||||
def is_valid(self, s, start, end):
|
||||
if start > end:
|
||||
return False
|
||||
if s[start] == '0' and start != end: # 0开头的数字不合法
|
||||
return False
|
||||
num = 0
|
||||
for i in range(start, end + 1):
|
||||
if not s[i].isdigit(): # 遇到非数字字符不合法
|
||||
return False
|
||||
num = num * 10 + int(s[i])
|
||||
if num > 255: # 如果大于255了不合法
|
||||
return False
|
||||
return True
|
||||
|
||||
```
|
||||
回溯(版本二)
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
class Solution:
|
||||
def restoreIpAddresses(self, s: str) -> List[str]:
|
||||
global results, path
|
||||
results = []
|
||||
path = []
|
||||
self.backtracking(s,0)
|
||||
self.backtracking(s, 0, [], results)
|
||||
return results
|
||||
|
||||
def backtracking(self,s,index):
|
||||
global results,path
|
||||
if index == len(s) and len(path)==4:
|
||||
results.append('.'.join(path)) # 在连接时需要中间间隔符号的话就在''中间写上对应的间隔符
|
||||
def backtracking(self, s, index, path, results):
|
||||
if index == len(s) and len(path) == 4:
|
||||
results.append('.'.join(path))
|
||||
return
|
||||
for i in range(index,len(s)):
|
||||
if len(path)>3: break # 剪枝
|
||||
temp = s[index:i+1]
|
||||
if (int(temp)<256 and int(temp)>0 and temp[0]!='0') or (temp=='0'):
|
||||
path.append(temp)
|
||||
self.backtracking(s,i+1)
|
||||
path.pop()
|
||||
|
||||
if len(path) > 4: # 剪枝
|
||||
return
|
||||
|
||||
for i in range(index, min(index + 3, len(s))):
|
||||
if self.is_valid(s, index, i):
|
||||
sub = s[index:i+1]
|
||||
path.append(sub)
|
||||
self.backtracking(s, i+1, path, results)
|
||||
path.pop()
|
||||
|
||||
def is_valid(self, s, start, end):
|
||||
if start > end:
|
||||
return False
|
||||
if s[start] == '0' and start != end: # 0开头的数字不合法
|
||||
return False
|
||||
num = int(s[start:end+1])
|
||||
return 0 <= num <= 255
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Go
|
||||
|
||||
```go
|
||||
|
@ -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 (
|
||||
|
@ -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
|
||||
|
@ -347,64 +347,88 @@ class Solution {
|
||||
```
|
||||
|
||||
### python
|
||||
回溯 使用used数组
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
|
||||
# defaultdic(list) 是为了方便直接append
|
||||
tickets_dict = defaultdict(list)
|
||||
for item in tickets:
|
||||
tickets_dict[item[0]].append(item[1])
|
||||
# 给每一个机场的到达机场排序,小的在前面,在回溯里首先被pop(0)出去
|
||||
# 这样最先找的的path就是排序最小的答案,直接返回
|
||||
for airport in tickets_dict: tickets_dict[airport].sort()
|
||||
'''
|
||||
tickets_dict里面的内容是这样的
|
||||
{'JFK': ['ATL', 'SFO'], 'SFO': ['ATL'], 'ATL': ['JFK', 'SFO']})
|
||||
'''
|
||||
path = ["JFK"]
|
||||
def backtracking(start_point):
|
||||
# 终止条件
|
||||
if len(path) == len(tickets) + 1:
|
||||
return True
|
||||
for _ in tickets_dict[start_point]:
|
||||
#必须及时删除,避免出现死循环
|
||||
end_point = tickets_dict[start_point].pop(0)
|
||||
path.append(end_point)
|
||||
# 只要找到一个就可以返回了
|
||||
if backtracking(end_point):
|
||||
return True
|
||||
path.pop()
|
||||
tickets_dict[start_point].append(end_point)
|
||||
|
||||
backtracking("JFK")
|
||||
return path
|
||||
```
|
||||
|
||||
python - 使用used数组 - 神似之前几题写法
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
|
||||
global used,path,results
|
||||
used = [0]*len(tickets)
|
||||
tickets.sort() # 先排序,这样一旦找到第一个可行路径,一定是字母排序最小的
|
||||
used = [0] * len(tickets)
|
||||
path = ['JFK']
|
||||
results = []
|
||||
tickets.sort() # 先排序,这样一旦找到第一个可行路径,一定是字母排序最小的
|
||||
self.backtracking(tickets,'JFK')
|
||||
self.backtracking(tickets, used, path, 'JFK', results)
|
||||
return results[0]
|
||||
def backtracking(self,tickets,cur):
|
||||
if sum(used) == len(tickets):
|
||||
results.append(path[:])
|
||||
return True # 只要找到就返回
|
||||
for i in range(len(tickets)):
|
||||
if tickets[i][0]==cur and used[i]==0:
|
||||
used[i]=1
|
||||
path.append(tickets[i][1])
|
||||
state = self.backtracking(tickets,tickets[i][1])
|
||||
path.pop()
|
||||
used[i]=0
|
||||
if state: return True # 只要找到就返回,不继续搜索了
|
||||
|
||||
def backtracking(self, tickets, used, path, cur, results):
|
||||
if len(path) == len(tickets) + 1: # 终止条件:路径长度等于机票数量+1
|
||||
results.append(path[:]) # 将当前路径添加到结果列表
|
||||
return True
|
||||
|
||||
for i, ticket in enumerate(tickets): # 遍历机票列表
|
||||
if ticket[0] == cur and used[i] == 0: # 找到起始机场为cur且未使用过的机票
|
||||
used[i] = 1 # 标记该机票为已使用
|
||||
path.append(ticket[1]) # 将到达机场添加到路径中
|
||||
state = self.backtracking(tickets, used, path, ticket[1], results) # 递归搜索
|
||||
path.pop() # 回溯,移除最后添加的到达机场
|
||||
used[i] = 0 # 标记该机票为未使用
|
||||
if state:
|
||||
return True # 只要找到一个可行路径就返回,不继续搜索
|
||||
|
||||
```
|
||||
回溯 使用字典
|
||||
```python
|
||||
from collections import defaultdict
|
||||
|
||||
class Solution:
|
||||
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
|
||||
targets = defaultdict(list) # 构建机场字典
|
||||
for ticket in tickets:
|
||||
targets[ticket[0]].append(ticket[1])
|
||||
for airport in targets:
|
||||
targets[airport].sort() # 对目的地列表进行排序
|
||||
|
||||
path = ["JFK"] # 起始机场为"JFK"
|
||||
self.backtracking(targets, path, len(tickets))
|
||||
return path
|
||||
|
||||
def backtracking(self, targets, path, ticketNum):
|
||||
if len(path) == ticketNum + 1:
|
||||
return True # 找到有效行程
|
||||
|
||||
airport = path[-1] # 当前机场
|
||||
destinations = targets[airport] # 当前机场可以到达的目的地列表
|
||||
for i, dest in enumerate(destinations):
|
||||
targets[airport].pop(i) # 标记已使用的机票
|
||||
path.append(dest) # 添加目的地到路径
|
||||
if self.backtracking(targets, path, ticketNum):
|
||||
return True # 找到有效行程
|
||||
targets[airport].insert(i, dest) # 回溯,恢复机票
|
||||
path.pop() # 移除目的地
|
||||
return False # 没有找到有效行程
|
||||
|
||||
```
|
||||
回溯 使用字典 逆序
|
||||
```python
|
||||
from collections import defaultdict
|
||||
|
||||
class Solution:
|
||||
def findItinerary(self, tickets):
|
||||
targets = defaultdict(list) # 创建默认字典,用于存储机场映射关系
|
||||
for ticket in tickets:
|
||||
targets[ticket[0]].append(ticket[1]) # 将机票输入到字典中
|
||||
|
||||
for key in targets:
|
||||
targets[key].sort(reverse=True) # 对到达机场列表进行字母逆序排序
|
||||
|
||||
result = []
|
||||
self.backtracking("JFK", targets, result) # 调用回溯函数开始搜索路径
|
||||
return result[::-1] # 返回逆序的行程路径
|
||||
|
||||
def backtracking(self, airport, targets, result):
|
||||
while targets[airport]: # 当机场还有可到达的机场时
|
||||
next_airport = targets[airport].pop() # 弹出下一个机场
|
||||
self.backtracking(next_airport, targets, result) # 递归调用回溯函数进行深度优先搜索
|
||||
result.append(airport) # 将当前机场添加到行程路径中
|
||||
```
|
||||
|
||||
### GO
|
||||
|
@ -305,21 +305,43 @@ class Solution {
|
||||
|
||||
### Python
|
||||
|
||||
**贪心**
|
||||
贪心(版本一)
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def wiggleMaxLength(self, nums):
|
||||
if len(nums) <= 1:
|
||||
return len(nums) # 如果数组长度为0或1,则返回数组长度
|
||||
curDiff = 0 # 当前一对元素的差值
|
||||
preDiff = 0 # 前一对元素的差值
|
||||
result = 1 # 记录峰值的个数,初始为1(默认最右边的元素被视为峰值)
|
||||
for i in range(len(nums) - 1):
|
||||
curDiff = nums[i + 1] - nums[i] # 计算下一个元素与当前元素的差值
|
||||
# 如果遇到一个峰值
|
||||
if (preDiff <= 0 and curDiff > 0) or (preDiff >= 0 and curDiff < 0):
|
||||
result += 1 # 峰值个数加1
|
||||
preDiff = curDiff # 注意这里,只在摆动变化的时候更新preDiff
|
||||
return result # 返回最长摆动子序列的长度
|
||||
|
||||
```
|
||||
贪心(版本二)
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def wiggleMaxLength(self, nums: List[int]) -> int:
|
||||
preC,curC,res = 0,0,1 #题目里nums长度大于等于1,当长度为1时,其实到不了for循环里去,所以不用考虑nums长度
|
||||
if len(nums) <= 1:
|
||||
return len(nums) # 如果数组长度为0或1,则返回数组长度
|
||||
preDiff,curDiff ,result = 0,0,1 #题目里nums长度大于等于1,当长度为1时,其实到不了for循环里去,所以不用考虑nums长度
|
||||
for i in range(len(nums) - 1):
|
||||
curC = nums[i + 1] - nums[i]
|
||||
if curC * preC <= 0 and curC !=0: #差值为0时,不算摆动
|
||||
res += 1
|
||||
preC = curC #如果当前差值和上一个差值为一正一负时,才需要用当前差值替代上一个差值
|
||||
return res
|
||||
curDiff = nums[i + 1] - nums[i]
|
||||
if curDiff * preDiff <= 0 and curDiff !=0: #差值为0时,不算摆动
|
||||
result += 1
|
||||
preDiff = curDiff #如果当前差值和上一个差值为一正一负时,才需要用当前差值替代上一个差值
|
||||
return result
|
||||
|
||||
```
|
||||
|
||||
**动态规划**
|
||||
动态规划(版本一)
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
@ -341,25 +363,44 @@ class Solution:
|
||||
return max(dp[-1][0], dp[-1][1])
|
||||
```
|
||||
|
||||
动态规划(版本二)
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def wiggleMaxLength(self, nums: List[int]) -> int:
|
||||
# up i作为波峰最长的序列长度
|
||||
# down i作为波谷最长的序列长度
|
||||
n = len(nums)
|
||||
# 长度为0和1的直接返回长度
|
||||
if n<2: return n
|
||||
for i in range(1,n):
|
||||
if nums[i]>nums[i-1]:
|
||||
# nums[i] 为波峰,1. 前面是波峰,up值不变,2. 前面是波谷,down值加1
|
||||
# 目前up值取两者的较大值(其实down+1即可,可以推理前一步down和up最多相差1,所以down+1>=up)
|
||||
up = max(up, down+1)
|
||||
elif nums[i]<nums[i-1]:
|
||||
# nums[i] 为波谷,1. 前面是波峰,up+1,2. 前面是波谷,down不变,取较大值
|
||||
down = max(down, up+1)
|
||||
return max(up, down)
|
||||
def wiggleMaxLength(self, nums):
|
||||
dp = [[0, 0] for _ in range(len(nums))] # 创建二维dp数组,用于记录摆动序列的最大长度
|
||||
dp[0][0] = dp[0][1] = 1 # 初始条件,序列中的第一个元素默认为峰值,最小长度为1
|
||||
for i in range(1, len(nums)):
|
||||
dp[i][0] = dp[i][1] = 1 # 初始化当前位置的dp值为1
|
||||
for j in range(i):
|
||||
if nums[j] > nums[i]:
|
||||
dp[i][1] = max(dp[i][1], dp[j][0] + 1) # 如果前一个数比当前数大,可以形成一个上升峰值,更新dp[i][1]
|
||||
for j in range(i):
|
||||
if nums[j] < nums[i]:
|
||||
dp[i][0] = max(dp[i][0], dp[j][1] + 1) # 如果前一个数比当前数小,可以形成一个下降峰值,更新dp[i][0]
|
||||
return max(dp[-1][0], dp[-1][1]) # 返回最大的摆动序列长度
|
||||
|
||||
```
|
||||
|
||||
动态规划(版本三)优化
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def wiggleMaxLength(self, nums):
|
||||
if len(nums) <= 1:
|
||||
return len(nums) # 如果数组长度为0或1,则返回数组长度
|
||||
|
||||
up = down = 1 # 记录上升和下降摆动序列的最大长度
|
||||
for i in range(1, len(nums)):
|
||||
if nums[i] > nums[i-1]:
|
||||
up = down + 1 # 如果当前数比前一个数大,则可以形成一个上升峰值
|
||||
elif nums[i] < nums[i-1]:
|
||||
down = up + 1 # 如果当前数比前一个数小,则可以形成一个下降峰值
|
||||
|
||||
return max(up, down) # 返回上升和下降摆动序列的最大长度
|
||||
|
||||
|
||||
```
|
||||
### Go
|
||||
|
||||
**贪心**
|
||||
|
@ -177,32 +177,33 @@ class Solution {
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
贪心 大饼干优先
|
||||
```python
|
||||
class Solution:
|
||||
# 思路1:优先考虑小胃口
|
||||
def findContentChildren(self, g: List[int], s: List[int]) -> int:
|
||||
g.sort()
|
||||
s.sort()
|
||||
res = 0
|
||||
for i in range(len(s)):
|
||||
if res <len(g) and s[i] >= g[res]: #小饼干先喂饱小胃口
|
||||
res += 1
|
||||
return res
|
||||
def findContentChildren(self, g, s):
|
||||
g.sort() # 将孩子的贪心因子排序
|
||||
s.sort() # 将饼干的尺寸排序
|
||||
index = len(s) - 1 # 饼干数组的下标,从最后一个饼干开始
|
||||
result = 0 # 满足孩子的数量
|
||||
for i in range(len(g)-1, -1, -1): # 遍历胃口,从最后一个孩子开始
|
||||
if index >= 0 and s[index] >= g[i]: # 遍历饼干
|
||||
result += 1
|
||||
index -= 1
|
||||
return result
|
||||
|
||||
```
|
||||
|
||||
贪心 小饼干优先
|
||||
```python
|
||||
class Solution:
|
||||
# 思路2:优先考虑大胃口
|
||||
def findContentChildren(self, g: List[int], s: List[int]) -> int:
|
||||
g.sort()
|
||||
s.sort()
|
||||
start, count = len(s) - 1, 0
|
||||
for index in range(len(g) - 1, -1, -1): # 先喂饱大胃口
|
||||
if start >= 0 and g[index] <= s[start]:
|
||||
start -= 1
|
||||
count += 1
|
||||
return count
|
||||
def findContentChildren(self, g, s):
|
||||
g.sort() # 将孩子的贪心因子排序
|
||||
s.sort() # 将饼干的尺寸排序
|
||||
index = 0
|
||||
for i in range(len(s)): # 遍历饼干
|
||||
if index < len(g) and g[index] <= s[i]: # 如果当前孩子的贪心因子小于等于当前饼干尺寸
|
||||
index += 1 # 满足一个孩子,指向下一个孩子
|
||||
return index # 返回满足的孩子数目
|
||||
|
||||
```
|
||||
|
||||
### Go
|
||||
|
@ -268,80 +268,56 @@ class Solution {
|
||||
|
||||
### Python
|
||||
|
||||
python3
|
||||
**回溯**
|
||||
|
||||
回溯 利用set去重
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.paths = []
|
||||
self.path = []
|
||||
|
||||
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
|
||||
'''
|
||||
本题求自增子序列,所以不能改变原数组顺序
|
||||
'''
|
||||
self.backtracking(nums, 0)
|
||||
return self.paths
|
||||
|
||||
def backtracking(self, nums: List[int], start_index: int):
|
||||
# 收集结果,同78.子集,仍要置于终止条件之前
|
||||
if len(self.path) >= 2:
|
||||
# 本题要求所有的节点
|
||||
self.paths.append(self.path[:])
|
||||
def findSubsequences(self, nums):
|
||||
result = []
|
||||
path = []
|
||||
self.backtracking(nums, 0, path, result)
|
||||
return result
|
||||
|
||||
def backtracking(self, nums, startIndex, path, result):
|
||||
if len(path) > 1:
|
||||
result.append(path[:]) # 注意要使用切片将当前路径的副本加入结果集
|
||||
# 注意这里不要加return,要取树上的节点
|
||||
|
||||
# Base Case(可忽略)
|
||||
if start_index == len(nums):
|
||||
return
|
||||
|
||||
# 单层递归逻辑
|
||||
# 深度遍历中每一层都会有一个全新的usage_list用于记录本层元素是否重复使用
|
||||
usage_list = set()
|
||||
# 同层横向遍历
|
||||
for i in range(start_index, len(nums)):
|
||||
# 若当前元素值小于前一个时(非递增)或者曾用过,跳入下一循环
|
||||
if (self.path and nums[i] < self.path[-1]) or nums[i] in usage_list:
|
||||
uset = set() # 使用集合对本层元素进行去重
|
||||
for i in range(startIndex, len(nums)):
|
||||
if (path and nums[i] < path[-1]) or nums[i] in uset:
|
||||
continue
|
||||
usage_list.add(nums[i])
|
||||
self.path.append(nums[i])
|
||||
self.backtracking(nums, i+1)
|
||||
self.path.pop()
|
||||
|
||||
uset.add(nums[i]) # 记录这个元素在本层用过了,本层后面不能再用了
|
||||
path.append(nums[i])
|
||||
self.backtracking(nums, i + 1, path, result)
|
||||
path.pop()
|
||||
|
||||
```
|
||||
**回溯+哈希表去重**
|
||||
回溯 利用哈希表去重
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.paths = []
|
||||
self.path = []
|
||||
def findSubsequences(self, nums):
|
||||
result = []
|
||||
path = []
|
||||
self.backtracking(nums, 0, path, result)
|
||||
return result
|
||||
|
||||
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
|
||||
'''
|
||||
本题求自增子序列,所以不能改变原数组顺序
|
||||
'''
|
||||
self.backtracking(nums, 0)
|
||||
return self.paths
|
||||
|
||||
def backtracking(self, nums: List[int], start_index: int):
|
||||
# 收集结果,同78.子集,仍要置于终止条件之前
|
||||
if len(self.path) >= 2:
|
||||
# 本题要求所有的节点
|
||||
self.paths.append(self.path[:])
|
||||
def backtracking(self, nums, startIndex, path, result):
|
||||
if len(path) > 1:
|
||||
result.append(path[:]) # 注意要使用切片将当前路径的副本加入结果集
|
||||
|
||||
# Base Case(可忽略)
|
||||
if start_index == len(nums):
|
||||
return
|
||||
used = [0] * 201 # 使用数组来进行去重操作,题目说数值范围[-100, 100]
|
||||
for i in range(startIndex, len(nums)):
|
||||
if (path and nums[i] < path[-1]) or used[nums[i] + 100] == 1:
|
||||
continue # 如果当前元素小于上一个元素,或者已经使用过当前元素,则跳过当前元素
|
||||
|
||||
used[nums[i] + 100] = 1 # 标记当前元素已经使用过
|
||||
path.append(nums[i]) # 将当前元素加入当前递增子序列
|
||||
self.backtracking(nums, i + 1, path, result)
|
||||
path.pop()
|
||||
|
||||
|
||||
# 单层递归逻辑
|
||||
# 深度遍历中每一层都会有一个全新的usage_list用于记录本层元素是否重复使用
|
||||
usage_list = [False] * 201 # 使用列表去重,题中取值范围[-100, 100]
|
||||
# 同层横向遍历
|
||||
for i in range(start_index, len(nums)):
|
||||
# 若当前元素值小于前一个时(非递增)或者曾用过,跳入下一循环
|
||||
if (self.path and nums[i] < self.path[-1]) or usage_list[nums[i]+100] == True:
|
||||
continue
|
||||
usage_list[nums[i]+100] = True
|
||||
self.path.append(nums[i])
|
||||
self.backtracking(nums, i+1)
|
||||
self.path.pop()
|
||||
```
|
||||
### Go
|
||||
|
||||
|
@ -326,7 +326,22 @@ class Solution:
|
||||
return root
|
||||
```
|
||||
|
||||
递归法(版本四)
|
||||
```python
|
||||
class Solution:
|
||||
def insertIntoBST(self, root, val):
|
||||
if root is None:
|
||||
node = TreeNode(val)
|
||||
return node
|
||||
|
||||
if root.val > val:
|
||||
root.left = self.insertIntoBST(root.left, val)
|
||||
if root.val < val:
|
||||
root.right = self.insertIntoBST(root.right, val)
|
||||
|
||||
return root
|
||||
|
||||
```
|
||||
|
||||
迭代法
|
||||
```python
|
||||
|
@ -356,76 +356,80 @@ Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
|
||||
res = []
|
||||
nums.sort()
|
||||
def backtracking(start, path):
|
||||
res.append(path)
|
||||
uset = set()
|
||||
for i in range(start, len(nums)):
|
||||
if nums[i] not in uset:
|
||||
backtracking(i + 1, path + [nums[i]])
|
||||
uset.add(nums[i])
|
||||
def subsetsWithDup(self, nums):
|
||||
nums.sort() # 去重需要排序
|
||||
result = []
|
||||
self.backtracking(nums, 0, [], result)
|
||||
return result
|
||||
|
||||
def backtracking(self, nums, startIndex, path, result):
|
||||
result.append(path[:])
|
||||
used = set()
|
||||
for i in range(startIndex, len(nums)):
|
||||
if nums[i] in used:
|
||||
continue
|
||||
used.add(nums[i])
|
||||
path.append(nums[i])
|
||||
self.backtracking(nums, i + 1, path, result)
|
||||
path.pop()
|
||||
|
||||
backtracking(0, [])
|
||||
return res
|
||||
```
|
||||
|
||||
**40. 组合总和 II**
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
|
||||
|
||||
res = []
|
||||
def combinationSum2(self, candidates, target):
|
||||
candidates.sort()
|
||||
result = []
|
||||
self.backtracking(candidates, target, 0, 0, [], result)
|
||||
return result
|
||||
|
||||
def backtracking(start, path):
|
||||
if sum(path) == target:
|
||||
res.append(path)
|
||||
elif sum(path) < target:
|
||||
used = set()
|
||||
for i in range(start, len(candidates)):
|
||||
if candidates[i] in used:
|
||||
continue
|
||||
else:
|
||||
used.add(candidates[i])
|
||||
backtracking(i + 1, path + [candidates[i]])
|
||||
|
||||
backtracking(0, [])
|
||||
def backtracking(self, candidates, target, sum, startIndex, path, result):
|
||||
if sum == target:
|
||||
result.append(path[:])
|
||||
return
|
||||
used = set()
|
||||
for i in range(startIndex, len(candidates)):
|
||||
if sum + candidates[i] > target:
|
||||
break
|
||||
if candidates[i] in used:
|
||||
continue
|
||||
used.add(candidates[i])
|
||||
sum += candidates[i]
|
||||
path.append(candidates[i])
|
||||
self.backtracking(candidates, target, sum, i + 1, path, result)
|
||||
sum -= candidates[i]
|
||||
path.pop()
|
||||
|
||||
return res
|
||||
```
|
||||
|
||||
**47. 全排列 II**
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
|
||||
path = []
|
||||
res = []
|
||||
used = [False]*len(nums)
|
||||
def permuteUnique(self, nums):
|
||||
nums.sort() # 排序
|
||||
result = []
|
||||
self.backtracking(nums, [False] * len(nums), [], result)
|
||||
return result
|
||||
|
||||
def backtracking():
|
||||
if len(path) == len(nums):
|
||||
res.append(path.copy())
|
||||
|
||||
deduplicate = set()
|
||||
for i, num in enumerate(nums):
|
||||
if used[i] == True:
|
||||
continue
|
||||
if num in deduplicate:
|
||||
continue
|
||||
def backtracking(self, nums, used, path, result):
|
||||
if len(path) == len(nums):
|
||||
result.append(path[:])
|
||||
return
|
||||
used_set = set()
|
||||
for i in range(len(nums)):
|
||||
if nums[i] in used_set:
|
||||
continue
|
||||
if not used[i]:
|
||||
used_set.add(nums[i])
|
||||
used[i] = True
|
||||
path.append(nums[i])
|
||||
backtracking()
|
||||
used[i] = False
|
||||
self.backtracking(nums, used, path, result)
|
||||
path.pop()
|
||||
deduplicate.add(num)
|
||||
|
||||
backtracking()
|
||||
used[i] = False
|
||||
|
||||
return res
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
|
Reference in New Issue
Block a user