组合问题
77. Combinations
class Solution:
def combine(self, n, k):
res = []
def dfs(k, tmp, start):
if k == 0:
res.append(tmp.copy())
return
for num in range(start, n + 1):
tmp.append(num)
dfs(k - 1, tmp, num + 1)
tmp.pop()
dfs(k, [], 1)
return res
227. Basic Calculator II
class Solution:
def combinationSum3(self, k, n):
res = []
def dfs(k, start, sum, tmp):
if k == 0:
if sum == 0:
res.append(tmp.copy())
return
for i in range(start, 10):
tmp.append(i)
dfs(k-1, i+1, sum-i, tmp)
tmp.pop()
dfs(k, 1, n, [])
return res
def combinationSum3_2(self, k: int, n: int) -> List[List[int]]:
res = []
def dfs(k, start, sum, tmp):
if k == 0:
if sum == 0:
res.append(tmp.copy())
return
for i in range(start, 10):
# 剪枝
if k > 1 and sum - i <= 0 :
continue
else:
tmp.append(i)
dfs(k-1, i+1, sum-i, tmp)
tmp.pop()
dfs(k, 1, n, [])
return res
17. Letter Combinations of a Phone Number
class Solution:
def letterCombinations(self, digits):
if digits == '':
return []
dic = {'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']}
res = []
def dfs(index, tmp):
if index == len(digits):
res.append(tmp)
return
for char in dic[digits[index]]:
tmp += char
dfs(index+1, tmp)
tmp = tmp[:-1]
dfs(0, '')
return res
39. Combination Sum
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
candidates = sorted(candidates) # 剪枝需要
def dfs(start, target, tmp):
if target == 0:
res.append(tmp.copy())
return
for index in range(start, len(candidates)):
# 剪枝
if target - candidates[index] < 0:
break
tmp.append(candidates[index])
dfs(index, target-candidates[index], tmp)
tmp.pop()
dfs(0, target, [])
return res
40. Combination Sum II
- 只能用一次!
- 去重trick:搜过的就不用再搜了,continue掉;ps,先sort,那么就是跟前面对比下一不一样就知道搜过没了;
if index > start and candidates[index - 1] == candidates[index]: continue
!!!重要!- 另外剪枝了
- 因为只能用一次,判断下当前到最后还够不够,一个点不够了,后面的点就都不够了;break掉;
- 和超过target;由于sort过了,所以break掉;
class Solution:
def combinationSum2(self, candidates, target):
res = []
candidates = sorted(candidates)
def dfs(start, target, tmp):
if target == 0:
if tmp not in res:
res.append(tmp.copy())
return
for index in range(start, len(candidates)):
if sum(candidates[start:]) < target:
break
if target - candidates[index] < 0:
break
if index > start and candidates[index - 1] == candidates[index]:
continue
tmp.append(candidates[index])
dfs(index+1, target-candidates[index], tmp)
tmp.pop()
dfs(0, target, [])
return res
分割问题
131. Palindrome Partitioning
2022-5-1:五一好!今天想把backtracking攻下来耶!好吧!
先说了,又掉进🕳里了,这里树杈的的划分是按当前元素到结尾的划分case:如上图:
aab
当前元素假设是第一个a
,那么我有三种划分方式a
,aa
.aab
;是吧!我掉什么🕳里了呢?跟集合元素要不要那种🕳想搞二叉哈哈哈哈,后来发现tmp不好加;
class Solution:
def partition(self, s: str) -> List[List[str]]:
res, tmp = [], []
def dfs(i):
if i == len(s):
res.append(tmp.copy())
for j in range(i, len(s)):
if self.helper(s, i, j):
tmp.append(s[i:j+1]) # 不要以为你变了我就认不出来了,我的确第一次没认出来!哭!
dfs(j+1)
tmp.pop()
dfs(0)
return res
def helper(self, s, i, j):
# 基操双指针~
left, right = i, j
while left < right:
if s[left] != s[right]:
return False
left, right = left + 1, right - 1
return True
93. Restore IP Addresses
- 几个点需要处理:
- 每个数字都要用到,且树高为4,所以以
k>4
和i==len(s)
作为判断条件; - 不是个位数的前导零的处理;
- debug的时候出现
ValueError: invalid literal for int() with base 10
的情况,测试示例为"101023"
,存在情况当tmp=[10,10,23]
,此时树深没有达到要求,仍会往下走,看了一下应该是在单层循环时for j in range(i, i+4)
这里出现问题,有可能j会超出len(s),所以下面加了判断条件超过了就可以break;
- 每个数字都要用到,且树高为4,所以以
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
res, tmp = [], []
def dfs(i, k):
if k > 4:
if i == len(s):
res.append('.'.join(tmp.copy()))
return
for j in range(i, i+4):
if j == len(s): # 剪枝
break
if self.helper(s[i:j+1]):
tmp.append(s[i:j+1])
dfs(j+1, k+1)
tmp.pop()
dfs(0, 1)
return res
def helper(self, s):
if len(s) > 1 and s[0] == '0':
return
if 0 <= int(s) <= 255:
return True
子集问题
78. Subsets
- 解法2:区别于组合和分割的解法(restore的根节点),这里restore的是所有节点!妙蛙简直大一统!
class Solution:
def subsets(self, nums):
res, tmp = [], []
def dfs(i):
if i == len(nums):
res.append(tmp.copy())
return
tmp.append(nums[i])
dfs(i+1)
tmp.pop()
dfs(i+1)
dfs(0)
return res
def subsets2(self, nums):
res, tmp = [], []
def dfs(i):
res.append(tmp.copy())
if i == len(nums):
return
for j in range(i, len(nums)):
tmp.append(nums[j])
dfs(j + 1)
tmp.pop()
dfs(0)
return res
90. Subsets II
- 两个注意点:
- 需要sorted;[辅助后面剪枝]
- 同层判断是否前面有重复元素,剪枝;
class Solution:
def subsetsWithDup(self, nums):
res, tmp = [], []
nums = sorted(nums)
def dfs(i):
res.append(tmp.copy())
if i == len(nums):
return
for j in range(i, len(nums)):
if j > i and nums[j] == nums[j-1]:
continue
tmp.append(nums[j])
dfs(j+1)
tmp.pop()
dfs(0)
return res
491. Increasing Subsequences
逐渐上瘾哈哈哈哈哈
- 几个注意点:
- code中标记为1处:res加的还是叶子节点,不过需要len至少为2的叶子节点;
- code中标记为2处:判断不是递增序列,由于是递归所以只需和当前tmp的最后一位check一下就够了;
- code中标记为3处:解决重复元素剪枝问题;
class Solution:
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
res, tmp = [], []
def dfs(i):
if len(tmp) > 1: # 1.
res.append(tmp.copy())
if i == len(nums):
return
for j in range(i, len(nums)):
if len(tmp) > 0 and nums[j] < tmp[-1]: # 2.
continue
if nums[j] not in nums[i:j]: # 3.
tmp.append(nums[j])
dfs(j+1)
tmp.pop()
dfs(0)
return res
排列问题
46. Permutations
- 排列问题:不用再有startindex,因为是排列(顺序无关);
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
res, tmp = [], []
def dfs(k):
if k == len(nums):
res.append(tmp.copy())
for j in range(len(nums)): # 1.
if nums[j] not in tmp: # 2.
tmp.append(nums[j])
dfs(k + 1)
tmp.pop()
dfs(0)
return res
47. Permutations II
- 这道题有点意思,去重真的哈哈哈哈
- 困难的点在于哪里呢:
- 全排列;
- 存在重复元素;
- sorted基操了,然后需要注意的是不仅仅要判断当前和之前的元素是不是相同的,因为是全排列所以当之前的元素访问过了说明现在是下一层,而全排列的下一层是可以访问相同元素(重复的元素,index不同);但是如果是当前层之前的元素的visited的值为0,由于是在之前就已经restore好了结果,之后回溯恢复了现场哈哈哈,就是说当前层的若又重复元素则不用考虑!
visited[j-1]==0
用的好!!!
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
res, tmp = [], []
nums = sorted(nums)
visited = [0] * len(nums)
def dfs(k):
if k == len(nums):
res.append(tmp.copy())
for j in range(len(nums)):
if j > 0 and nums[j] == nums[j-1] and visited[j-1] == 0:
continue
if visited[j]==0:
tmp.append(nums[j])
visited[j] = 1
dfs(k+1)
visited[j] = 0
tmp.pop()
dfs(0)
return res
332. Reconstruct Itinerary
- 跟的nc
- 几个点注意下:
- 存在多种有效的行程,需要按字典排序;即在构建树时每个节点先进行排序,那么先搜索到的路径(一条就够了)就是最后的答案;
- 维护一个adj字典:key为当前的城市,value为当前城市可以飞到的城市list;这里又有trick:
ex:adj=[city1:[city2, city3]]
- 第一个是当在key=city1按已经sorted的value中选择了第一个元素city2,那么当飞机又飞到city1时此时我们就不能再选择city2了,所以我们应该把他从adj[city1]中删除;
- 但是考虑另一种情况,当我们选择的这个city2他不能飞到任何城市,而此时还没到结束的时候;既然我们先从city2开始dfs发现此路不通,于是我们要开始恢复现场,而为了保证下一次需要从city3开始dfs,同时我们也需要city1访问到city2!该怎么恢复city2才能让下次先从city3开始同时city2还在呢?
BINGO!把city2添加到list尾部就好啦!【所以实际上是一个queue哈】
说了这么多不如放图
class Solution:
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
adj = {u: [] for u, _ in tickets}
for u, v in sorted(tickets):
adj[u].append(v)
res = ['JFK']
def dfs(cur):
if len(res) == len(tickets) + 1: # Happy Ending ~
return True
if cur not in adj: # 没结束,但是此路不通
return False
tmp = list(adj[cur])
for city in tmp:
res.append(city)
adj[cur] = adj[cur][1:] # 飞过了,暂时删了你
if dfs(city): # 有一条成了就返回吧
return res
res.pop() # 恢复现场
adj[cur].append(city)
dfs('JFK')
return res
棋盘问题
79. Word Search
-
哭了 花费好多内存哈哈哈不过没有看啥思路自己尝试了一下~竟然可以!!!中间去debug两次记录一下出错的情况以及怎么去debug呀!其实我感觉debug的过程也是一种在误差中学习的感觉,给你一个feedback去penalty哈哈哈,又开始伪哲学,但是其实很多case比如cost function我们要想找到的是假设空间中的局部最优解,梯度下降的过程其实也是在误差中学习撒,哔哔赖赖已上线orz,自动屏蔽![反正也没人看 擅长自娱自乐哈哈哈哈]
-
ver1
- 首先是去找第一个word的char,也就是说我们要找到树根的位置,注意到board里是可以存放重复元素的,所以打算用start这个list存放所有根节点的index位置;
- visited这个数组存的是否在这棵树的这条路劲被访问了,注意哈这里有两个恢复现场的地方:一个是对根节点(这棵树行不行,他不当根节点还可以当别的树的非根节点呀),一个是对非根节点(这个节点被访问过了没,访问过就不用dfs了pass你);
- dfs中选择用tmp去存当前节点的所有可能的分支(一般来说是上下左右,但是考虑边界以及是否被访问过,估计会有更简洁的写法蹲蹲);
- dfs的终止条件:第一个是成功找到了,还有一个是还没check完所有的char,但是当前的char它没有分支了(tmp==[]),就是此路不通了,回溯吧那就,去别的节点看看~
debug:
- [[‘a’,‘a’]] ‘aaa’:查看了是根节点的visited忘记设置了;
- [[“C”,“A”,“A”],[“A”,“A”,“A”],[“B”,“C”,“D”]]
“AAB”:忘记把前一个根节点的visited回退了SAD
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
visited = [[0 for _ in range(len(board[0]))] for _ in range(len(board))]
start = []
# 找第一个元素的位置
for i in range(len(board)):
for j in range(len(board[0])):
if board[i][j] == word[0]:
start.append([i, j])
def dfs(i, j, k):
tmp = []
if i - 1 >= 0 and visited[i - 1][j] == 0:
tmp.append([i - 1, j])
if i + 1 < len(board) and visited[i + 1][j] == 0:
tmp.append([i + 1, j])
if j - 1 >= 0 and visited[i][j - 1] == 0:
tmp.append([i, j - 1])
if j + 1 < len(board[0]) and visited[i][j + 1] == 0:
tmp.append([i, j + 1])
if k == len(word):
return True
if tmp == []:
return False
for index in tmp:
if board[index[0]][index[1]] == word[k] and visited[index[0]][index[1]] == 0:
visited[index[0]][index[1]] = 1 # 访问这个节点
if dfs(index[0], index[1], k + 1):
return True
visited[index[0]][index[1]] = 0 # 这个节点不行,恢复现场
return False
# 对每个可能的位置
for index in start:
visited[index[0]][index[1]] = 1 # 访问根
if dfs(index[0], index[1], 1):
return True
visited[index[0]][index[1]] = 0 # 这棵树不行,恢复现场
return False
51. N-Queens
- 衰卡在一个点上没过去:在nc的解法下豁然开朗
- 皇后所在的行列对角线的位置需要更新,不需要访问;
- 卡住的点是:如何存放对角线的信息,需要存放index吗?如果把行列对角线的所有不需要访问的index放在一起,回溯的时候假设是当前对角线的位置恢复了,但是实际上是前一个皇后的列位置,这个时候位置是不能恢复的,该如何处理呢?
- nc的思路是单独存放列,对角线1,对角线2的信息;然后他有个trick很有意思,对对角线的处理妙蛙不用存放index了,用对角线的性质:
- diag1:行列index之差为某个数 save到set diag1中;
- diag2:行列index之和为某个数 save到set diag2中;
记一下这题还有set的初始化,add和remove操作,之前用的少,记录一哈!
- 另 修改现场和恢复现场 有丢丢感觉哈哈哈可以!
class Solution:
def solveNQueens(self, n):
res = []
board = [['.'] * n for _ in range(n)]
col = set()
diag1 = set()
diag2 = set()
def dfs(r):
if r == n:
tmp = ["".join(row) for row in board]
res.append(tmp)
return
for c in range(n):
if c in col or (r + c) in diag1 or (r - c) in diag2:
continue
board[r][c] = 'Q'
col.add(c)
diag1.add(r + c)
diag2.add(r - c)
dfs(r + 1)
board[r][c] = '.'
col.remove(c)
diag1.remove(r + c)
diag2.remove(r - c)
dfs(0)
return res
暂时完结 撒花~
晕感觉快把层序遍历忘完了【2022-5-2】
“平凡的我啊,哪有时间低头回望啊”
小排球田中仙贝的这句话好戳啊,飛べ!
22. Generate Parentheses
我又回来了orz
nc把这道题归在了stack那里,回溯做了一版
- 看上图就知道是个什么回事了:
- 两个变量l,r分别记录当前左右括号的个数,终止条件为当左括号个数达到n(右括号补齐就好了);
- 剪枝是考虑到符合条件的括号的情况,若当前左右括号数相等的时候只需要选择加左括号,即加右括号剪枝(啪!没了)
class Solution:
def generateParenthesis(self, n):
res = []
path = '('
def dfs(l, r, path):
if l == n:
tmp = path + ')' * (n-r)
res.append(tmp)
return
for i in [0, 1]:
if l == r and i == 1:
continue
if i == 0:
path += '('
l += 1
dfs(l, r, path)
path = path[:-1]
l -= 1
if i == 1:
path += ')'
r += 1
dfs(l, r, path)
path = path[:-1]
r -= 1
dfs(1, 0, path)
return res