刷题不总结复习,等于白刷。
最近越来越意识到这个道理,于是决定开始把刷过的容易忘记的题总结一下。
一开始看到一些资料中说回溯法是有框架可以遵循的,并不是十分的理解,后来看了几个题目以后,包括dfs的一些题,总算是有了一些体会。
总结一下的框架就是
def backtrace()
if 满足条件:
结果中加入track
for 选择 in 选择列表:
做选择
backtrace()进入下一级
撤销选择 重点
1、全排列问题
#1.1 全排列1
Leetcode46全排列
对于这个问题,也没什么好说的了,直接上代码吧!!!
def permuate(nums):
def backtrace(nums,track,n):
if len(track) == n: #当track中达到n个,结束返回值到res中
res.append(track.copy()) #这里注意要用track.copy()
return
for i,num in enumerate(nums): #for 选择 in 选择列表:
if num in track: #这里属于附加条件,全排列问题要求不能重复
continue
track.append(num) #做选择
backtrace(nums,track,n) #进入下一级
track.pop() #撤销选择
res=[]
track=[]
n=len(nums)
backtrace(nums,track,n)
return res
if __name__=='__main__':
nums=[1,3,5]
print(permuate(nums))
C++代码也是一样,也来一份吧。。。。
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> permute(vector<int>& nums) {
vector<int> track;
backtrack(nums,track);
return res;
}
void backtrack(vector<int> nums,vector<int> track){
if(track.size()==nums.size()){
res.push_back(track);
return;
}
for(int i=0;i<nums.size();++i){
if(find(track.begin(),track.end(),nums[i])!=track.end()) continue;
track.push_back(nums[i]);
backtrack(nums,track);
track.pop_back();
}
}
};
你会了吗?
#1.2 全排列2
LeetCode47. 全排列 II
nums中有了重复的数字,这时候需要添加一个是否使用过的标志数组used,对nums做一个排列,然后在源代码的基础更改一下剪枝调剂即可,
[1,3,3],对于这样一个数组进行全排列,如果这个数字已经使用过(used[i]==1),那么重复,如果这个数字和前一个数字相同且前一位数字未使用过,也会重复,修改一下代码完成。
def permuate(nums):
def backtrace(nums,track,used,n):
if len(track) == n:
res.append(track.copy())
return
for i,num in enumerate(nums):
if used[i]:continue
if i>0 and nums[i]==nums[i-1] and used[i-1]==0:
continue
used[i]=1
track.append(num)
backtrace(nums,track,used,n)
used[i] = 0
track.pop()
res=[]
track=[]
nums.sort()
n = len(nums)
used=[0 for _ in range(n)]
backtrace(nums,track,used,n)
return res
if __name__=='__main__':
nums=[1,3,3]
print(permuate(nums))
2、组合问题
说完了全排列,再用公式来套一下组合问题吧。
#2.1LeetCode77组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
def combine(n,k):
def backtrace(st,n,track,k):
if len(track) == k:
res.append(track.copy())
return
for i in range(st,n+1):
if i in track:
continue
track.append(i)
backtrace(st+1,n,track,k) #重点在这里,下一次回溯st+1
track.pop()
res=[]
track=[]
backtrace(1,n,track,k)
return res
if __name__=='__main__':
print(combine(4,2))
C++代码也是一样~~
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> combine(int n, int k) {
vector<int> track;
backtrack(1,n,k,track);
return res;
}
void backtrack(int first,int ed,int k,vector<int> &track ){
if(track.size()==k)res.push_back(track);
for(int i=first;i<=ed;++i){
track.push_back(i);
backtrack(i+1,ed,k,track);
track.pop_back();
}
}
};
##2.2 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
一样的套路,直接上代码吧。。。。
class Solution:
def combinationSum(self, nums: List[int], target: int) -> List[List[int]]:
def backtrace(nums,track,target):
if sum(track) > target:
return
tmp=track.copy()
tmp.sort()
if sum(track) == target and tmp not in res:
res.append(tmp)
return
for i, num in enumerate(nums):
track.append(num)
backtrace(nums,track,target)
track.pop()
res=[]
track=[]
backtrace(nums,track,target)
return res
##2.2 组合总和2
稍微加一点改动,加一个used数组记录该数字是否被使用过,即可。
class Solution:
def combinationSum2(self, nums: List[int], target: int) -> List[List[int]]:
def backtrace(nums,track,target):
if sum(track) > target:
return
tmp=track.copy()
tmp.sort()
if sum(track) == target and tmp not in res:
res.append(tmp)
return
for i, num in enumerate(nums):
if used[i]: #如果数字被用过,跳过
continue
track.append(num)
used[i]=1
backtrace(nums,track,target)
track.pop()
used[i]=0
res=[]
track=[]
used=[0 for _ in range(len(nums))]
backtrace(nums,track,target)
return res
##2.3电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
有了上面的两题做铺垫,这个题目应该是小case了吧,让我来试试。。。
def letterCombinations(digits):
phone = {'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']}
def backtrace(digits,track,st,n):
if len(track) == n:
res.append(track)
return
digit=phone[digits[st]]
for letter in digit:
track += letter
backtrace(digits, track, st+1, n)
track = track[0:-1]
res = []
n = len(digits)
if digits:
backtrace(digits, "", 0, n)
return res
C++代码也一样啊
class Solution {
public:
vector<string> res;
unordered_map<char, string> phone{
{'0', " "}, {'1',"*"}, {'2', "abc"},
{'3',"def"}, {'4',"ghi"}, {'5',"jkl"},
{'6',"mno"}, {'7',"pqrs"},{'8',"tuv"},
{'9',"wxyz"}};
vector<string> letterCombinations(string digits) {
int n=digits.size();
if(digits == "") return res;
backtrace(digits, "", n,0);
return res;
}
void backtrace( string &digits, string track, int n,int k){
if(track.size() == n){
res.push_back(track);
return;
}
string tmp = phone[digits[k]];
for(char w : tmp){
track += w;
backtrace(digits,track,n, k+1);
track.pop_back();
}
return ;
}
};