电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:输入:“23”;输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
分析:回溯法
class Solution {
//总结字符串的全排列问题,用递归解决不同输入情形下嵌套for循环的个数的问题。
public:
vector<string> letterCombinations(string digits) {
unordered_map<char, string> table{
{'0', " "}, {'1',"*"}, {'2', "abc"},
{'3',"def"}, {'4',"ghi"}, {'5',"jkl"},
{'6',"mno"}, {'7',"pqrs"},{'8',"tuv"},
{'9',"wxyz"}};
vector<string> res;
if(digits == "") return res;
func(res, "", digits, table, 0);
return res;
}
void func(vector<string> &res, string str, string &digits, unordered_map<char, string> &m, int k){
if(str.size() == digits.size()){
res.push_back(str);
return;
}
string tmp = m[digits[k]];
for(char w : tmp){
str += w;
func(res, str, digits, m, k+1);
str.pop_back();
}
return ;
}
};
全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例: 输入: [1,2,3],
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
分析:回溯递归
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
if (nums.size() == 0)
return vector<vector<int>>{};
vector<vector<int>> res;
func(nums,0,res);
return res;
}
void func(vector<int> nums, int index, vector<vector<int>>& res){
if (index == nums.size()-1)
{
res.push_back(nums);
return;
}
for (int i = index; i < nums.size(); i++){
int temp = nums[index];
nums[index] = nums[i];
nums[i] = temp;
func(nums, index + 1, res);
temp = nums[index];
nums[index] = nums[i];
nums[i] = temp;
}
}
};
全排列II
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例: 输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
分析:回溯递归 ,用map去重,map的key唯一不重复,map统计每个字符出现的次数。每个位置为不重复的字符轮询一遍,若某个字符在之前已经出现完,则后续不再轮询该字符(用map的second值统计次数)。
//每个位置的字符 为不重复的字符轮询一遍。
class Solution {
public:
void backtrace(map<int, int>& m, int k, int n,
vector<int>& v, vector<vector<int> >& res) {
if (k == n) {
res.push_back(v);
return;
}
for (auto& p : m) {
if (p.second == 0) continue; //该字符已经在之前轮询完毕
--p.second;
v.push_back(p.first);
backtrace(m, k + 1, n, v, res);
++p.second;
v.pop_back();
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
map<int, int> m;
for (auto x : nums) ++m[x];
vector<vector<int> > res;
vector<int> v;
backtrace(m, 0, nums.size(), v, res);
return res;
}
};
组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:所有数字(包括 target)都是正整数。解集不能包含重复的组合。
示例 :输入: candidates = [2,3,6,7], target = 7,所求解集为:
[
[7],
[2,2,3]
]
分析: DFS深度优先搜索
class Solution {
public:
vector<int> tmp;
vector<vector<int>> ans;
void dfs(int now, int sum, int target, vector<int>& num)
{
if (sum > target)
return;
if (sum == target)
{
ans.push_back(tmp);
return;
}
//元素的第一个数依次从canditates的下标0~size()-1依次遍历,now其实为上一个所push节点的下标
for (int i = now; i < num.size(); i++)
{
tmp.push_back(num[i]);
dfs(i, sum + num[i], target, num);
tmp.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
dfs(0, 0, target, candidates);
return ans;
}
};
组合总和II
给定一个输入数字可重复的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
分析:用map的first把输入变成不重复的输入数组,用map的second变为不重复输入数组中每个值可重复作为解的次数。此题即可转换为组合总和题中的算法求解。
class Solution {
public:
vector<int> tmp;
vector<vector<int>> ans;
void dfs(map<int,int>::iterator now, int sum, int target, map<int,int>&mnum)
{
if (sum > target)
return;
if (sum == target)
{
ans.push_back(tmp);
return;
}
//元素的第一个数依次从canditates的下标0~size()-1依次遍历,now其实为上一个所push节点的下标
for (auto i = now; i!= mnum.end(); i++)
{
if (i->second == 0)
continue;
--(i->second);
tmp.push_back(i->first);
dfs(i, sum + i->first, target, mnum);
++(i->second);
tmp.pop_back();
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
map<int, int>mnum;
for (int value : candidates)
++mnum[value];
dfs(mnum.begin(), 0, target, mnum);
return ans;
}
};
组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>>res;
vector<int> tmp;
if (n <= 0||k>n)
return res;
if (n == k)
{
for (int i = 1; i <= n; i++)
tmp.push_back(i);
res.push_back(tmp);
return res;
}
dfs(n, k,1, res, tmp);
return res;
}
void dfs(int n, int k, int now, vector<vector<int>>&res, vector<int> tmp) {
if (k == tmp.size())
{
res.push_back(tmp);
return;
}
for (int i = now; i <= n; i++) {
tmp.push_back(i);
dfs(n, k, i + 1, res, tmp);
tmp.pop_back();
}
}
};
子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
分析:回溯递归
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>>res;
for (int i = 0; i <=nums.size(); i++)
{
vector<int> tmp;
dfs(nums, 0, i, res, tmp);
}
return res;
}
void dfs(vector<int>& nums,int now, int k, vector<vector<int>>&res, vector<int> tmp) {
if (tmp.size() == k)
{
res.push_back(tmp);
return;
}
if ((nums.size() - now + tmp.size() < k)|| (now >= nums.size()))
return;
for (int i = now; i <nums.size() ; i++) {
tmp.push_back(nums[i]);
dfs(nums, i+1,k, res, tmp);
tmp.pop_back();
}
}
};
子集II
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
示例:输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
分析:用map的first把输入变成不重复的输入数组,用map的second变为不重复输入数组中每个值可重复作为解的次数。此题即可转换为子集题中的算法求解。
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<vector<int>>res;
map<int, int>mnum;
for (int i : nums)
++mnum[i];
for (int i = 0; i <=nums.size(); i++)
{
vector<int> tmp;
dfs(mnum,mnum.begin(), i, res, tmp);
}
return res;
}
void dfs(map<int,int>&nums,map<int,int>::iterator now, int k, vector<vector<int>>&res, vector<int> tmp) {
if (tmp.size() == k)
{
res.push_back(tmp);
return;
}
for (auto i = now; i !=nums.end() ; i++) {
if (i->second == 0)
continue;
tmp.push_back(i->first);
(i->second)--;
dfs(nums, i,k, res, tmp);
tmp.pop_back();
(i->second)++;
}
}
};