70.给出一个函数来输出一个字符串的所有排列。
分析:
采用递归实现,从字符串中依次选出一个元素做为全排列的第一个元素,然后求剩下的字符的全排列,采用和当前字符串第一个字符进行交换的方式来将字符串中的某个字符放到第一位。
需要注意的是,对于重复的字符要判断决定要不要进行交换操作,避免重复的全排列结果。
位置current的字符和位置i的字符进行交换时,有两种可能导致重复的情况:
1.当位置current和位置i字符相同时,不进行交换。例如current指向字符串“abcbd”第一个b,i指向第二b,交换后排序相同
2.位置current和位置i字符不相同,但是current和i之间有和i位置相同的字符,不进行交换。例如current指向字符串“abcdc”的a,i指向最后一个c,因为之前a已经和第一个c交换过,并且在子递归过程中,交换之后的a又和最后一个c进行了交换,已经产生“cbcda”这样的排列,所以这里也不进行交换。
C++代码:
void swap(char& c1, char& c2)
{
int temp = c1;
c1 = c2;
c2 = temp;
}
bool needSwap(char *str, int current, int i) //重复判断
{
for (int j = current; j < i; j++)
if (str[j] == str[i])
return false;
return true;
}
void helper(char* str, int current, int length)
{
if (current == length) //结束条件
{
cout << str << endl;
}
for (int i = current; i <length; i++)
{
if (needSwap(str, current, i))
{
swap(str[current], str[i]);
helper(str, current + 1, length);
swap(str[current], str[i]);
}
}
}
//递归算法
void allRange(char* str)
{
if (str == NULL) return;
int len = strlen(str);
helper(str, 0, len);
}
上面给出的是递归的做法,这里再拓展一下,讲一讲 STL算法中next_permutation的思想:字典排序算法。从字典顺序最小的一种排列开始,我们每次获得字典序刚好比前一个排列大的排列,直到得到字典序最大的排列时,就得到了所有的结果。
以字符串"abc"为例,"abc"是字典序最小的排列,所有情况按字典序排列为"abc","acb","bac","bca","cba","cab"
具体算法为:
1.字符串进行排序,得到字符串的最小字典序排列(C0C1C2...Cn),Ci<=Ci+1。
2.从后往前,找到一对相邻的升序元素CiCi+1,(Ci<Ci+1),如果遍历完字符串找不到这样的相邻升序对,说明已经达到了字典序最大的全排列
3.从字符串结束位置到位置i遍历,找到比Ci大的元素Cj,交换Cj的位置
4.将Ci+1到Cn所有的字符逆序,这样得到的排列刚好比之前的字典序大(因为转换后Ci+1<Ci+2<...<Cn,为最小字典序)。
5.重复3,4,5过程直到字典序最大
C++代码:
void quickSort(char* str, int len)
{
int i = 0, j = len - 1;
char c = str[i];
while (i < j)
{
for (; j>i; j--)
if (str[j] < c)
{
str[i] = str[j];
break;
}
for (; j>i; j++)
if (str[i] > c)
{
str[j] = str[i];
break;
}
}
str[i] = c;
}
//非递归算法
void allRangeDict(char* str)
{
int len = strlen(str);
quickSort(str, len);
int i, j;
cout << str << endl;
while (true)
{
for (i = len - 2; i >= 0; i--)
if (str[i] < str[i + 1])
break;
if (i == -1) return; //已达到最大字典序
for (j = len - 1; j>i; j--)
if (str[j] > str[i]) break;
swap(str[j], str[i]);
for (int k = 0; k<(len - i - 2); k++)
swap(str[i + k + 1], str[len - k - 1]);
cout << str << endl;
}
}
全部代码和测试结果:
#include<iostream>
using namespace std;
namespace MS100P_70
{
void swap(char& c1, char& c2)
{
int temp = c1;
c1 = c2;
c2 = temp;
}
bool needSwap(char *str, int current, int i) //重复判断
{
for (int j = current; j < i; j++)
if (str[j] == str[i])
return false;
return true;
}
void helper(char* str, int current, int length)
{
if (current == length) //结束条件
{
cout << str << endl;
}
for (int i = current; i <length; i++)
{
if (needSwap(str, current, i))
{
swap(str[current], str[i]);
helper(str, current + 1, length);
swap(str[current], str[i]);
}
}
}
//递归算法
void allRange(char* str)
{
if (str == NULL) return;
int len = strlen(str);
helper(str, 0, len);
}
void quickSort(char* str, int len)
{
int i = 0, j = len - 1;
char c = str[i];
while (i < j)
{
for (; j>i; j--)
if (str[j] < c)
{
str[i] = str[j];
break;
}
for (; j>i; j++)
if (str[i] > c)
{
str[j] = str[i];
break;
}
}
str[i] = c;
}
//非递归算法
void allRangeDict(char* str)
{
int len = strlen(str);
quickSort(str, len);
int i, j;
cout << str << endl;
while (true)
{
for (i = len - 2; i >= 0; i--)
if (str[i] < str[i + 1])
break;
if (i == -1) return; //已达到最大字典序
for (j = len - 1; j>i; j--)
if (str[j] > str[i]) break;
swap(str[j], str[i]);
for (int k = 0; k<(len - i - 2); k++)
swap(str[i + k + 1], str[len - k - 1]);
cout << str << endl;
}
}
void test()
{
char str[] = "abcc";
cout << "递归:" << endl;
allRange(str);
cout << "非递归:" << endl;
allRangeDict(str);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
MS100P_70::test();
return 0;
}
运行结果: