排列组合是回溯算法的经典问题,有固定的模板写法,包括重复元素以及非重复元素。下面总结一下leetcode中的排列组合问题。
排列
排列问题一般是对原数组进行交换,然后维护一个全局变量的结果集合,每当符合要求将当前状态下的原数组加入到结果集合之中。
题目: 46 全排列
vector<vector<int>> res;
vector<vector<int>> permute(vector<int>& nums) {
backtrack(nums, 0);
return res;
}
void backtrack(vector<int> & nums, int depth)
{
if(depth==nums.size()) res.emplace_back(nums);
for(int i=depth;i<nums.size();i++)
{
swap(nums[i], nums[depth]);
backtrack(nums, depth+1);
swap(nums[i], nums[depth]);
}
}
注意,backtrack里面depth表示的是深度,需要不断加1。
题目:47全排列 ||
包含重复元素的序列,需要返回不重复的全排列,如果不从结果出发(使用一个set<vector >保存),需要保证在每一层交换时,不能有两个重复元素别交换到头位置。
vector<vector<int>> res;
vector<vector<int>> permuteUnique(vector<int>& nums) {
// sort(nums.begin(), nums.end());
backtrack(nums, 0);
return res;
}
void backtrack(vector<int> & nums, int index)
{
if(index==nums.size()){
res.push_back(nums); return ;
}
// 去除重复的关键在于同一深度不能有相同的元素
unordered_set <int> record;
for(int i = index;i<nums.size();i++)
{
// cout << "test " << index << " " << i << endl;
// 因为换完顺序之后 不能保证index之后的元素其顺序性 如果使用这种不能交换元素
// for(int j=0;j<nums.size();j++) cout << nums[j] << " ";
// if(i>index && nums[i]==nums[i-1]) continue;
if(record.find(nums[i])!=record.end()) continue;
record.insert(nums[i]);
swap(nums[i], nums[index]);
backtrack(nums, index+1);
swap(nums[i], nums[index]);
}
}
组合
组合问题与排列问题不同,原数组是不能进行删除操作的,需要手动维护一个当前结果的序列(一般使用vector),然后每个元素都有添加与不添加两种选择。
这里的depth表示选择哪个元素 不再表示深度
经典的组合问题, 无重复元素,不重复解。
需要注意的是数字可以无限制被选取。
无重复元素意味着不需要对每个选择的元素进行判断
无限制选取意味着下一次还可以从当前元素选起