版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目描述(来源:力扣(LeetCode))
给定一个可包含重复数字的序列,返回所有不重复的全排列。(输入的数范围为 1~n)
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
思路分析
大体框架是回溯或者DFS,其次针对该问题核心是如何避免重复的排列出现。
采取的可行的措施就是在每个数每次进入排列的某个位置的时候,我们可以设置一个标志,如使用last存第一次给该位置赋的值,在第二次给同样的位置赋值的时候应该与上一次该位置的赋值进行比较,若相等,就不能再放该数在目前位置上,如图:
第一次v = 1的第一层递归:从num [ 1 ] ,即第一个1开始进入排列数组,第一次递归排序无需考虑重复;
第一次v = 1第二层递归:
第二次v = 2开始第一层递归:从num [ 2 ],即第二个1开始进入排列数组,发现上一层递归中last == 1,那么num [ 2 ]此时不能进入排列数组,到num [ 3 ],即第一个2进入排列数组,并且更新last,last = 2,再进行递归即可。
整个过程
代码实现
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define Maxsize 1000
int n, visited[Maxsize];
int cunt;
int parent[Maxsize];
void Recall(int *, int, int);//回溯函数
int main()
{
int num[Maxsize], i;
cin >> n;//输入项数
for (i = 1; i <= n; i++)
cin >> num[i];
memset(parent,0,sizeof(int)*(n + 1));//初始化
memset(visited,0,sizeof(int)*(n + 1));
sort(num + 1,num + 1 + n);//库函数排序
Recall(num, 1, 0);//调用递归函数
cout << "一共有" << cunt << "种全排列" << endl;
return 0;
}
void Recall(int num[], int i, int cnt)
{
int v, w;
if (cnt == n)//全部排列进去后,结束该层递归
{
cunt++;//种类累计变量
for (w = 1; w <= n; w++)//输出该次排列元素
cout << parent[w] << " ";
cout << endl;
return;
}
int last = 0;
for (v = 1; v <= n; v++)
{
if (visited[v] == 0 && last != num[v])//没有被访问过,
{
parent[++cnt] = num[v];//进入排列
last = num[v];//改变最后一次该位的赋值
visited[v] = 1;//标记已访问
Recall(num, v, cnt);//递归下一层
visited[v] = 0;//上述递归完成后,回溯
cnt--;//消去上层递归已进入元素
}
}
}
运行结果