递归编程题目
题目1:
实现一个函数,能够输出一个字符串中的所有字符的所有可能的组合,例如将字符串"dog"作为该函数的输入,那么输出将是"dog","dgo","odg","ogd","gdo","god"。如果字符串中有重复的字符,将它们看作是不同的字符,例如输入"xxx",那么输出将是6个"xxx"。
解法1:
可以想想我们在写"dog"的全排列时是怎么写的?首先固定第一个字母'd',然后再找出后面字符序列的全排列,这是很自然的思路。而且很明显,这是一个递归的过程,可以用一个公式来表达:P("dog") = dP("og") + oP("dg") + gP("do")。我们可以按照这个递归的公式来构造我们的函数。递归函数有两个必要的组成部分:base case和recusive case。按照以上的公式,递归的base case就是子字符串长度为1的时候,此时就将结果输出,并返回。recusive case就是求除了首字符以外的后续字符的排列。
C++代码实现:
template <class T>
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
/**
* 输出字符串s中所有字符的全排列
* T(dog) = dT(og) + oT(dg) + gT(do)
* 不要一个个字符的输出,要整个字符串一起输出!
*
* @param length 原始字符串的长度
* @param s 子字符串
*/
void doPermute(char result[], char s[], int length) {
int len = strlen(s);
for (int i = 0; i < len; i++) {
// 字符串深拷贝,避免交换时改变原字符串的顺序
char temp[20];
strcpy_s(temp, s);
mySwap(temp[0], temp[i]);
result[length - len] = temp[0];
doPermute(result, temp + 1, length);
}
// base case
if (len == 0) {
printf("%s\n", result);
return;
}
}
void stringPermute(char input[]) {
// 初始化参数
int length = strlen(input);
char result[20] = {'\0'};
// 进行递归调用
doPermute(result, input, length);
}
递归函数doPermute需要原始字符串的长度length,length - len就是结果字符串(result)中当前需要填充的位置。需要注意的是,C++中字符串最后都有一个附加的空字符'\0',strlen()计算字符串长度时不计入该字符,所以我们的base case如果放在for循环上面的话,if判断条件必须是 len == 0,这样才能输出完整的字符,而将base case放到下面时,if判断条件可以为 len == 1。
解法2:
回忆一下我们中学时做过的排列组合的题目,有abcd四个字母,求这四个字母能够构成多少种不同的序列,我们是怎么做的呢?我们可以假设有四个空位,每个空位可以容纳下一个字母,有多少种放置方法就有多少种字母序列。第一个空位有4种选择(abcd),第二个空位只能有3种选择,因为有一个字母已经放到了第一个空位上。
我们可以按照这种思路来解答上面这个问题。假设起始位置是x,找出可以放在x上的所有可能的字符,并将该字符添加到结果字符串中。每次找到一个可以放到x上的字符时继续找位置x+1上所有可供选择的字符,将字符添加到结果字符串中。以上就是我们的recusive case,而base case是x>最后一个字符的下标(x == strlen(str)),此时输出结果字符串,并且返回。为了知道有哪些字符已经被使用了,我们需要一个bool数组来标记原始字符串中的每一个字符,如果被使用了则bool数组中对应该字符下标位置的值为true。
C++代码实现:
/**
* used表示当前位置(pos)下,字符的可使用情况,下标对应原始字符串中的每个字符
* pos用来跟踪递归层次,并且标记当前待选择的字符位置
* length是原始字符串长度
* src是原始的字符串
*/
void doPermute(int pos, char src[], bool used[], char result[], const int length) {
if (pos == length) {
printf("%s\n", result);
return;
}
for (int i = 0; i < length; i++) {
if (used[i])
continue;
result[pos] = src[i];
used[i] = true;
doPermute(pos + 1, src, used, result, length);
// 返回之后要将字符设为未使用状态
used[i] = false;
}
}
void stringPermute(char input[]) {
int length = strlen(input);
char result[20] = {'\0'};
bool used[20] = {false};
doPermute(0, input, used, result, length);
}

浙公网安备 33010602011771号