全排列 方法:全排列函数和递归和字典序

题目描述

给你一个字符串,按字典序从小到大输出这个字符串的全排列

输入

一个由小写字母组成的长度小于等于8的不含重复字符的字符串

输出

按字典序从小到大输出这个字符串的全排列

样例输入

abc

样例输出

abc
acb
bac
bca
cab
cba

1

next_permutation函数


    组合数学中经常用到排列,这里介绍一个计算序列全排列的函数:next_permutation(start,end),和prev_permutation(start,end)。这两个函数作用是一样的,区别就在于前者求的是当前排列的下一个排列,后一个求的是当前排列的上一个排列。至于这里的“前一个”和“后一个”,我们可以把它理解为序列的字典序的前后,严格来讲,就是对于当前序列pn,他的下一个序列pn+1满足:不存在另外的序列pm,使pn<pm<pn+1.


对于next_permutation函数,其函数原型为:

     #include <algorithm>

     bool next_permutation(iterator start,iterator end)

当当前序列不存在下一个排列时,函数返回false,否则返回true

next_permutation(num,num+n)函数是对数组num中的前n个元素进行全排列,同时并改变num数组的值。

另外,需要强调的是,next_permutation()在使用前需要对欲排列数组按升序排序,否则只能找出该序列之后的全排列数。

此外,next_permutation(node,node+n,cmp)可以对结构体num按照自定义的排序方式cmp进行排序。

应用举例:

#include <iostream>  
#include <algorithm>  
using namespace std;  
int main()  
{  
    int num[3]={1,2,3};  
    do  
    {  
        cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;  
    }while(next_permutation(num,num+3));  
    return 0;  
}  


2.递归 但运行出来不是字典序  递归序

#include <iostream>
#include <string.h>
using namespace std;
void swap(char *a,int i,int j)
{
	char temp=a[i];
	a[i]=a[j];
	a[j]=temp;
}
void quanpailie(char array[],int len,int index)
{
	if(index==len)//全排列结束
	{
		cout<<array<<endl;
    }
    else
    {
    	for(int i=index;i<len;++i)
    	{
    		swap(array,index,i);//将第i个元素交换至当前index下标处 
    		quanpailie(array,len,index+1);//以递归的方式对剩下的元素进行全排列 
    		swap(array,index,i);//将第i个元素放回原处 
		}
	}
}	
int main()
{
	char array[8]={0};
	cin>>array;
	quanpailie(array,strlen(array),0);
	return 0;	
}

2.字典序

解题思路

设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn

  1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi<pi+1}
  2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=max{i|pi>pj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者)
  3)对换pi,pk

  4)再将pj+1......pk-1pkpk+1......pn倒转得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个排列。

证明


算法步骤1,得到的子串 s = {pj+1,.....,pn}, 是按照从大到小进行排列的。即有 pj+1 > pj+2 > ... > pn, 因为 j=max{i|pi<pi+1}。
算法步骤2,得到了最小的比pj大的pk,从n往j数,第一个比j大的数字。将pk和pj替换,保证了替换后的数字比当前的数字要大。
于是得到的序列为p1p2...pj-1pkpj+1...pk-1pjpk-1...pn.注意这里已经将pk替换成了pk。
这时候我们注意到比p1..pj-1pk.....,恰好比p1....pj.....pn大的数字集合。我们在这个集合中挑选出最小的一个即时所要求的下一个排列。
算法步骤3,即是将pk后面的数字逆转一下(从从大到小,变成了从小到大。)
由此经过上面3个步骤得到的下个排列时恰好比当前排列大的排列。
同时我们注意到,当所有排列都找完时,此时数字串从大到小排列。步骤1得到的j = 0,算法结束。

算法实现:

#include <iostream>
#include <string.h>
using namespace std;
void swap(char*s,int i,int j){//交换两个元素
	char temp;
	temp=s[i];
	s[i]=s[j];
	s[j]=temp;
}
void reverse(char *s, int first, int last){//翻转序列
	while (first<last){
		swap(s,first++,last--);
	}
}
int findmin(char*a){  //从排列的右端开始,找出第一个比右边数字小的数字的序号
    int j,length=strlen(a);
	for(j=length-2;j>=0;j--)
	{
		if(a[j]<a[j+1])break;
	}
	return j;
}
int main()
{
	char a[8];
	cin>>a;
	int i=0,j,k,length=strlen(a);
	for(i=0;i<length-1;i++)
	{
		for(j=i;j<length-1;j++)
		{
			if(a[i]>a[j])swap(a,i,j);
		}
	}
	cout<<a<<endl;
	while(true)
	{
        j=findmin(a);
        if(findmin(a)==-1) break;
        for(i=length-1;i>j;i--)
        {
        	if(a[i]>a[j])break;
		}
		swap(a,j,i);
		reverse(a,j+1,length-1);
		cout<<a<<endl;
	}
	return 0;
 }

猜你喜欢

转载自blog.csdn.net/qq_41486817/article/details/80617270
今日推荐