C语言--- 函数递归

目录

一.什么是递归

1.递归的思想

二.递归的限制条件

2.递归的限制条件

三.递归举例

1.举例1:求n的阶乘.

2.举例2:顺序打印一个整数的每一位.

 四.递归与迭代

3.举例3:求第n个斐波那契数.


一.什么是递归

在C语言中,递归就是自己调用自己.

以下是一个C语言的递归代码

#include<stdio.h>
int main()
{
    printf("hello world!\n");
    main();
    return 0;
}

 在打印完hello world之后,又重新执行main函数,又继续打印hello world无限的执行下去.

但是这串代码最终会陷入死循环,导致栈溢出.(stack overflow)

1.递归的思想

把一个大型复杂的问题,不断的转化为更小的同类型的问题,直到不能继续转化下去,递归就结束.

也就是大事化小的思想.

递归:递是指递推的意思,归是指回归的意思.

二.递归的限制条件

2.递归的限制条件

1.递归存在限制的条件,当满足这个条件的时候,递归不会再进行下去.

2.递归总是越来越接近这个条件.

三.递归举例

1.举例1:求n的阶乘.

一个正整数的阶乘(factorial)就是所有小于以及等于这个数的正整数的乘积,并且规定0的阶乘为1;

阶乘符号:  !

例:5! = 5*4*3*2*1       4! = 4*3*2*1

由以上的条件我们知道n! =  n(n-1)!

5! = 5 * 4!

 这样我们就可以把一个大问题简化为一个与原问题相识的,但规模较小的问题来求解.

 如果Fact(n)指的是n的阶乘,那么我们很容易知道n的阶乘的递归公式如下:

代码:

#include<stdio.h>
int Fact(int n)
{
    if(n==0)
        return 1;
    else
        return n * Fact(n - 1);
}


int main()
{
    int n = 0;
    scanf("%d",&n);
    printf("%d\n",Fact(n));

    return 0;
}

在进行测试的时候,n的值输入的不要太大,否则可能会溢出. 

2.举例2:顺序打印一个整数的每一位.

比如:

输入:1234  输出 1 2 3 4
输入987    输出  9 8 7 

如果是倒序打印每一位,我们首先可能想到的是,通过%10就可以得到最后一位,再/10,就可以去掉最后一位,通过反复进行我们就可以倒叙打印每一位了.

但是如果是顺序打印就不一样了,

我们发现一个数字的最低位很容易得到,%10就可以了,那么我们可以定义一个函数Print().

Print(n)
Print(1234) =  Print(123) + 4
Print(123) =  Print(12) + 3
.......

 这样我们就可以成功顺序打印每一位了.

代码:

#include<stdio.h>

void Print(int n)
{
	if (n < 10)
	{
		printf("%d  ", n);
	}
	else
	{
		Print(n / 10);
		printf("%d  ", n % 10);
	}
}
int main()
{
	int n = 0;
	scanf("%d",&n);
	Print(n);
	return 0;
}

在这个过程中,我们就是使用了大事化小的思路 

把Print(1234)打印1234每一位,拆解成Print(123)打印123每一位,再打印4;

把Print(123)打印123每一位,拆解成Print(12)打印12每一位,再打印3.

把Pirnt(12)打印12每一位,拆解成Print(1)打印1每一位,再打印2.

而Print(1)就是打印n.

 四.递归与迭代

递归是一种很好的编程技巧,但是可能会被误用,看到推导的公式,就很容易写成递归的形式

在C语言中,每一次函数的调用,都要为本次函数调用在栈区申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行堆栈,或者函数栈帧.

函数如果不返回的话.函数对应的栈帧空间就会一直被占用.所以如果函数调用中存在递归调用的话,每一次递归函数调用就会开辟属于自己的栈帧空间,直到函数递归不再进行下去,才会逐层释放空间.所以如果函数递归的方式完成代码,递归的层次太深,就会浪费太多的栈帧空间,引起栈溢出的问题,也就是开头说写的代码的问题.

比如计算n的阶乘.

以下代码也可以完成问题

int Fact(int n)
{
    int i = 0;
    int ret = 1;
    for(i = 1; i <= n ;i++)
    {
    ret *= i;
    }
    return ret;
}

上述代码是能够完成问题的,而且效率也比函数递归的方式更高.

事实上,我们看到的许多问题是以递归的方式解决问题的,这仅仅是因为它比非递归的方式理解起来更加容易,这是这类问题通过非递归也就是迭代的方式实现往往比递归的方式更高效.

3.举例3:求第n个斐波那契数.

首先我们先了解斐波那契数列(Fibonacci):

 斐波那契数列:  1 1   2   3  5    8   13   21 .

也就是在规定前两个数是1的情况下,其中某一个数是前两个数之和.

Fibo(n) = Fibo(n-1)+FIbo(n-2)

看到这个推导公式,很容易将代码写成递归的形式

#include<stdio.h>
int Fibo(int n)
{
	if (n <= 2)
	{
		return 1;
	}
	else
	{
		return Fibo(n - 1) + Fibo(n - 2);
	}
}
int main()
{
	int n = 0;
	scanf("%d",&n);
	printf("%d\n",Fibo(n));
	return 0;
}

在我们对这个代码进行测试的时候,我们发现当n的值稍微有点大的时候,程序运行所需的时间很长.

在计算机如此快的计算速度下,我们仍然需要很长的时间,只能说明这个代码的效率太低了.

在递归的过程中,我们很容易发现有许多重复的计算(图中相同的颜色),递归的次数越多,这些计算就会冗余,也就是效率降低.比如我们可以尝试计算第3个斐波那契数被计算的次数 

#include<stdio.h>
int count = 0;

int Fibo(int n)
{
	if (n == 3)
		count++;
	if (n <= 2)
	{
		return 1;
	}
	else
	{
		return Fibo(n - 1) + Fibo(n - 2);
	}
}
int main()
{
	int n = 0;
	scanf("%d",&n);
	printf("%d\n",Fibo(n));
	printf("%d\n",count);
	return 0;
}

count就是次数.

仅仅是求第50个斐波那契数,第三个斐波那契数就被计算了五亿多次,可见程序运行如此复杂,在我测试这个代码的时候大概用了20多秒.

所以我们就需要使用其他方法,我们可以将三个数依次定义为 a b c,c就等于a+b,当进行完这一过程后,我们将b赋值给a,再将c赋值给a,这样就往后移动了一步,再继续相加,就可以得到后面一个数.

#include<stdio.h>
int main()
{
	int n = 0;
	int a = 1;
	int b = 1;
	int c = 1;
	scanf("%d", &n);
	if (n <= 2)
	{
		printf("%d\n",c);
	}
	for (int i = 1; i <= n-2; i++)
		//开始计算的时候,是从第三个数开始的,所以需要-2
	{
		c = a + b;
		a = b;
		b = c;
	}
	printf("%d\n",c);
	return 0;
}

这样的方式效果就比递归的方式高出很多.

  • 31
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
C语言是一种广泛应用于系统编程和嵌入式开发的高级编程语言。函数指针是C语言中的一个重要概念,它可以指向函数,并且可以通过函数指针来调用函数。快速排序算法是一种常用的排序算法,它的核心思想是通过分治的策略将一个大问题分解为多个小问题,并通过递归的方式解决这些小问题。 下面是C语言中使用函数指针实现快速排序算法的示例代码: ```c #include <stdio.h> // 交换两个元素的值 void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } // 快速排序的分区函数 int partition(int arr[], int low, int high) { int pivot = arr[high]; // 选取最后一个元素作为基准 int i = (low - 1); // 定义一个指针,用于指向小于基准的元素 for (int j = low; j <= high - 1; j++) { if (arr[j] < pivot) { i++; swap(&arr[i], &arr[j]); } } swap(&arr[i + 1], &arr[high]); return (i + 1); } // 快速排序函数 void quickSort(int arr[], int low, int high) { if (low < high) { int pi = partition(arr, low, high); // 将数组分区,并获取分区点的位置 quickSort(arr, low, pi - 1); // 对分区点左边的子数组进行快速排序 quickSort(arr, pi + 1, high); // 对分区点右边的子数组进行快速排序 } } // 打印数组元素 void printArray(int arr[], int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[] = {10, 7, 8, 9, 1, 5}; int n = sizeof(arr) / sizeof(arr[0]); printf("原始数组:"); printArray(arr, n); quickSort(arr, 0, n - 1); printf("排序后的数组:"); printArray(arr, n); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值