全排列算法之Perm递归算法实现

全排列算法之Perm递归算法实现

题目描述:

给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。
我们假设对于小写字母有’a’ < ‘b’ < … < ‘y’ < ‘z’,而且给定的字符串中的字母已经按照从小到大的顺序排列。

输入:

输入只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。

输出:

输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定义:
已知S = s1s2…sk , T = t1t2…tk,则S < T 等价于,存在p (1 <= p <= k),使得
s1 = t1, s2 = t2, …, sp – 1 = tp – 1, sp < tp成立。

样例输入:
abc
样例输出:
abc
acb
bac
bca
cab
cba
提示:

每组样例输出结束后要再输出一个回车。

想必大家对perm递归算法求全排列并不陌生,但我贴出来的题目却不能用perm算法来解决,为什么呢?请容我慢慢道来,首先题目对全排列有着非常严格的顺序要求,即按字典顺序排列,就是这个perm算法是满足不了的(或许经过小小的改变是可以实现的,我们在这里就不讨论了)。那么下来我来谈谈perm算法的核心思:举个例子,比如要你1的全排列,你肯定会说那还不简单啊,那么接下来加深难度求1,2的全排列,其实也不难,现在让你求1,2,3,4,5的全排列呢,还转得过来吗?现在我们可以这样想,3,4,5的全排列是以3开头的4,5的全排列组合和4开头的3,5的全排列组合以及以5开头的3,4全排列组合。这就是perm算法的核心思想,列出一个通俗一点的式子:

从而可以推断,设一组数p = {r1, r2, r3, … ,rn}, 全排列为perm(p),pn = p – {rn}。

因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), … , rnperm(pn)。

当n = 1时perm(p} = r1。

下面贴出代码:

#include<cstdio>
#include<cstring>
#define MAX 10

using namespace std;

void swap(char str[],int i,int j)
{
	int temp;
	temp=str[i];
	str[i]=str[j];
	str[j]=temp;
}

void perm(char str[],int k,int m)
{
	int i;
	if(k>m)
	{
		for(i=0;i<=m;i++)
			printf("%c",str[i]);
		printf("\n");
	}
	else
	{
		for(i=k;i<=m;i++)
		{
			swap(str,k,i);
			perm(str,k+1,m);
			swap(str,k,i);
		}
	}
}

int main(int argc,char *argv[])
{
	char str[MAX];
	while(scanf("%s",str)!=EOF)
	{
		int len=strlen(str);
		perm(str,0,len-1);
		printf("\n");
	}

	return 0;
}

这里会出现两个问题,其一是超时,其二是答案顺序不对。。。

因为每次都进行的是将数组中的数与第一个数进行交换,它注重的是所有的全排列,但没有注意到换位顺序的问题,这样会产生一个问题:比如 1 2 3 4 的全排列,处理2,3,4的全排列时会将4与2交换,这样会出现1432排在1423的前面。所以如果对全排列的顺序有非常严格的顺序,就不能用perm算法。

例如,abc的全排列:

有序全排: perm全排:

abc abc acb acb bac bac bca bca cab cba cba cab

接下来,我们来看一看perm算法的另一大问题,如果对有重复元素的序列进行全排呢?例如:输入122则会输出什么呢?很遗憾,输出结果为:122,122,212,221,221,212(如果你能直接说出来,那么你对perm算法的运行流程就弄明白了),这样的结果明显是不对的,该如何解决呢?我们来看一下,第1个数与第2个数交换得到212(此时第一个数在第二个位置),接着第3个数与第2个数交换得到221(此时第一个数1在第三个位置上,第三个数在第二个位置上,第二个数在第一个位置上),然而第二个数与第三个数是相等的(原序列上),这不相当于直接第三个数与第一个数交换了吗?一下子把下面的事给做了。所以第i个数与第j个数交换时,要求[i,j)中没有与第j个数相等的数就行了。。。

代码:

#include<cstdio>
#include<cstring>
#define MAX 10

using namespace std;

void swap(char str[],int i,int j)
{
	int temp;
	temp=str[i];
	str[i]=str[j];
	str[j]=temp;
}

bool IsUnique(char str[],int start,int end)//检查重复项
{
	int i;
	for(i=start;i<end;i++)
		if(str[i]==str[end])
			return false;
	return true;
}

void perm(char str[],int k,int m)
{
	int i;
	if(k>m)
	{
		for(i=0;i<=m;i++)
			printf("%c",str[i]);
		printf("\n");
	}
	else
	{
		for(i=k;i<=m;i++)
		{
			if(IsUnique(str,k,i))
			{
				swap(str,k,i);
			    perm(str,k+1,m);
		    	swap(str,k,i);
			}
		}
	}
}

int main(int argc,char *argv[])
{
	char str[MAX];
	while(scanf("%s",str)!=EOF)
	{
		int len=strlen(str);
		perm(str,0,len-1);
		printf("\n");
	}

	return 0;
}

    原文作者:递归算法
    原文地址: https://blog.csdn.net/cstopcoder/article/details/21185697
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞