什么是函数递归?
程序调用自身的编程技巧称为递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于:把大事化小
递归的两个必要条件:
1.存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2.每次递归调用之后越来越接近这个限制条件。
下面来看一下递归的具体栗子
例1:
接受一个整型值(无符号),按照顺序打印它的每一位。
例如:
输入:1234,输出 1 2 3 4.
思路:
首先我们可以知道最容易得到的数字为4,1234%10=4,同理123%10=3,而1234/10=123,以此类推,所以我们可以给出一个简单的算法:
1234%10=4
1234/10=123 —— 123%10=3
123/10=12 ——12%10=2
12/10=1 ——1%10=1
这样就能一次求出数字了。
给出代码:
#include <stdio.h>
void print(int n)
{
if(n>9)
{
print(n/10);
}
printf("%d ", n%10);
}
int main()
{
int num = 1234;
print(num);
return 0;
按如上代码图解说明递归是如何实现的
红色的箭头表示在函数内部调用函数的过程,蓝色的箭头表示函数调用完后返回的过程。其中,函数体内的判断语句"if(n>9)"即为整个递归过程中的限制条件,在最后一次调用中,我们可以看出,"n/10"的结果明显不在满足再次调用的条件,所以调用结束,函数开始返回,最终得到想要的结果。
例2:
strlen的模拟(可以用循环实现,也可以用递归来实现,这里重点说递归实现)
strlen的含义是:求字符串中有效字符的长度,不包括\0。即计算字符’\0’前的字符个数
循环实现:
- 给一个计数,用来统计有效字符的个数
- 遍历字符串,只要没有遇到\0, 遇到一个字符给计数加1,直到遇到\0
#include<stdio.h>
int my_strlen(char* str)
{
int count = 0;
while('\0' != *str)
{
count++;
str++;
}
return count;
}
int main()
{
char arr[]="Hello world";
printf("%d",my_strlen(arr));
return 0;
}
递归实现:
int my_strlen(char *str)
{
if('\0' == *str)
return 0;
else
return 1+my_strlen(1+str);
}
主函数(略)
每次调用一次 my_strlen 函数,返回值就+1,最后的限制条件为当访问到’\0’时就退出。
例3:
编写一个函数 reverse_string(char * string)(递归实现)
实现:将参数字符串中的字符反向排列,不是逆序打印。
要求:不能使用C函数库中的字符串操作函数。
比如:
char arr[] = “abcdef”; 逆序之后数组的内容变成:fedcba
同上题,有两种实现方式(循环和递归)这里重点讲递归实现
循环实现:
思路:
- 给两个指针,left放在字符串左侧,right放在最后一个有效字符位置
- 交换两个指针位置上的字符
- left指针往后走,right指针往前走,只要两个指针没有相遇,继续2,两个指针相遇后,逆置结束
void reverse_string(char* arr)
{
char* left = arr;
char* right = arr+strlen(arr)-1;
while(left<right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
主函数(略)
递归实现:
对于字符串“abcdefg”,递归实现的大概原理:
- 交换a和g
- 以递归的方式逆置源字符串的剩余部分,剩余部分可以看成一个有效的字符串,再以类似的方式逆置
void reverse_string(char* arr)
{
int len = strlen(arr);//计算字符串的长度
char tmp = *arr;//1.将首字符放入一个临时变量中
*arr = *(arr+len-1);//2.将最后一个非\0的字符放入第一个字符的位置中
*(arr+len-1) = '\0';//3.将字符'\0'放入最后一个非\0的字符位置中
if(strlen(arr+1)>=2)
reverse_string(arr+1);//4.若剩下的字符个数在2个以上,调用该函数
*(arr+len-1) = tmp;//5.将此次的首字符放入上述第3步中放入'\0'字符
// 的位置中
}
主函数(略)
为了理解,画图说明:
经过上述5步,就能用递归的方法实现字符串的逆序了。
对递归方法的总结:
1.不能死递归,都有跳出条件,每次递归需逼近跳出条件
2.递归层次不能太深,避免栈溢出
所谓栈溢出,作以下说明:
相对其他方法而言,递归的方法虽然有些时候比较巧妙,但也存在缺陷,递归所需要的空间相对比较大,运行效率相对低,存在栈溢出的问题;所以在用递归之前需注意这些问题。