全排列就是从第一个数字起 每个数分别与它后面的数字交换
用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");
}