函数与递归

一、函数及其应用

1.函数的概念 

函数是程序中一段相对独立的代码,这段代码能够实现某一项具体、独立、完整的功能。

函数分为系统函数自定义函数

常用的系统函数包括

  • 主函数:main
  • 数字函数(cmath):fabs、sqrt、powceil、floor...
  • 字符串函数(cstring):strlen、strcmp...
  • 算法函数(algorithm): max、min...  

2.函数的定义

        返回类型 函数名(参数列表)

        {

                函数体

        }

例:计算两个数的较大者

double imax(double a, double b){
    double ans;
    if(a>b) ans=a; else ans=b;
    return ans;
}

   说明:

  • 自定义函数类似于系统函数,参数列表相当于输入;
  • 函数名类似于变量名,命名规则一致,注意知名见意;
  • 参数列表可以为空,即无参函数,也可以有多个参数,参数之间用逗号隔开,每个参数包括参数类型和参数名;
  • 返回类型除了可以是int、double、bool、char等以外,也可以没有返回值(void);
  • 如返回类型不是void,函数体至少包含一个return语句,其返回值类型应与返回类型一致。

 3.函数的作用

  • 代码复用

        同一个函数可以被一个或多个函数调用多次,从而减少重复代码的编写。

  • 问题分析

        按照模块化思想,一个大的应该分解成若干结构清晰、功能独立、调试方便的小程序段。 

 例1:计算阶乘和

计算3!+   5!+   14!(其中n!=1x2x3x...xn )

分析:

        只要实现一个自定义函数fac用来计算n!即可。

#include<bits/stdc++.h>
using namespace std;
int fac(int x){
    int s=1;
    for(int i=1;i<=x;i++) s*=i;
    return s;
}

int main(){
    int sum=fac(3)+fac(5)+fac(14);
    cout<<sum<<endl;
    return 0;
} 

例2: 输出如图所示的图形

#include<bits/stdc++.h>
using namespace std;
void pic(int n){
    int i,j,k;
    for(i=1;i<=n;i++){
        for(j=1;j<=40-i;j++)
            cout<<" ";
        for(k=1;k<=2*i-1;k++)
            cout<<"*";
        cout<<<endl;
        }
}

int main({
    pic(4);pic(6);
    return 0;
}

4.函数的调用

程序中对函数的使用称为函数调用。

函数可以在表达式中被调用,也可以在其他函数体内被调用,还可作为另一个函数的参数被调用。

函数调用的一般形式为:

        函数名(实参1,实参2,...)

4.1形式参数与实际参数

(1)实际参数:在调用函数名后面的()内的变量或表达式称为实际参数(简称实参)。

(2)形式参数:在被调用函数名后面的()内的变量称为形式参数(简称形参)。

例如: 

#include<bits/stdc++.h>
using namespace std;
int maxint(int x,int y){ // 函数定义的x,y变量是函数调用的形参
    if(x>y)
        return x;
    else
        return y;
}

int main(){
    int a,b;
    cin>>a>>b;
    cout<<maxint(a,b)<<endl; // 函数定义的a,b变量是函数调用的实参
    return 0;
}

 注意:

(1)函数的实参与形参必须数量一致、顺序一致、数据类型一致。

(2)参数的类型可以是常量、变量、表达式、数组或函数等。

4.2调用方法

对于没有返回值的函数,调用时直接单独一行写“函数名(参数);

对于有返回值的函数,调用时必须以值的形式出现在表达式中。

程序可以调用任何前面已定义的函数,如果我们需要调用在后面定义的函数,就要先声明该被调用的函数。

4.3函数调用的执行过程

(1)计算实际参数的值。
(2)将实际参数传递给被调用函数的形式参数,程序执行跳到被调用的函数中。
(3)执行函数体,执行完后如果有返回值,则把返回值返回给调用该函数的地方继续执行。

5.参数的传递

5.1传值调用

传值调用方式将实参的值复制给形参(实参可以是变量、常量、或表达式)。本质上,形参是实参的一个副本,改变形参的值不会影响实参中值的内容。实参与相对应的形参甚至可以是相同的参数名,但它们是不同的内存存储地址

5.2引用调用

引用是使用变量的内存存储地址来访问操作变量的特殊形式。

引用变量类型定义:

        数据类型 变量

        数据类型  &变量

引用类型定义中的数据类型称为引用的 “基类型” ,如int&是int类型的引用。

5.3数组作为参数

将数组名作为参数传给函数,实际上是把数组的地址传给函数。形参数组和实参数组的首地址重合,因此在被调用函数中对数组元素值进行改变,主调函数中实参数组的相应元素值也会改变。

6.变量的作用域

变量按其在程序中作用的范围,分为全局变量局部变量

6.1全局变量

全局变量是指在程序代码开始执行前初始化并分配存储空间的变量,它在整个程序中(主程序和子程序)都起作用,整个程序执行结束后释放存储空间。

6.2局部变量

局部变量是指在函数内部定义的变量,它只在函数内部起作用在函数调用是动态初始化,函数调用完毕后释放存储空间。

7.函数的应用

例1:最大公约数

输入两个数,输出它的最大公约数

#include<bits/stdc++.h>
using namespace std;
int f(int x,int y){
	for(int i=x;i>=1;i--)
		if(x%i==0 && y%i==0)
			return i;
}

int main(){
	int x,y;
	cin>>x>>y;
	cout<<f(x,y)<<endl;
	return 0;
} 

例2:阶乘(factorial)

一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。
计算3!+7!+11!

#include<bits/stdc++.h>
using namespace std;
int fac(int x){
	int s=1;
	for(int i=1;i<=x;i++)
		s*=i;
	return s;
}

int main(){
	cout<<fac(3)+fac(7)+fac(11)<<endl;
	return 0;
} 

例3:孪生素数

输出1000以内所有的孪生素数对(差值为2)以及总对数。

#include<bits/stdc++.h>
using namespace std;
bool f(int x){
	bool s=true;
	for(int i=2;i<=x-1;i++)
		if(x%i==0)
			s=false;
	return s;
}

int main(){
	int s=0;
	for(int i=3;i<=997;i+=2)
		if(f(i)&&f(i+2))
			s+=1;
	cout<<s<<endl;
	return 0;
}

例4:数字统计

统计在给定范围[L,R]的所有整数中,数字2出现的次数。
比如在给定范围[2,22],数字2在2中出现了1次,在12中出现了1次,在20中出现了一次,在21中出现了1次,在22中出现了两次,所以一共出现了6次

#include<bits/stdc++.h>
using namespace std;
int f(int x){
	int s=0;
	while(x){
		if(x%10==2)
			s+=1;
		x=x/10;
	}
	return s;
}

int main(){
	int l,r;
	cin>>l>>r;
	int s=0;
	for(int i=l;i<=r;i++)
		s+=f(i);
	cout<<s<<endl;
	return 0;
}

例5:火柴数字

火柴数字如下图:

现用6根火柴摆数字,请列出所有能摆出的自然数,要求每个数火柴全用上,不多不少。

#include<bits/stdc++.h>
using namespace std;
int tj(int x){
	int f[]={6,2,5,5,4,5,6,3,7,6};	//0~9分别需要多少根火柴棒
	int s=0;			//统计总数目
	int ti=x;			//因x会在内层循环时发生改变,在此对i进行保存以便后续使用
    while(ti){			//计算ti需要多少根火柴棒,ti非0才会执行循环,所以0不在计算范围
    	s+=f[ti%10];	//个位所用的数量
    	ti=ti/10;		//整除10,除去个位数
    }
    return s;
}
int main(){
	cout<<0<<endl;			
	for(int i=1;i<112;i++)	//穷举范围
	    if(tj(i)==6)
	        cout<<i<<endl;
	return 0;
}

二、递归及其应用

8.递归函数

递归是计算科学领域中一种重要的计算思维模式。它既是一种抽象表达的手段,也是一种问题求解的重要方法。直接或间接地调用自身的方法称为递归,递归分为递推和回归。指一种通过重复将问题分解为同类的子问题,从而解决问题的方法。

在数学与计算机领域中,递归函数是指用函数自身来定义该函数的方法。如著名的斐波那契数列"1 1 2 3 5 8 13 …",可以递归定义为

          1(n=1或n=2)

​F(n)=

          F(n−1)+F(n−2)    (n>2)

递推关系是递归的重要组成,例如上式中的 F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n)=F(n-1)+F(n-2)F(n)=F(n−1)+F(n−2);而边界情况是递归的另一要素,例如上式中当 n 为 1 或 2 时的 F ( n ) = 1 F(n)=1F(n)=1,保证递归能在有限次的计算后得出结果,而不会产生无限的情况。

【样例研习】
以下为用递归算法求斐波那契数列第n项的C++函数

int fibo(int n){
    if(n==1 || n==2)
        return 1;
    else
        return fibo(n-1)+fibo(n-2);
}

本题的递推公式为 fibo( n ) = fibo ( n−1 ) + fibo ( n−2 ),边界条件为 fibo ( 1  or  2 ) = 1
求解过程如下:

fibo(5)

=fibo(4)+fibo(3)

=(fibo(3)+fibo(2))+(fibo(2)+fibo(1))

=((fibo(2)+fibo(1))+1)+(1+1)=(1+1)+1)+(1+1)

=5

递归算法需确定的两个条件:
1、递推关系
2、边界条件(即递归退出的条件)

常用的递归代码框架:

函数类型 函数名(形式参数):
	if(边界条件)
		语句组
	else
		递推公式

递归与迭代:
(1)递归与迭代算法都需要重复执行某些代码
(2)递归是重复调用函数自身,遇到满足终止条件时逐层返回;迭代是重复反馈过程,其目的是逼近所需目标或结果,通常使用计数器结束循环。

9.递归的应用

1、斐波那契数列:
1 1 2 3 5 8 13 21 34 55 89 ...
已知前两项为1,之后每一项等于前两项之和。
现输入n,请输出兔子数列的第n项。

2、用递归法求4!+5!+6!+7!的值。

                1  ( n = 0 )
F ( n ) =  

                n ∗ F ( n − 1 )     ( n > 0 ) 

本题的递推公式为 fac ( n ) = fac ( n−1 ) ∗ n,边界条件为 fac ( 0 ) = 1 。

fac(4)

=4∗fac(3)

=4∗(3∗fac(2))

=4∗(3∗(2∗fac(1)))

=4∗(3∗(2∗(1∗fac(0))))

=4∗(3∗(2∗(1∗1)))

3、用递归法求1+2+3+…+100的值。

4、输入两个数,求其最大公约数。
根据上文中所讲的辗转相除法可得公式如下:

                b(a%b=0)

gcd(a,b)=gcd(b,a%b)    (a%b=0)​

5、花果山上有一洞,小猴每次采取跳1阶或者跳3阶的办法从山下跳跃上台阶进洞,编程输入台阶数,输出有多少种不同的跳法。

  • 25
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值