- Permutation全排列
给定当前排列,如何得到下一个排列
1. 对于排列a[1...n],找到所有满足a[k] < a[k+1] 的k的最大值,如果这样的k不存在,则说明当前排列已经是a的所有排列中字典序最大者,所有排列输出完毕。
2. 在a[k+1...n]中,寻找满足这样条件的元素l,使得在所有a[l]>a[k]的元素中,a[l]取得最小值。也就是说a[l]>a[k],但是小于所有其他大于a[k]的元素。
3. 交换a[l]与a[k].
4. 对于a[k+1...n],反转该区间内元素的顺序。这样就得到了a[1...n]在字典序中的下一个排列。
public class Solution {
public void nextPermutation(int[] nums) {
if(nums==null || nums.length <= 1)
return;
int i=nums.length-2;
while(i>=0 && nums[i]>=nums[i+1]) i--;
if(i>=0){
int j = nums.length-1;
while(nums[j]<=nums[i])j--;
sw(nums,i,j);
}
re(nums,i+1,nums.length-1);
}
public void sw(int[] nums,int i,int j){
int tmp = nums[i];
nums[i]=nums[j];
nums[j]=tmp;
}
public void re(int[] nums,int i,int j){
while(i<j)
sw(nums,i++,j--);
}
}
STL中有next_permutation这个函数
找到前一个排列的算法和找下一个的算法类似
给定n,选择n中的m个数进行全排列,并输出。例如n=3 m=1 输出为1 2 3
递归,搜索
#include<cstdio>
#include<cstring>
int visit[10];
int a[10];
int n,m;
void permutation(int i)
{
int j,k;
if(i>m)
{
for(k=1;k<=m;++k)
printf("%d",a[k]);
printf("\n");
}
else
{
for(j=1;j<=n;++j)
{
if(visit[j]==0)
{
visit[j]=1;
a[i]=j;
permutation(i+1);
visit[j]=0;
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(visit,0,sizeof(int));
permutation(1);
}
return 0;
}
给定一个排序,输出在全排列中排第几 或者 给定n,输出全排列中第n个排列
康托展开公式
X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!
其中a[i]为当前未出现的元素中是排在第几个(从0开始)
适用范围:没有重复元素的全排列
题目:找出第16个n = 5的序列(12345)
首先第十六个也就是要前面有15个数
1. 根据第一行的那个全排列公式,15 / 4! = 0 …15 =》 有0个数比他小的数是1,所以第一位是1
2. 拿走刚才的余数15,用15 / 3! = 2 …3 => 剩下的数里有两个数比他小的是4(1已经没了),所以第二位是4
3. 拿走余数3, 用 3 / 2! = 1 …1 =》 剩下的数里有一个数比他小的是3,所以第三位是3
4. 拿走余数1, 用 1/ 1! = 1 …0 => 剩下的数里有一个数比他小的是 5(只剩2和5了),所以第四位是5
所以排列是 1,4,3,5,2
第二类题 :已知是n = 5,求14352是它的第几个序列?(同一道题)
用刚才的那道题的 反向思维 :
1. 第一位是1,有0个数小于1,即0* 4!
2. 第二位是4,有2个数小于4,即2* 3!
3. 第三位是3,有1个数小于3,即1* 2!
4. 第四位是5,有1个数小于5,即1* 1!
5. 第五位是2,不过不用算,因为肯定是0
所以14352是 n = 5的第 0 + 12 + 2 + 1 + 0 = 15 + 1(求的是第几个,所以要加一) = 16