题目:给定一个序列,求出其全排列,并保证有序。例如字符串123的全排列是123,132,213,231,312,321。
解答:
- 递归解法。从第一个数开始的每一个数字与其后面的数字交换。如果序列中存在重复数据,比如122,则在交换之前看两个交换数字之间有没有与下标比较大的交换数字相等的数字,如果有的话,则不交换。比如122中,1与第二个2交换,发现中间有一个2,所以不交换,因为它们即使交换,得到的排列是221,但是1与第一个2交换得到了212可以进一步通过交换获得221.
- 非递归解法,使用字典序算法。基本思路是:
- 对于输入的字典许排列<a1,a2,…,an>,反向查找第一对满足a[j]<a[j+1]的下标j
- 仍旧反向查找一个下标k,使得 a[j]<a[k]并且a[k]=min{a[t] | any t, a[j]<a[t]}
- 交换a[j]和a[k]
- 反转a[j+1..n]
代码:
#include <iostream>
#include <algorithm>
using namespace std;
//防止产生重复排列
bool canSwap(char *arr, int begin, int end)
{
char v = arr[end];
for(int i = begin; i < end; i++)
{
if(arr[i] == v)
return false;
}
return true;
}
//递归解法
void AllRange(char *arr, int k, int m)
{
if(k == m)
{
printf("%s\n", arr);
}
else
{
for(int i = k; i <= m; i++)
{
if(canSwap(arr, k, i))
{
swap(arr[k], arr[i]);
AllRange(arr, k + 1, m);
swap(arr[k], arr[i]);
}
}
}
}
//非递归解法,字典序算法
bool getNext(char *arr, int n)
{
if(arr == NULL || n <= 1)
return false;
int j = n - 2;
while(j >= 0 && arr[j] >= arr[j + 1])
{
j--;
}
if(j < 0)
return false;
int i = n - 1;
while(arr[j] >= arr[i] && i > j)
{
i--;
}
swap(arr[i], arr[j]);
while(j + 1 < n - 1)
{
swap(arr[j + 1], arr[n - 1]);
j++;
n--;
}
return true;
}
//测试
int main()
{
//char arr[4] = "111";
//char arr[4] = "123";
//char arr[4] = "122";
char arr[4] = "321";
cout << "递归结果:" << endl;
AllRange(arr, 0, 2);
cout << "非递归结果:" << endl;
sort(arr, arr + 3);
cout << arr << endl;
while(getNext(arr, 3))
{
cout << arr << endl;
}
return 0;
}