全排列问题
一、数组中不包含重复的数字
题目
思路
题目要求我们找出一个不包含重复数字的一个序列的全排列,对于全排列问题,我们可以这样进行思考:每一个位置所有数字都有可能进行选择,如:有数组[1, 2, 3]
我们求它的全排列,对于第一个位置有:[1, ], [2, ], [3, ]
,对于第二个位置(我们要依据于第一个位置进行判断,因为我们最终的数是一个长度为3的序列)有:([1, 2], [1, 3]),([2, 1],[2, 3]),([3, 1],[3, 2])
,对于第三个位置无疑是确定的(对于一个长度有限且已知的序列而言),那么最后的结果为:[1, 2, 3], [1, 3, 2],[2, 1, 3],[2, 3, 1],[3, 1, 2],[3, 2, 1]
,结果一共有6个不相同的排列。通过这个思路,我们可以设计出一个算法。
对于每一个位置,我们可以将当前位置和其之后位置的数进行填充,直到填充满位置
最简单的思路就是使用递归的思想,每次判断完成一个位置之后就进行递归判断下一个位置(需要注意的是,再进行递归之后还需要将位置进行换回来,保证这个位置的数字始终是和原来的数字进行做交换的)
为什么要换回来:
如1, 2, 3 在第一个位置的时候,循环的时候第一个数
i = pos
,首先pos
会和自己进行交换,得到[1, ]
,这个位置不换回来不影响最后的结果,但是在第二个数的时候i = pos + 1
,将pos
与pos + 1
进行了交换,如果不换回来,就会导致在第三个数的时候i = pos + 2
与pos + 1
进行交换了,这并不是与第一个位置上的数进行了交换,会导致结果出现错误。
核心代码
vector<vector<int>> ans; // 用于记录全排列的结果
void fullyArranged(vector<int> nums, int pos){ // 参数解释:nums传递过来的数组,pos当前的位置
// 首先判断边界问题,如果填充满了,就加入到结果数组中去
if(pos == int(nums.size() - 1)){
// 将结果进行添加
ans.push_back(nums);
return ;
}
// 进行位置交换
for(int i = pos + 1;i < nums.size();i ++){
swap(nums[pos], nums[i]); // 当前位置与之后的每一个位置进行交换
fullyArranged(nums, pos + 1); //每一个位置进行交换之后就进行判断下一个位置的情况
swap(nums[pos], nums[i]); // 交换之后还需要再交换回来
}
return ;
}
代码
调试代码
// 全排列问题
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
vector<vector<int>> ans; // 用于记录全排列的结果
// 不去重的全排列
void fullyArranged(vector<int>& nums, int pos){ // 参数解释:nums传递过来的数组,pos当前的位置
// 首先判断边界问题
if(pos == int(nums.size() - 1)){
// 将结果进行添加
ans.push_back(nums);
return ;
}
// 进行位置交换
for(int i = pos + 1;i < nums.size();i ++){
swap(nums[pos], nums[i]); // 当前位置与之后的每一个位置进行交换
fullyArranged(nums, pos + 1); //每一个位置进行交换之后就进行判断下一个位置的情况
swap(nums[pos], nums[i]); // 交换之后还需要再交换回来,以保证i始终是和num[pos]进行交换的。
}
return ;
}
// 查看全排列之后的结果
void Traverse(){
for(auto r : ans){
for(auto c : r) cout<<c<<' ';
cout<<endl;
}
// for(int i = 0;i < ans.size();i++){
// for(int j = 0;j < ans[i].size();j++){
// cout<<ans[i][j]<<' ';
// }
// cout<<endl;
// }
}
// 解决方案
void solve(){
// 举例:1,2,3的全排列
vector<int> nums = {1, 2, 3};
// 进行全排列
fullyArranged(nums, 0);
// 进行输出所有结果
Traverse();
return ;
}
int main(){
int t = 1;
while(t--){
solve();
}
return 0;
}
力扣提交代码
class Solution {
public:
vector<vector<int>> ans;
void fullArranged(vector<int> &nums, int pos){
// 如果满了进行存储
if(pos == nums.size() - 1){
ans.push_back(nums);
return ;
}
// 否则继续进行遍历
for(unsignd i = pos;i < nums.size();i++){
swap(nums[pos], nums[i]);
fullArranged(nums, pos + 1);
swap(nums[pos], nums[i]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
fullArranged(nums, 0);
return ans;
}
};
二、数组中含有重复的数字
题目
思路
包含重复数字的序列与不包含重复数字的序列的思想大体相同,只是在原有的基础上多进行判断了一次是否有重复数字。
解决方案:首先对序列进行排序,然后再每个位置进行判断的时候,都判断该位置之前是否有与之相同的数字进行过了一次交换,如果有:就进行跳过,否则:就进行交换
核心代码
vector<vector<int>> ans; // 用于记录全排列的结果
sort(nums.begin(), nums.end());
// 去重的全排列问题
void fullArrangedPro(vector<int>& nums, int pos){
if(pos == int(nums.size() - 1)){
ans.push_back(nums);
return ;
}
for(int i = pos;i < nums.size();i++){
bool flag = false; // 设定一个标记,用于标记是否有重复数字进行了交换
// 与之前交换过的所有数进行判断是否已经有相同的数进行了交换
for(int j = pos;j < i;j ++){
if(nums[j] == nums[i]){
flag = true;
}
}
// 进行正常的交换
if(!flag){
swap(nums[pos], nums[i]);
fullArrangedPro(nums, pos + 1);
swap(nums[pos], nums[i]);
}
}
return ;
}
代码
调试代码
// 全排列问题
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
vector<vector<int>> ans; // 用于记录全排列的结果
// 去重的全排列问题
void fullArrangedPro(vector<int>& nums, int pos){
if(pos == int(nums.size() - 1)){
ans.push_back(nums);
return ;
}
for(unsigned i = pos;i < nums.size();i++){
bool flag = false;
// 与之前交换过的所有数进行判断是否已经有相同的数进行了交换
for(int j = pos;j < i;j ++){
if(nums[j] == nums[i]){
flag = true;
}
}
if(!flag){
swap(nums[pos], nums[i]);
fullArrangedPro(nums, pos + 1);
swap(nums[pos], nums[i]);
}
}
return ;
}
// 查看全排列之后的结果
void Traverse(){
for(auto r : ans){
for(auto c : r) cout<<c<<' ';
cout<<endl;
}
}
// 解决方案
void solve(){
// 举例:1,2,3的全排列
vector<int> nums = {1, 3, 1};
// 进行全排列
// fullyArranged(nums, 0);
sort(nums.begin(), nums.end());
fullArrangedPro(nums, 0);
// 进行输出
Traverse();
return ;
}
int main(){
int t = 1;
while(t--){
solve();
}
return 0;
}
力扣提交代码
class Solution {
public:
// 可能包含重复数字
vector<vector<int>> ans;
void fullArrangedPro(vector<int>& nums, int pos){
if(pos == nums.size() - 1){
ans.push_back(nums);
return ;
}
// 继续进行判断
for(int i = pos;i < nums.size(); i++){
// 设定一个标记用于标记是否已经有重复的数字进行标记过了
bool flag = false;
// 循环遍历已经交换过的部分,判断是否有重复的数字
for(int j = pos;j < i;j ++){ // 注意这里不能再包含第i个数了,因为第i个数是需要判断的位置
if(nums[j] == nums[i]){
flag = true;
break;
}
}
// 如果没有重复的数,就对当前的位置进行交换位置
if(!flag){
swap(nums[pos], nums[i]);
fullArrangedPro(nums, pos + 1);
swap(nums[pos], nums[i]);
}
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
fullArrangedPro(nums, 0);
return ans;
}
};