【问题】给出字符串或者一个数组的全排列,
例如"abc",那么所有的排列组合有"abc"、"acb"、"bac"、"bca"、"cba"、"cab",对数组也是如此。
【思路】递归
- 首先固定第一个字符,a,那么,以a开头的剩下的组合,就变成了"bc"的组合,有"bc"、"cb";
- 然后将原来字符串的第二个字符b与a交换,重复上一步的过程
- 然后将第三个字符c与原字符的a交换,重复第一步的过程
- 在第一步的过程中,其实是个递归过程,固定a,求剩下的全排列,然后在固定前两个字符,求剩下的字符的全排列,递归的边界就是未固定部分到了整个字符串的结束位置。
- OK,按照这个思路,递归函数有三个参数,首先是原字符串str,起始的固定位置k,终止位置m,固定0~k,全排列k+1~m-1;
【写法1】
void str_perm(char* str, int k, int m, int& count)
{
assert(str != NULL && k <= m);
if(k == m)
{
cout<<str<<endl;
count++;
}
else
{
for (int i = k; i < m; i++)
{
swap(str[i], str[k]);
str_perm(str, k+1, m, count);
swap(str[i], str[k]);
}
}
}
【写法2】
//@2 第二种写法,主要是根据字符串的结束'\0'来处理
void str_permutation(char* str, char* begin)
{
assert(str != NULL && begin != NULL);
if(*begin == '\0')
cout<<str<<endl;
else
{
for (char* s = begin;*s != '\0'; ++s)
{
swap(*s, *begin);
str_permutation(str,begin+1);
swap(*s, *begin);
}
}
}
【对数组的处理】其实大同小异
//void perm(int *a, int k, int m)是将a[]的第0~k个元素不变,第k+1~m-1个元素进行全排列得到
void perm(int *a, int k, int m)
{
if(k == m)
{
for (int i=0; i < m; i++)
cout<<a[i]<<" ";
cout<<endl;
}
else
{
for (int i = k; i < m; i++)
{
swap(a[k], a[i]);
perm(a, k+1, m);
swap(a[k], a[i]);
}
}
}
【测试代码】
#include <assert.h>
#include <iostream>
using namespace std;
int main()
{
int a[]={1,2,3};
perm(a, 0, 3);
char s[]="your";
int times = 0;
str_perm(s, 0, strlen(s), times);
str_permutation(s, s);
}
【体会】
递归是一个伟大的思想,每次看完用递归写的代码,总有这样的感叹!可以说,不会递归,就不是合格的程序员:),而递归的关键就是发现规律,让问题规模朝着小的方向,然后找到边界剩下的就是debug了!!!