给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
作为一道经典的回溯法题目,全排列的问题时回溯法入门的基础。
首先,给定一个没有重复数字,总长为N的序列,其可能的排列组合为 A N N − 1 A^{N-1}_N ANN−1
借用从全排列问题开始理解“回溯搜索”算法的图:
可以看出,当“遍历”完每一个可能的节点之后的排列,就是一个符合条件的组合。
class Solution {
private:
vector<int> path; // 一种选择的结果
vector<vector<int>> result; // 各种排列的结果
unsigned long n_size; // 需要排列的数字总数
public:
void backTrack(vector<int> nums){
if(path.size() == n_size){
result.push_back(path); // 一种选择的结果符合要求,即将其添加到result中
return;
}
for(int i = 0; i < nums.size(); i++){
// 不符合一种排列的要求
path.push_back(nums[i]); // 将当前选择压入路径中,即选择以nums[i] 为起点的路径
vector<int> tmp = nums;
tmp.erase(tmp.begin()+i); // nums[i] 已经被选择,因而将nums[i] 排除在之后要选择的路径之外
backTrack(tmp); // 递归解决
path.pop_back(); // 以nums[i]为起点的路径已经遍历结束,将节点弹出路径的选择
}
}
vector<vector<int>> permute(vector<int>& nums) {
this->n_size = nums.size();
backTrack(nums);
return result;
}
};
给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
示例:
输入: S = "a1b2"
输出: ["a1b2", "a1B2", "A1b2", "A1B2"]
输入: S = "3z4"
输出: ["3z4", "3Z4"]
输入: S = "12345"
输出: ["12345"]
由题意可以得出,当遍历字符串“遇到”字符时会发生两种结果:
根据回溯法可以大致推出代码:
class Solution {
private:
unsigned int strLen; // S 字符串的长度
vector<string> result; // 组合结果
public:
vector<string> letterCasePermutation(string S) {
this->strLen = S.length();
generate(0, S);
return result;
}
void generate(int i, string S){
if(i == strLen){
// 已经“遍历”完S字符串的最后一个字符
result.push_back(S); // 将这个结果插入到result 中
return;
}
if(S[i] >= 'A' && S[i] <= 'Z'){
// 情况 1
generate(i+1, S);
// 原字符是大写,现在进行小写情况的推理
S[i] += 32;
generate(i+1, S);
}
else if(S[i] >= 'a' && S[i] <= 'z'){
// 情况 2
generate(i+1, S);
// 原字符时小写,现在进行大写情况的推理
S[i] -= 32;
generate(i+1, S);
}
else{
// 数字一遍过
generate(i+1, S);
}
}
};
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
还是使用回溯法的方式来解决这道问题。
从nums数组的第一个数字开始算起,之后的每一次修改path中的成员都是一种全新的组合。
以[1, 2, 3]为例, 当选择第一个数字1时,path此时就是[1];但nums数组未遍历完毕,因此此时仍需使用深度遍历算法来遍历剩余的数字。
很明显,在以1开始的“查找”过程中,增加的子集为[1], [2], [3], [1, 2, 3];
在以2开始的“查找”过程中,增加的子集为[2], [3], [2, 3].
最后由于空集是所以集合的子集,最后加上一个空集就是所有的集合。
class Solution {
private:
vector<int> path;
vector<vector<int>> res;
vector<int> numsCopied;
public:
void DFS(int begin){
for(int i = begin; i < numsCopied.size(); i++){
path.push_back(numsCopied[i]);
res.push_back(path);
DFS(i+1);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
if(nums.empty()){
return res;
}
numsCopied = nums;
DFS(0);
vector<int> nullSet;
res.push_back(nullSet);
return res;
}
};