一、求子集问题
Leetcode78:子集
题目描述:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
思路:
1.回溯法
2.Python的extend功能
方法1代码:
class Solution:
def subsets_way3(self,nums):
if not nums:
return []
res = []
lens = len(nums)
def helper(idx,temp_list):
res.append(temp_list)
for i in range(idx,lens):
helper(i+1,temp_list+[nums[i]])
helper(0,[])
return res
方法2代码:
def subsets_way1(nums):
result = [[]]
for x in nums:
result.extend([subset + [x] for subset in result])
return result
Leetcode90:子集II
题目描述:给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
这道题与78题区别在于,数组里包含了重复数组,一般有3种办法:
1.用上面一题方法,把相同的不加入
2.在递归时候,加个判断,发现此位置和前位置相等,就跳过(必须是有序列表)
3.先按78的思路,把重复的也算出来,再用dict遍历一遍,去重(这个方法比较笨重)
方法1、2代码:
class Solution1:
def subsets2(self,nums):
if not nums:
return []
n = len(nums)
res = []
nums.sort()
def helper(idx,temp_list):
if temp_list not in res:#相同的不加入
res.append(temp_list)
for i in range(idx,n):
helper(i+1,temp_list+[nums[i]])
def helper2(idx,temp_list):
res.append(temp_list)
for i in range(idx,n):
if i > idx and nums[i] == nums[i-1]:
continue
helper(i+1,temp_list+[nums[i]])
helper(0,[])
return res
方法3代码:
class Solution:
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
result = [[]]
for x in nums:
result.extend([subset + [x] for subset in result])
# print("result",result)
return self.transfer(result)
def transfer(self, res):#去重
Res = {}
for key,value in enumerate(res):
res[key] = sorted(value,reverse=True)
for key,value in enumerate(res):
if tuple(value) not in Res:
Res[tuple(value)] = 1
Res[tuple(value)] += 1
res = [list(key) for (key,value) in Res.items()]
return res
二、全排列
Leetcode46:全排列
题目描述:给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
思路:
1.直接调用Python的itertools.permutations包,不过返回的是tuple类型的;
2.回溯法,递归得到结果
(1)n个元素的全排列=(n-1个元素的全排列)+(另一个元素作为前缀);
(2)出口:如果只有一个元素的全排列,则说明已经排完,则输出数组;
(3)不断将每个元素放作第一个元素,然后将这个元素作为前缀,并将其余元素继续全排列,等到出口,出口出去后还需要还原数组;
方法1代码:
import itertools
def permutations1(nums):
return list(itertools.permutations(nums,len(nums)))
方法2代码:
res = []
def permutations2(s,k):
if k == len(s):
res.append(s.copy())
# print(s)
#第0个元素和后面的每个元素交换
#第1个元素和后面的每个元素交换
#......
#k:当前的交换位置(关注点),与其后的元素交换
for i in range(k,len(s)):
s[i],s[k] = s[k],s[i]#试探
permutations2(s,k+1)
s[i], s[k] = s[k], s[i] # 回溯
#比如123交换之后变为132,那么第二次循环的时候应该从123开始,
#而不是从132开始,所以要回溯
Leetcode47:全排列II
题目描述:给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
两种思路:
1.一种是列出所有的排列,再去重(笨重的方法)
2.递归,交换之前先判断是否已经出现重复的元素,若存在,就不交换
方法1代码:
import itertools
class Solution:
def permuteUnique(self,nums):
return list(itertools.permutations(nums,len(nums)))
def unique_permutation(self,candidates):#去重,把每个排列放入字典中,去掉重复的
candidates.sort()
result = []
Re = []
dict_ = {}
if len(result) == 1:
return result
else:
for i in candidates:
if i not in dict_:
dict_[i] = 1
else:
dict_[i] += 1
for item in dict_:
Re.append(item)
return Re
方法2代码:依旧全排列的思路,只不过需要定义一个set来存储已经交换过的元素值,也就是已经做过某个排列的首元素的值存放在set中,避免重复这种情形。
import copy
class Solution1(object):
def permuteUnique(self, nums):
self.res = []
self.f(nums,k=0)
return self.res
def f(self,num_list, k):
begindata = set() # 每层递归都要重置
if k == len(num_list):
print(num_list)
tmp = copy.copy(num_list)
self.res.append(tmp)
for i in range(k, len(num_list)):
if num_list[i] in begindata: # 已经做过某个排列首元素,则跳过这种情况
continue
begindata.add(num_list[i])
num_list[i],num_list[k] = num_list[k],num_list[i]#交换两个元素的位置
self.f(num_list, k + 1)
num_list[i], num_list[k] = num_list[k], num_list[i]#回溯
S = Solution1()
res = S.permuteUnique(nums=[1,2,2])
print(res)
三、组合总和
Leetcode39:组合总和
题目描述:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:所有数字(包括 target)都是正整数。解集不能包含重复的组合。
示例:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
思路1:回溯法递归求解
class Solution:
def combinationSum(self, candidates, target):
if not candidates:
return []
if min(candidates) > target:
return []
candidates.sort()
res = []
def helper(candidates_,target_,temp_list):
if target_ == 0:
# print(temp_list)
res.append(temp_list)
if target < 0:
return
for i in range(len(candidates_)):
# print(temp_list,candidates_[i])
if candidates_[i] > target_:
break
helper(candidates_[i:],target_-candidates_[i],temp_list+[candidates_[i]])
helper(candidates,target,[])
return res
Leetcode40:组合总和II
题目描述:给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
示例:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
思路1:回溯法,递归求解,因为不能重复使用列表中的元素,所以下一次递归要从下个元素开始(不包括自身)
class Solution1:
def combinationSum2(self, candidates, target):
if not candidates:
return []
if min (candidates) > target:
return []
candidates.sort ()
res = []
def helper(candidates_, target_, temp_list):
if target_ == 0 and temp_list not in res:
# print(temp_list)
res.append (temp_list)
if target < 0:
return
for i in range (len (candidates_)):
# print(temp_list,candidates_[i])
if candidates_[i] > target_:
break
helper (candidates_[i+1:], target_ - candidates_[i], temp_list + [candidates_[i]])
helper(candidates, target, [])
return res
四、分割回文串
Leetcode131:分割回文串
题目描述:给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。
示例:
输入: "aab"
输出:
[
["aa","b"],
["a","a","b"]
]
思路1:回溯法
题目要求返回所有的可能方案,所以依旧采用回溯算法。循环查看当前字符串的每一个可切分位置位;判断若在当前位置切分,前半部分是否是回文串。若是,则将前半部分存入当前解,并递归分割后半部分。
例如输入字符串为示例:
|a |a |b |
0 1 2 3
首先判断分割位1,发现前半部分‘a’是回文串,将‘a’存入temp_list,将后半部分‘ab’用作递归。当遍历到字符串尾端时,递归结束,将temp_list加入的res,最终返回res.
方法1代码:
class Solution (object):
def partition(self, s):
"""
:type s: str
:rtype: List[List[str]]
"""
if not s:
return []
res = []
lens = len(s)
def helper(idx,temp_list):
if idx == lens:
res.append(temp_list)
for i in range(idx,lens):
if s[idx:i+1] == s[idx:i+1][::-1]:
helper(i+1,temp_list+[s[idx:i+1]])
helper(0,[])
return res
S = Solution()
res = S.partition(s='aab')
print(res)
参考文献:回溯算法系列