C语言简单函数递归介绍和题目应用

简单函数递归递归的两个必要条件存在限制条件,当满足这个限制条件的时候,递归便不再继续。每次递归调用后越来越接近这个条件 编写一个函数,要求不创建变量,计算字符串的长度在上一篇 实现strcpy和strlen函数 对此题已有详细介绍。 求n的阶乘对于n的阶乘, 当N<=1,N的阶乘是1,当N>2时,!n=n*fac( n-1)(其中f...
摘要由CSDN通过智能技术生成

简单函数递归


递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用后越来越接近这个条件

编写一个函数,要求不创建变量,计算字符串的长度

在上一篇 实现strcpy和strlen函数 对此题已有详细介绍。

求n的阶乘

对于n的阶乘, 当N<=1,N的阶乘是1,当N>2时,!n=n*fac( n-1)(其中fac为阶乘函数)

int fac(n)
{
  if(n<=1)
  return 1;
  if(n>1)
  return n*fac(n-1);
}
  • 这个函数中的限制条件,n<=1,
  • n-1使得每次递归后越来与接近这个条件,让函数进入最内部,也就时当nn<=1
    求第n个斐波那契数
    当n<=2时,值为1,n>2时fib=(n-1)+(n-2)
int fib(n)
{
  if(n<=2)
  {
  return 0;
  }
  else
  return fib(n-1)+fib(n-2)
  • 这个函数中的限制条件,n<=2,
  • n-1n-2每次递归后越来与接近这个条件,让函数进入最内部,然后逐层返回。

但是该递归函数出现大量的重复,传入的n越大,计算的重率程度呈指数级增长。所以使用循环的方法可以极大的提高效率

int fib(int n)
{
    int a = 1, b = 1;
    int c = 1;//当n小于2时直接返回1

    while (n > 2)
    {
        c = a + b;
        a = b;
        b = c;
        n--;
    }
    return c;
}

int main()
{
    int a = 0;
    printf("%u", fib(a));
    getchar();
    system("pause");
    return 0;

在这里通过定义三个变量,将c的值存储要求的斐波那契数,然后利用a和b更新来分别进行每次求下一个斐波那契数的求值。以%u的形式输出为了是数字正确,避免最高位是1是成为负数。
1

编写一个函数实现n^k,使用递归实现

int func(int n, int k)
{
    if (k <= 1)//限定递归终止条件
    {
        return n;  //返回n开始逐层返回
    }
    return n*func(n, k - 1);
}
int main()
{
    int n = 5;
    int k = 4
;
    printf("%u", func(n,k));
    system("pause");
    return 0;
    //编写一个函数实现n^k,使用递归实现

}

写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和,例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19

int DigitSum(unsigned int n)
{
    if (n <= 9)
    {
        return n; //在n之只有一位的时候直接返回
    }
    return n % 10 + DigitSum(n / 10);//表示将每次的取模相加
}
int main()
{
    int num = 0;
    scanf("%d", &num);
    printf("%d", DigitSum(num));
    system("pause");
    return 0;
}

这里写图片描述
同理,对函数进行稍微更改,实现打印函数的每一位。

void Digitprint(unsigned int n)
{
    if (n <= 9)
    {
        printf("%d ", n);
    }
    else
    {
        DigitSum(n / 10);
        printf("%d ", n % 10);
    }
}
int main()
{
    int num = 1234;
    Digitprint(num);
    system("pause");
    return 0;
}   
  1. 编写一个函数reverse_string(char * string)(递归实现)
    实现:将参数字符串中的字符反向排列。
    要求:不能使用C函数库中的字符串操作函数,若要实现strlen函数,只能用一行代码实现。不能在函数中创建静态变量
  • 思路

看到逆转字符串的题目,想到的是
求出字符串的长度,通过指针加法找到首部对应的末尾字符进行交换,然后对称虽小范围,直到两个指针相等或者越界。
但是题目要求不能使用库函数,且函数规定的参数只有一个。所以我们可以用递归实现my_strlen函数,题目要求只能用一行代码,又要用到assert断言函数,如何实现?

  • 使用逗号操作符将assert函数添加
  • 使用三目操作符省略if判断语句
  • 使用递归将长度累加
int my_strlen(const char* str)
{
    return (((assert(str)),(*str ? (1 + my_strlen(str + 1) ):0)));
}

实现strlen函数后,对于reverse函数而言,要想将逆序实现,似乎必须要创建一个变量来对每次的首元素相加来得到末尾元素。但是函数中的元素创建是局部变量,每次调用都需要重新初始化,如何在不用static修饰的情况下进行每次的末尾元素寻找?

以第一次交换为例:
这里写图片描述
此时值在交换时,我们定义char类型的变量tmp来进行对首元素的保存,然对末尾元素赋值\0☞调用reverse函数后,再将存储的tmp赋值给末尾元素。

void reverse_string(char * string)
{
    assert(string);
    if (*string!='\0')
    {
        char *end= string + (my_strlen(string) - 1);//将末尾非零元素存放至指针end
        char tmp = *string;//开始交换
        *string =*end ; //将末尾的非\0元素赋值给tmp
        *end = '\0';//此时将该处置'\0',便于第一阶段递归计算末尾元素
        reverse_string(string + 1);
        *end = tmp;//此时开始返回,将保存对应交换元素的tmp赋给它。
    }
}
int main()
{
    char arr[] = "abcdefghi";
    printf("%d\n",my_strlen(arr));
    reverse_string(arr);
    printf("%s", arr);
    system("pause");
    return 0;
}
  • 输出结果
    99

对于刚接触到递归的盆友,都想要对递归过程进行全程跟踪,对于简单的递归函数等接触时,最好在纸上画出函数调用和返回的流程图,便于理解和记忆,从递归终止条件的情况和递归的限制条件入手。这里一篇针对于汉诺塔的趣文分享。

对递归的理解的要点主要在于放弃!放弃你对于理解和跟踪递归全程的企图,只理解递归两层之间的交接,以及递归终结的条件。想象你来到某个热带丛林,意外发现了十层之高的汉诺塔。正当你苦苦思索如何搬动它时,林中出来一个土著,毛遂自荐要帮你搬塔。他名叫二傻,戴着一个草帽,草帽上有一个2字,号称会把一到二号盘搬到任意柱。你灵机一动,问道:“你该不会有个兄弟叫三傻吧?”“对对,老爷你咋知道的?他会搬一到三号盘。“”那你去把他叫来,我不需要你了。“于是三傻来了,他也带着个草帽,上面有个3字。你说:”三傻,你帮我把头三个盘子移到c柱吧。“三傻沉吟了一会,走进树林,你听见他大叫:”二傻,出来帮我把头两个盘子搬到C!“由于天气炎热你开始打瞌睡。朦胧中你没看见二傻是怎么工作的,二傻干完以后,走入林中大叫一声:“老三,我干完了!”三傻出来,把三号盘从A搬到B,然后又去叫二傻:“老二,帮我把头两个盘子搬回A!”余下的我就不多说了,总之三傻其实只搬三号盘,其他叫二傻出来干。最后一步是三傻把三号盘搬到C,然后呼叫二傻来把头两个盘子搬回C事情完了之后你把三傻叫来,对他说:“其实你不知道怎么具体一步一步把三个盘子搬到C,是吧?”三傻不解地说:“我不是把任务干完了?”你说:“可你其实叫你兄弟二傻干了大部分工作呀?”三傻说:“我外包给他和你屁相干?”你问到:“二傻是不是也外包给了谁?“三傻笑了:“这跟我有屁相干?”你苦苦思索了一夜,第二天,你走入林中大叫:“十傻,你在哪?”一个头上带着10号草帽的人,十傻,应声而出:“老爷,你有什么事?”“我要你帮把1到10号盘子搬到C柱““好的,老爷。“十傻转身就向林内走。“慢着,你该不是回去叫你兄弟九傻吧““老爷你怎么知道的?““所以你使唤他把头九个盘子搬过来搬过去,你只要搬几次十号盘就好了,对吗?““对呀!““你知不知道他是怎么干的?““这和我有屁相干?“你叹了一口气,决定放弃。十傻开始干活。树林里充满了此起彼伏的叫声:“九傻,来一下!“ “老八,到你了!““五傻!。。。“”三傻!。。。“”大傻!“你注意到大傻从不叫人,但是大傻的工作也最简单,他只是把一号盘搬来搬去。若干年后,工作结束了。十傻来到你面前。你问十傻:“是谁教给你们这么干活的?“十傻说:“我爸爸。他给我留了这张纸条。”他从口袋里掏出一张小纸条,上面写着:“照你帽子的号码搬盘子到目标柱。如果有盘子压住你,叫你上面一位哥哥把他搬走。如果有盘子占住你要去的柱子,叫你哥哥把它搬到不碍事的地方。等你的盘子搬到了目标,叫你哥哥把该压在你上面的盘子搬回到你上头。“你不解地问:“那大傻没有哥哥怎么办?“十傻笑了:“他只管一号盘,所以永远不会碰到那两个‘如果’,也没有盘子该压在一号上啊。”但这时他忽然变了颜色,好像泄漏了巨大的机密。他惊慌地看了你一眼,飞快地逃入树林。第二天,你到树林里去搜寻这十兄弟。他们已经不知去向。你找到了一个小屋,只容一个人居住,但是屋里有十顶草帽,写着一到十号的号码。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值