全排列问题

全排列就是从第一个数字起 每个数分别与它后面的数字交换


用C++写一个函数, 如 Foo(const char *str), 打印出 str 的全排列, 
如 abc 的全排列: abc, acb, bca, dac, cab, cba。。


第一种方法:

用递归(不包含有重复数字或字符出现的情况)

void swap(char *a, char *b)
{
	char temp;
	temp = *a; *a = *b; *b = temp;
}
//k表示当前选取到第几个数,m表示共有多少数.
void AllRange(char *plStr, int k, int m)
{
	if (k == m)
	{
		static int s_i = 1;
		printf("第%3d个排列\t%s\n", s_i++, plStr);
	}
	else
	{
		//第i个数分别与它后面的数字交换就能得到新的排列
		for (int i = k; i <=m ; i++)
		{
			swap(plStr + k, plStr + i);
			AllRange(plStr, k + 1, m);
			swap(plStr + k, plStr + i);//一层层地回复到当前大循环的初始值
		}
	}
}
void Foo(char *plStr)
{
	AllRange(plStr, 0, strlen(plStr)-1);
}
void main()
{
	char szTextStr[] = "12345";
	 Foo(szTextStr);
	system("pause");
}
包含重复出现的数字或字符的情况
void swap(char *a, char *b)
{
	char temp;
	temp = *a; *a = *b; *b = temp;
}
//在plStr数组中[nBegin,nEnd]中是否有数字与下标为nEnd的数字相等
bool IsSwap(char *plStr, int nBegin, int nEnd)
{
	for (int i = nBegin; i < nEnd; i++)
	{
		if (plStr[i] == plStr[nEnd])
			return false;
	}
	return true;
}
//k表示当前选取到第几个数,m表示共有多少数.
void AllRange(char *plStr, int k, int m)
{
	if (k == m)
	{
		static int s_i = 1;
		printf("第%3d个排列\t%s\n", s_i++, plStr);
	}
	else
	{
		//第i个数分别与它后面的数字交换就能得到新的排列
		for (int i = k; i <= m; i++)
		{
			if (IsSwap(plStr, k, i))//判断第i k之间是否有与k相等的数字
			{
				swap(plStr + k, plStr + i);
				AllRange(plStr, k + 1, m);
				swap(plStr + k, plStr + i);//一层层的打回以前的排列顺序(回溯)
			}		
		}
	}
}
void Foo(char *plStr)
{
	AllRange(plStr, 0, strlen(plStr) - 1);
}
int main()
{
	cout << "       全排列的递归实现" << endl;
	char szTextStr[] = "1222";
	printf("%s的全排列如下:\n", szTextStr);
	Foo(szTextStr);
	system("pause");
	return 0;
}

第二种方法:

非递归实现

要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。
如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。


如何计算字符串的下一个排列了?来考虑"926520"这个字符串,
我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "
即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面
找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交
换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。
对于像"4321"这种已经是最“大”的排列,采用STL中的处理方法(快速排序),
将字符串整个颠倒得到最“小”的排列"1234"并返回false。*/

void Swap(char *a, char *b)
{
	char t = *a; *a = *b; *b = t;
}
//反转区间
void Reverse(char *a, char *b)
{
	while (a < b)
		Swap(a++, b--);
}
//是否存在下一个排列
bool Next_permutation(char a[])//这是一个求一个排序的下一个排列的函数,可以遍历全排列,要包含头文件<algorithm>
{
	char *pEnd = a + strlen(a);
	if (a == pEnd)
		return false;//防止越界
	char *p, *q, *pFind;
	pEnd--;//指针指向最后一个元素
	p = pEnd;
	while (p != a) //找两个挨着的数  没有的话p-- 直到发现这两个数
			//没有的话跳出while语句
	{
		q = p;
		--p;
		if (*p < *q)//比较挨着的数的大小
		{
			pFind = pEnd;//给find指针赋初值,从最后一位找起
			while (*pFind <= *p)//判断我们找到的替换数字与后面数字的大小
				--pFind;//并找到比要替换的数字大的值
			Swap(pFind, p);//交换这两个值
			Reverse(q, pEnd);//q做个标记从p后面的值进行反转
			return true;
		}
	}
	Reverse(p, pEnd);//如果没找到这样的两个数,证明排序已经完成,没有下一个排列了
	return false;
}
//比较函数
//qsort 要求提供一个 比较函数,是为了做到通用性更好一点
//qsort函数构成的示范:
//Sample: int cmp( const void *a , const void *b )
//{ return *(char *)a - *(int *)b; }
//qsort(word, 100, sizeof(word[0]), cmp);

int QsortCmp(const void *pa, const void *pb)
{
	return *(char*)pa - *(char*)pb;
}

int main()
{
	cout << "       全排列的递归实现" << endl;
	char szTextStr[] = "1423";
	printf("%s的全排列如下:\n", szTextStr);
	//将输入的东西进行快速排序必须要有的步骤.
	qsort(szTextStr, strlen(szTextStr), sizeof(szTextStr[0]), QsortCmp);
	//输出各个排列
	int i = 1;
	do {
		printf("第%3d个排列\t%s\n", i++, szTextStr);
	} while (Next_permutation(szTextStr));
	system("pause");
	return 0;
}

最后是一个整合的例子

输入一个字符串S和一个整数N,其中S中的各个字符均不相等,
并且N小于等于S的长度,请输出由S中字符组成的长度为N的所有的排列。

比如 要输出12345中任意3个数的组合

void swap(char *a, char *b)
{
	char temp;
	temp = *a; *a = *b; *b = temp;
}
//k表示当前选取到第几个数,m表示共有多少数.
void AllRange(char *plStr, int k, int m)
{
	if (k == m)
	{
		static int s_i = 1;
		printf("第%3d个排列\t%s\n", s_i++, plStr);
	}
	else
	{
		//第i个数分别与它后面的数字交换就能得到新的排列
		for (int i = k; i <= m; i++)
		{
			swap(plStr + k, plStr + i);
			AllRange(plStr, k + 1, m);
			swap(plStr + k, plStr + i);//一层层地回复到当前大循环的初始值
		}
	}
}

//全排列函数    有序的组合
void Foo(char *plStr)//N代表几个数的全排列
{
	AllRange(plStr, 0, strlen(plStr) - 1);
}

//小于字符串总字符个数的输出  无序的排列
void innerCombin(const char *arr, int n, int idx, int count, char *buf)
{
	if (count == n) {
		//printf("%s\n", buf);
		Foo(buf);//输出所有三个字符组合的全排列
		return;
	}
	if (arr[idx] == '\0') return;
	buf[count] = arr[idx];
	innerCombin(arr, n, idx + 1, count + 1, buf);//管输出的语句.输出:1 2 3 4
	innerCombin(arr, n, idx + 1, count, buf);//管往下执行的1->2->3->4
}

void combination(const char *arr, int n)//组合
{
	if (arr == NULL) return;
	if (strlen(arr) < n) return;
	char *buf = new char[n + 1];
	buf[n] = '\0';
	innerCombin(arr, n, 0, 0, buf);
	delete[] buf;
}

void main()
{
	char szTextStr[] = "12345";
	combination(szTextStr, 3);
	system("pause");
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值