输入一组数字(可能包含重复数字),输出其所有的排列方式。
样例
输入:[1,1,1,2]
输出:
[
[1,1,1,2],
[1,1,2,1],
[1,2,1,1],
[2,1,1,1],
]
最暴力的解法:
- 当重复的数字不存在,做正常的全排列,最后在结果集中去重即可。
另外一种解法:
- 找重复数字在排列的中的规律
例如:
输入:[1,1,1,2,3] 里面的重复数字是三个 1,考虑三个 1 的顺序
- - - - -
1 1 1 - - [1,1,1,2,3]、[1,1,1,3,2]
1 1 - 1 -
1 1 - - 1
1 - 1 1 -
1 - 1 - 1
1 - - 1 1
- 1 1 1 -
- 1 1 - 1
- 1 - 1 1
- - 1 1 1
这就是三个 1 的所有情况。会感觉到有一种最外层的 1 慢慢的压到最右边
实际上,当确定了 1 的位置的时候,下一个 1 一定得在上一个 1 的右边,保证一个相对位置。
如果当 1 放完了,就算后面还有其他重复的数字,也只需要按照放 1 的规则在空的位置上放就行了(如果没有重复那就是自己随便跑到哪个空位都可以)。
而此时所有情况的个数就是:C(5, 2) = 10 (五个位置选三个放 1 ),因为剩下的两个位置留个了 [2, 3] 他们在空位上选相当于 A(2, 2) = 2(2,3和3,2是不一样的)。
所以不重复的全排列个数 = C(5, 2) * A(2, 2) = 20 种
求全部情况的部分代码:
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> permutation(vector<int>& nums) {
path.resize(nums.size());
sort(nums.begin(), nums.end());
dfs(nums, 0, 0, 0);
return ans;
}
void dfs(vector<int>& n, int u, int start, int state){
if(u == n.size()){
ans.push_back(path);
return;
}
if(!u || n[u] != n[u - 1])start = 0;//如果是第一个数,或者,与前一个数不相等
for (int i = start; i < n.size(); i ++ )
if(!(state >> i & 1)){//第i位为 0,可放数据
path[i] = n[u];
dfs(n, u + 1, i + 1, state + (1 << i));//将第i位置为1
}
}
};
求C(n, m) 代码(杨辉三角):
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
int f[N][N];
int main(){
for(int i = 0; i < N; i++ ){
for(int j = 0; j <= i; j++ ){
if(!j)f[i][j] = 1;//第一列时,都为1
else f[i][j] = f[i - 1][j] + f[i - 1][j - 1];//其他则为两数相加
}
}
int n,m;
cin >> n >> m;
cout << "C(n, m):" << f[n][m];