字符串的排列,递归输出

(先稳定一下军心,下面所有代码都测试过,可以直接用,所以就没有截图,想看效果的可以自己运行一下)

1.全排列

全排列:字符以任意顺序排列,比如123的全排列是123,132,213,231,312,321。长度为n的字符串的全排列个数为n!个,下面是代码:

/*思路:s全排列是第一位与s[1~n-1]的全排列,所以每次交换第一位与
** s中的某一位,然后递归求出s[1~n-1]的全排列。
*/
#include<iostream>
#include <string>
using namespace std;
static int count=0;
void Permutation(string s,int beg,int sLen)
{
	if(0==sLen)  //这轮全排列结束
	{
		++count;
		cout<<s<<endl;
		return;
	}
	for (int i=0;i<sLen;++i)
	{
		swap(s[beg],s[beg+i]);      //将s中的每一位与第一位交换
		Permutation(s,beg+1,sLen-1); //排列s[1~n-1]
		swap(s[beg+i],s[beg]);     //每一轮完后将交换后的字符还原
	}
}
int main()
{
	string s;
	cout<<"请输入字符串:";
	cin>>s;
	int sLen=s.size();
	Permutation(s,0,sLen);
	cout<<endl<<"共"<<count<<"个"<<endl<<endl;
	return 0;
}


代码中的Permutation换成下面的代码,效果也一样:

void Permutation(string s,int beg,int end)
{
	if(beg==end-1)  //这轮全排列结束
	{
		++count;
		cout<<s<<endl;
		return;
	}
	for (int i=beg;i<end;++i)
	{
		swap(s[beg],s[i]);
		Permutation(s,beg+1,end);
		swap(s[i],s[beg]);
	}
}


在上面的代码中,如果字符串中有重复的字符,就不能避免重复输出了。例如对于1213,n!=4!=24,如果考虑到避免重复输出,应该有4!/2!=12个。下面的代码是避免全排列中的重复输出:

/*	思路:1.轮流选取s中的每位作为第一位。如果第i位字符在[0,i-1]这个区间内出现过,
**	就不再对其进行排列,因为把它放在第一位的所有排列在前面排列过了(就是代码中j作为循环变量的作用)。
**	2.把当前选取的这一位放入目标数组中。
**	3.用一个临时数组保存剩余的字符,并把其作为源。同时目标数组的地址下移一位,因为当前位已经排列好。
*/
#include<iostream>
#include <string>
using namespace std;
static int count=0;  //记录全排列个数
char  *result=new char[16]();     //存放某个排列
void Permutation(string strSource,char *strResult,int sLen)
{
	if(sLen==1)  //这轮全排列结束
	{
		strResult[0]=strSource[0];   //只有一个字符
		++count;
		 //strResult在递归的过程中是一次向后移一位的,所以字符串的首地址是result
		cout<<result<<endl;    
		return;
	}
	for (int i=0;i<sLen;++i)
	{
		int j;
		for ( j=0;j<i&&strSource[j]!=strSource[i];j++);    //判断当前字符是否在之前已经出现过
		if (j==i)      //没有出现过
		{
			strResult[0]=strSource[i];    //把当前字符放在第一位
			//将除了当前位剩下的字符存入nextRecursion中,进入下一次递归
			string nextRecursion=strSource.substr(0,i)+strSource.substr(i+1,sLen-i-1); 
			Permutation(nextRecursion,strResult+1,sLen-1); //strResult向后移一位,构造下个字符
		}
	}
}
int main()
{
	string s;
	cout<<"请输入字符串:";
	cin>>s;
	int sLen=s.size();
	Permutation(s,result,sLen);
	cout<<endl<<"共"<<count<<"个"<<endl<<endl;
	return 0;
}


2.排列

在第一节中讨论了字符串全排列的问题,在这一节讨论排列的问题。

排列:从长度为n的字符串中任意挑选m个元素以任意次序排列。全排列是n=m的特殊情况。

编程思路和上面的代码很像。最大的不同之处就是把结束时的情况放到了for循环里面,并且把return去掉了,这是为了保证所有的情况都能遍历到。还增加了一个参数k来记录已经取得的字符个数。

#include<iostream>
#include <string>
using namespace std;
static int count=0;  //记录全排列个数
char  *result=new char[16]();     //存放某个排列
//sLen是源字符串的长度,m是要取的字符个数,所以排列个数是A(sLen,m)
//k为已经取得的字符个数
void Permutation(string strSource,char *strResult,int sLen,int m,int k)
{
	for (int i=0;i<sLen;++i)
	{
		int j;
		for ( j=0;j<k&&result[j]!=strSource[i];j++);    //判断当前字符是否在之前已经出现过
		if (j==k)      //没有出现过
		{
			if(k==m-1) //这轮全排列结束
			{
				strResult[0]=strSource[i];   
				++count;
				//strResult在递归的过程中是一次向后移一位的,所以字符串的首地址是result
				cout<<result<<endl;    
			//	return;
			}
			else
			{
				strResult[0]=strSource[i];    //把当前字符放在第一位
				//将除了当前位剩下的字符存入nextRecursion中,进入下一次递归
				string nextRecursion=strSource.substr(0,i)+strSource.substr(i+1,sLen-i-1); 
				Permutation(nextRecursion,strResult+1,sLen-1,m,k+1);  //strResult向后移一位
			}
		}
	}
}
int main()
{
	string s;
	int m;
	cout<<"请输入字符串:";
	cin>>s;
	cout<<"请输入m:";
	cin>>m;
	int sLen=s.size();
	if(m<=sLen)
		Permutation(s,result,sLen,m,0);
	cout<<endl<<"共"<<count<<"个"<<endl<<endl;
	return 0;
}

ps:为了和之前的代码保持连贯性,并且突出修改的重点(1.终止条件放到for循环中;2.去掉return),代码中的一些优化我都没有改。比如说,可以把if和esle中的strResult[0]=strSource[i]; 提出来;28行和29行的截取字符串是多余的,因为13行的代码已经判断重复问题了,所以28行和29行直接可以换成Permutation(strSource,strResult+1,sLen,m,k+1);

这个代码和上面的代码很像,这里可以好好体会一下这两个代码的不同之处。为了和数学中的排列定义A(n,m)相符,我把代码中的n换成了m,sLen相当于这里的n 。还需要说明的一点就是这段代码只能运用在无重复字符的字符串中,若有重复字符,会有重复输出。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值