1.浅谈递归的理解
有时候,对于一个复杂问题的求解,我们可以考虑通过一些步骤,将复杂的问题转化成规模变小的相同问题进行求解。最后一层一层传递下去,只要我们解决了规模最小的、不可再分解的问题后,逐级回溯也就解决了规模较大的复杂问题。
学会递归,就是学会分解,在我看来就是要学会将问题的求解分步拆解,并考虑周全。
什么叫拆解成类似的子问题呢?比如有一个问题叫做跑100米,我们可以将其分步进行:先跑10米,再解决子问题:跑90米,这样我们就成功地将问题的规模从跑100米,缩小成了跑90米。而父子问题都是类似的,都是要解决跑n米的问题,只不过是规模缩小了罢了。至此,我们实现了复杂问题的拆解。但我们还需要考虑周全,试想我们每次分步进行都能将问题的规模减少个10米,但是当我们最后拆到了需要解决跑0米这个问题的时候,我们还能按照之前的方式分步进行吗,(即先走10米,再解决跑-10米的问题),显然不对的,所以我们要为这个不满足我们分步拆解的情况单独处理。也就是要求当跑到0米的时候,我们就停止,而不是继续拆分。这就是考虑周全。
2.一些实例(to be continued)
1.strlen的递归实现(自定义的strlen命名为my_strlen)
分步思路:计算一个首地址为string的字符串长度,可以先算第一个字符,再计算这个字符以后的字符串长度。也就是my_strlen(string)返回的值等于1+my_strlen(string+1)。然后考虑周全,如果是my_strlen处理的字符串就是\0呢,所以我们要求如果是这个情况,则这个函数返回0。
代码实现:
int my_strlen(char *string)
{
if (*string)
return 1 + my_strlen(string + 1);
else
return 0;
}
2.字符数组内容逆置的递归实现(逆置函数命名为reverse)
分步思路:这次相对复杂些。考虑一个字符数组string内容为:abcdefg\0
首先第一步,将bcdefg\0这个字符数组逆置,即reverse(string+1),然后将a移到\0前面(具体实现可以通过与a右边的字符交换位置,直到遇到\0停止)。然后考虑周全,当处理的字符串长度只有1的时候,就无法按照前面那样分步了,此时我们不做处理。
代码实现:
void reverse(char string[])
{
if (*(string + 1))
{
reverse(string + 1);
}
while (*(string + 1))
{
char temp = *string;
*string = *(string + 1);
*(string + 1) = temp;
string++;
}
}
3.打印菱形的递归实现(自定义函数命名为print)
(首先明确该问题完全没必要用递归,此处只不过为了演示分步的方法)
打印出来的菱形大概长这样:
分步思路:将print(n)看成是打印第一行和最后一行有n个星的"菱形"(中间为13个星),则打印如图的菱形分步走的话就是先打印第一行的(13-1)/2个空格和1个星,然后打印print(2),再打印最后一行的(13-1)/2个空格和1个星。对于print(n)就相当于先打印第一行(13-n)/2个空格和n个星,然后再打印print(n+1),然后再打印最后一行的(13-1)/2个空格和n个星。考虑特殊情况,当n已经等于13,即最中间的星星数的时候,显然无法用上面的情况将其分解了,此时就只打印中间的13个星。
代码实现:
void print(int n)
{
int i;
for (i = 0; i < (13 - n) / 2; i++)
printf(" ");
for (i = 0; i < n; i++)
printf("*");
printf("\n");
if (n != 13)
{
print(n + 2);
for (i = 0; i < (13 - n) / 2; i++)
printf(" ");
for (i = 0; i < n; i++)
printf("*");
printf("\n");
}
}