递归编程题目

题目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);
}

 

 

 

posted @ 2013-08-25 00:08  StrikeW  阅读(750)  评论(0)    收藏  举报