全排列
什么是全排列
在有n个数的数组中取m个数按任意的顺序组合,叫做排列
而在有n个数的数组中n个数按任意的顺序组合,叫做全排列
例如数组[1,2,3,4]
他的全排列有
[1,2,3,4]
[1,2,4,3]
[1,3,2,4]
[1,3,4,2]
[1,4,2,3]
[1,4,3,2]
.
.
.
[4,2,3,1]
[4,3,1,2]
[4,3,2,1]
(举例太多了中间就省略了)
4个不重复的数的全排列有432*1总共24种组合。
可以推倒出n个不重复的数字的全排列有n!种可能
不重复数组的全排列
LeetCode 46.全排列
在LeetCode上有与上文描述的一致的全排列的题目。
关于不重复的数组的全排列,我们可以采用搜索回溯的方法来求解。
void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len){
// 所有数都填完了
if (first == len) {
res.emplace_back(output);
return;
}
for (int i = first; i < len; ++i) {
// 动态维护数组
swap(output[i], output[first]);
// 继续递归填下一个数
backtrack(res, output, first + 1, len);
// 撤销操作
swap(output[i], output[first]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int> > res;
backtrack(res, nums, 0, (int)nums.size());
return res;
}
含有重复数组的排序
对于含有重复的数组,在用回溯法全排列的时候,需要加上一个判断条件来去重。
首先我们将数组排序,这样我们的数组中的重复的数字都相邻在了一起,假设排序之后的数组为nums[n],用vis[n]来表示对应的nums[n]中的数有没有被使用过。如果nums[i]被使用过了,则vis[i]为1,否则为0。
class Solution {
vector<int> vis;
public:
void backtrack(vector<int>& nums, vector<vector<int>>& ans, int idx, vector<int>& perm) {
if (idx == nums.size()) {
ans.emplace_back(perm);
return;
}
for (int i = 0; i < (int)nums.size(); ++i) {
if (vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])) {
continue;
}
perm.emplace_back(nums[i]);
vis[i] = 1;
backtrack(nums, ans, idx + 1, perm);
vis[i] = 0;
perm.pop_back();
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> ans;
vector<int> perm;
vis.resize(nums.size());
sort(nums.begin(), nums.end());
backtrack(nums, ans, 0, perm);
return ans;
}
};