67、请编程实现全排列算法。
全排列算法有两个比较常见的实现:递归排列和字典序排列。

/*
67、请编程实现全排列算法。
全排列算法有两个比较常见的实现:递归排列和字典序排列。

(1)递归实现
从集合中依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,
如此递归处理,从而得到所有元素的全排列.

(2)字典序排列
思想是让排列成为可递推的数列,也就是说从前一状态的排列,可以推出一种新的状态,直到最终状态。
比如说,最初状态是12345,最终状态是54321。首先是12345,然后12354,然后12435,12453....逐渐地从后往前递增。

算法:
首先,将待排序列变成有序(升序)序列。然后,从后向前寻找,找到相邻的两个元素,Ti<Tj(j=i+1)。
如果没有找到,则说明整个序列已经是降序排列了,也就是说到达最终状态54321了。此时,全排列结束。

接着,如果没有结束,从后向前找到第一个元素Tk,使得Tk>Ti(很多时候k=j),找到它,交换Ti跟Tk,
并且将Tj到Tn(Tn是最后一个元素)的子序列进行倒置操作。
输出此序列。并回到第二步继续寻找ij.

例如839647521是数字1~9的一个排列。从它生成下一个排列的步骤如下:
自右至左找出排列中第一个比右边数字小的数字4 839647521
在该数字后的数字中找出比4大的数中最小的一个5 839647521
将5与4交换 839657421
将7421倒转 839651247
所以839647521的下一个排列是839651247。
839651247的下一个排列是839651274。

递归实现版本在优化情况下要慢很多,主要原因可能在于太多的函数调用开销,
但在不优化情况下执行比其它二个版本要快,原因可能在于程序结构更简单,执行的语句较少。
比较而言,递归算法结构简单,适用于全部计算出所有的排列(因此排列规模不能太大,计算机资源会成为限制);
而字典序排列逐个产生、处理排列,能够适用于大的排列空间,并且它产生的排列的规律性很强。

*/
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
//template <typename T>
int k,n=4;

//递归
void rankRuc(char perm[],int n,int index)
{
if(index==n)
{
printf("%2d:",k++);
for(int i=0;i<n;i++)
printf("%c",perm[i]);
printf("\n");
return ;
}
for (int i=index;i<n;++i)
{
swap(perm[i],perm[index]);
rankRuc(perm,n,index+1);
swap(perm[i], perm[index]);
}
}
//字典序
void rankDic(char perm[],int num)
{
int i,k;

if (num<1)
return;

while(true)
{
for (i=num-2;i>=0;--i)
if (perm[i]<perm[i+1])
break;
if (i<0)
break; // 已经找到所有排列

for(k=num-1;k>i;--k)
if(perm[k]>perm[i])
break;
swap(perm[i],perm[k]);
reverse(perm +i+1,perm+num);

for(int i=0;i<n;i++)
printf("%c",perm[i]);
printf("\n");
}
}

int main()
{
char perm[]={"abcd"};
k=1;
rankRuc(perm,4,0); //24个

printf("字典序排列:\n");
rankDic(perm,4); //24个
}