0.该算法同时使用,有重复的全排列,无重复的全排列,字符串全排列,但需要注意的是字符串全排列传递的参数以及预设的变量不同,eg vetcor<string>
1.考虑重复的情况,逐个分支去遍历。弄懂算法。
2.再次基础上 理解if(i > 0 && num[i - 1] == num[i] && !vis[i - 1]),//当前的元素num[i]与同一层的前一个元素num[i-1]相同且num[i-1]已经用过了
出现此种情况可以舍弃分支,但这一定是建立再已经sort的基础上的。所以不管题目是否要求按字典大或小顺序输出都得排序,换句话说,该算法输出的结果一定是按照字典排序的。数注意该算法与前面的交换法时间复杂度、空间复杂度一样,前者胜在 代码结构简单。
class Solution {
public:
void recursion(vector<vector<int> > &res, vector<int> &num, vector<int> &temp, vector<int> &vis){
//临时数组满了加入输出
if(temp.size() == num.size()){
res.push_back(temp);
return;
}
//遍历所有元素选取一个加入
for(int i = 0; i < num.size(); i++){
//如果该元素已经被加入了则不需要再加入了
if(vis[i]) //控制循环
continue;
if(i > 0 && num[i - 1] == num[i] && !vis[i - 1]) //剪枝
//当前的元素num[i]与同一层的前一个元素num[i-1]相同且num[i-1]已经用过了
continue;
//标记为使用过
vis[i] = 1;
//加入数组
temp.push_back(num[i]);
recursion(res, num, temp, vis);
//回溯
vis[i] = 0;
temp.pop_back();
}
}
vector<vector<int> > permuteUnique(vector<int> &num) {
//先按字典序排序
sort(num.begin(), num.end());
//标记每个位置的元素是否被使用过
vector<int> vis(num.size(), 0);//vector初始化语法,指定长度为。size,且每个元素赋初值为0
vector<vector<int> > res;//传引用仅作为输出二位容器用
vector<int> temp;//记录当前暂存递归过程中组装的排列情况。
public:
void recursion(vector<vector<int> > &res, vector<int> &num, vector<int> &temp, vector<int> &vis){
//临时数组满了加入输出
if(temp.size() == num.size()){
res.push_back(temp);
return;
}
//遍历所有元素选取一个加入
for(int i = 0; i < num.size(); i++){
//如果该元素已经被加入了则不需要再加入了
if(vis[i]) //控制循环
continue;
if(i > 0 && num[i - 1] == num[i] && !vis[i - 1]) //剪枝
//当前的元素num[i]与同一层的前一个元素num[i-1]相同且num[i-1]已经用过了
continue;
//标记为使用过
vis[i] = 1;
//加入数组
temp.push_back(num[i]);
recursion(res, num, temp, vis);
//回溯
vis[i] = 0;
temp.pop_back();
}
}
vector<vector<int> > permuteUnique(vector<int> &num) {
//先按字典序排序
sort(num.begin(), num.end());
//标记每个位置的元素是否被使用过
vector<int> vis(num.size(), 0);//vector初始化语法,指定长度为。size,且每个元素赋初值为0
vector<vector<int> > res;//传引用仅作为输出二位容器用
vector<int> temp;//记录当前暂存递归过程中组装的排列情况。
使用额外的vis数组用于记录哪些位置的数字被加入了。
//递归获取
recursion(res, num, temp, vis);
return res;
}
};
//递归获取
recursion(res, num, temp, vis);
return res;
}
};