给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合
candidates 中的数字可以无限制重复被选取。
示例1
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例2
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
思路: 回溯法法,如果这条路走不下去,就退回一级,重新选择路径,一直往下走,直到 target 等于 0 就添加到返回列表中,如果 target 小于 0 则返回
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if(candidates.size()==0){
return res;
}
combinationSum(candidates, target,0);
return res;
}
void combinationSum(vector<int>& candidates,int target,int index){
if(target < 0){
return;
}
if(target == 0){
res.push_back(temp);
}
for(int i = index;i<candidates.size();++i){
temp.push_back(candidates[i]);
combinationSum(candidates, target-candidates[i],i);
temp.pop_back();
}
}
private:
vector<vector<int>>res;
vector<int>temp;
};
python 写法
def combinationSum(candidates, target):
length = len(candidates)
res = []
def combinationSum_in(target,index,maybe):
if target<0:
return
if target==0:
res.append(maybe)
for i in range(index,length):
combinationSum_in(target-candidates[i],i,maybe+[candidates[i]])
combinationSum_in(target,0,[])
return res
如果 candidates 中的每个数字在每个组合中只能使用一次,该怎么修改代码?
所有数字(包括目标数)都是正整数,解集不能包含重复的组合
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
思路:同样也是回溯法,只不过每个元素只能使用一次,所以我们先得排序,按照升序或者降序的方式,在满足一个条件之后进行判断前一个元素和后一个元素是否相等,如果相等就跳过,以防重复
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
if(candidates.empty()){
return res;
}
sort(candidates.begin(),candidates.end());
combinationSum2(candidates,target,0);
return res;
}
void combinationSum2(vector<int>& candidates, int target,int index){
if(target<0){
return;
}
if(target == 0){
res.push_back(temp);
return;
}
for(int i = index;i<candidates.size();++i){
// 这个 if 语句还是挺难理解的,
// 目的防止在 candidates 中有重复的值被重复计算
// 而这个 if 语句在回溯的时候才会去判断,插入的时候不会判断
if(i>index && candidates[i] == candidates[i-1]){
continue;
}
// 下面这个 if 语句加不加无所谓,加上程序跑的快
// 如果当前的 candidates[i] 都已经大于 target 了
// 就说明这条路肯定不行,需要退回去
if(candidates[i]>target){
return;
}
temp.push_back(candidates[i]);
combinationSum2(candidates,target-candidates[i],i+1);
temp.pop_back();
}
}
private:
vector<vector<int>>res;
vector<int>temp;
};
这里还有一个注意事项,就是不能使用 set 函数对 candidates 进行去重,题目说的是 candidates 中的每个值都仅能使用一次,并不是说一个数字只能出现一次,这里还是有点绕的.
python 写法
def combinationSum2(candidates, target):
c = sorted(candidates)
res = []
len_c = len(c)
def dfs(target, index, path):
if target == 0:
res.append(path)
return
for i in range(index, len_c):
# 这个回溯就是满足了一个条件,那么i 一定大于 index
# 在刚开始的阶段 i == index
if i>index and c[i] == c[i-1]:
continue
if c[i]>target:
break
dfs(target-c[i], i+1, path+[c[i]])
# python 这里不是没有回退,而是上一行代码使用
# 的是 path + [c[i]] 采用 + 的方法,返回去的
# 话就直接减去了,没必要自定制的弹出
dfs(target, 0, [])
return res
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
解题思路:回溯法,在每一次的遍历都需要进行标记,如果访问了这个元素就标为 1,弹出这个元素就标 0 ,为的就是回溯的时候可以不重复
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<int>used(nums.size(),0);
dfs(used,nums);
return res;
}
void dfs(vector<int>&used,vector<int>& nums){
if(temp.size() == nums.size()){
res.push_back(temp);
return;
}
for(int i = 0;i<nums.size();++i){
if(used[i] !=0){
continue;
}
temp.push_back(nums[i]);
used[i] = 1;
dfs(used,nums);
temp.pop_back();
used[i] = 0;
}
}
private:
vector<vector<int>>res;
vector<int>temp;
};
python 写法
def permute(nums):
res = []
def dfs(num, path):
if not num:
res.append(path)
return
for i in range(len(num)):
dfs(num[:i]+num[i+1:], path+[num[i]])
dfs(nums, [])
return res
另一种方法是在 python 中存在库函数
import itertools
def permute(nums: List[int]):
return list(itertools.permutations(nums))
如果给定一个可包含重复数字的序列,要求返回所有不重复的全排列,代码应该如何修改?
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
可以增加一个变量,判断是否重复,重复的话就直接跳过
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<int>Isused(nums.size(),0);
dfs(nums,Isused);
return res;
}
void dfs(vector<int>&nums,vector<int>&Isused){
if(nums.size() == temp.size()){
res.push_back(temp);
return;
}
int r = INT_MAX;
for(int i = 0;i<nums.size();++i){
if(Isused[i] != 0 || nums[i] ==r){
continue;
}
r = nums[i];
Isused[i] = 1;
temp.push_back(nums[i]);
dfs(nums,Isused);
Isused[i] = 0;
temp.pop_back();
}
}
private:
vector<vector<int>>res;
vector<int>temp;
};
python 版本
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
nums.sort()
res = []
def dfs(nums,path):
if not nums:
res.append(path)
return
for i in range(len(nums)):
if i>0 and nums[i]==nums[i-1]:
continue
dfs(nums[:i]+nums[i+1:],path+[nums[i]])
dfs(nums,[])
return res
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径
示例:
给定如下二叉树,以及目标和 sum = 22
返回:
[
[5,4,11,2],
[5,8,4,5]
]
参考代码
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
dfs(root,sum,0);
return res;
}
void dfs(TreeNode* root, int sum,int cur){
if(root == nullptr){
return;
}
path.push_back(root->val);
cur+=root->val;
if(root->left == nullptr && root->right == nullptr\
&& sum == cur){
res.push_back(path);
}
dfs(root->left,sum,cur);
dfs(root->right,sum,cur);
path.pop_back();
}
private:
vector<vector<int>>res;
vector<int>path;
};
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
回溯法
class Solution {
public:
unordered_map<char,string>um{
{'0'," "},{'1',"*"},{'2',"abc"},
{'3',"def"},{'4',"ghi"},{'5',"jkl"},
{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},
{'9',"wxyz"}
};
vector<string> letterCombinations(string digits) {
vector<string>res;
if(digits == ""){
return res;
}
dfs(res,"",digits,0);
return res;
}
void dfs(vector<string>&res,string str,\
string& digits,int k){
// 满足条件则进行插入返回
if(str.size() == digits.size()){
res.push_back(str);
return;
}
// 取出对应的字符串.
string temp = um[digits[k]];
for(char e:temp){
str +=e;
dfs(res,str,digits,k+1);
str.pop_back();
}
return;
}
};
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
示例
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
假设 我们的数1376542
,我们第一要找的位置就是 3 ,把 3 和 4 进行交换,然后对3后面的数字进行翻转。
可以看出,需要改变的子数组的起始数为 从后往前第一个nums[i]>nums[i-1]的数k,即3
要把起始数k替换为从后往前第一个大于k的数,即4,交换两数变为1476532
翻转i之后的一半数组
有一种特殊情况:倒序遍历到0都没有碰到 nums[i]>nums[i-1] 的情况,直接翻转整个数组 最后结果就是:1423567
def nextPermutation(self, nums):
n = len(nums)
i = j = n-1
while i > 0 and nums[i-1] >= nums[i]:
i -= 1
k = i - 1 # find the last "ascending" position
if k>=0:
while nums[j] <= nums[k]:
j -= 1
nums[k], nums[j] = nums[j], nums[k]
l, r = k+1, n-1 # reverse the second part
while l < r:
nums[l], nums[r] = nums[r], nums[l]
l +=1 ; r -= 1
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例1
输入: [1,2,0]
输出: 3
示例2
输入: [3,4,-1,1]
输出: 2
示例3
输入: [7,8,9,11,12]
输出: 1
思路:首先进行判空,如果为空返回1,然后找到最小的值,对最小的值进行分情况讨论,如果大于1,则返回1,如果小于等于1,就设置一个变量,赋初始化为1,从1开始判断它在不在数组中,如果在就一直加,如果不在就返回当前变量。
def firstMissingPositive(self, nums: List[int]) -> int:
if not nums:
return 1
if min(nums)>1:
return 1
if min(nums)<=1:
i = 1
while i in nums:
i+=1
return i
或者这样也行,直接判断1在不在当前数组中,如果不在就返回1,就不用考虑空数组和最小值大于1的数组了。
def firstMissingPositive(self, nums: List[int]) -> int:
if 1 not in nums:
return 1
if min(nums)<=1:
i = 1
while i in nums:
i+=1
return i
当然也可是使用桶排序,先初始化 n 个桶,值都为True,对数组进行遍历,遇到大于n和小于1的数不用管,如果遍历的一个数在1到n之间,则把相应的桶设置为True,遍历完数组后,第一桶是True的就是返回的数字。
def firstMissingPositive(self, nums):
if 1 not in nums:
return 1
n = len(nums)
bucket = [True]*(n+1)
for i in range(n):
if 0<nums[i]<=n:
bucket[nums[i]] = False
for i in range(1,n+1):
if bucket[i]:
return i
return n+1