五、函数

时间:2020-01-24 14:43:19   收藏:0   阅读:64

? C语言的程序是由函数组成的。最简单的程序有一个主函数main(),但实用程序往往由多个函数组成,由主函数调用其他函数,其他函数也可以互相调用。

? 函数是C语言程序的基本模块,程序的许多功能是通过对函数模块的调用来实现的,学会编写和调用函数可以提高编程效率。


5.1 函数的定义

数据类型 函数名(形式参数列表) {
    函数体 //需要执行的语句
}
  1. 函数的数据类型是函数的返回值类型(若数据类型为 void,则无返回值)。函数返回值不能是数组,也不能是函数,除此之外任何合法的数据类型都可以,如:int、long long、float、char等。
  2. 函数名是标识符,一个程序中除了主函数名必须为main外,其余函数的名字按照标识符的取名规则可以任意选取,最好取有助于记忆的名字。
  3. 形式参数(简称形参)列表可以是空的(即无参函数),也可以有多个形参,形参间用逗号隔开,不管有无参数,函数名后的圆括号都必须有。形参必须有类型说明,形参可以是变量名、数组名或指针名,它的作用是实现主调函数与被调函数之间的关系。
  4. 函数中最外层一对花括号“{ }”括起来的若干个语句组成了一个函数的函数体。由函数体内的语句决定该函数功能。函数内部应有自己的说明语句(即变量的定义)和执行语句(即运算),但函数内定义的变量不可以与形参同名,形参和函数内定义的普通变量的生命周期取决于该函数。函数体中也可以没有任何语句,即空函数。
  5. 函数不允许嵌套定义。在一个函数内定义另一个函数是非法的。但是允许嵌套使用,也就是后面我们要讲到的递归。
  6. 函数在没有被调用的时候是静止的,此时的形参只是一个符号,它标志着在形参出现的位置应该有一个什么类型的数据。函数在被调用时才执行,也就是在被调用时才由主调函数将实际参数(简称实参)值赋予形参。这与数学中的函数概念相似,如数学函数:\(f(x)=x^2+x+1\),这样的函数只有当自变量被赋值以后,才能计算出函数的值。

5.2 函数定义的例子

//定义一个函数,返回两个数中较大者。
int mymax(int x, int y) {//该函数返回值是整型,有两个整型的形参,用来接受实参传递的两个数据
    return (x > y ? x : y); //函数体内的语句是求两个数中的较大者并将其返回主调函数。
}

5.3 函数的形式

  1. 无参函数:无参函数顾名思义即为没有参数传递的函数,无参函数一般不需要带回函数值,所以函数类型说明为void。

  2. 有参函数:有参函数即有参数传递的函数,一般需要带回函数值。例如int max(int x,int y)

  3. 空函数:空函数即函数体只有一对花括号,花括号内没有任何语句的函数。例如:

    //空函数不完成什么工作,只占据一个位置。在大型程序设计中,空函数用于扩充函数功能。
    void zhanzuo() {
    }

5.4 函数的声明

? 数据类型 函数名(含类型说明的形参表);

5.5 函数的调用

? 函数名 (参数列表)

5.6 函数的返回值


5.7 我们补全引例的代码

#include<cstdio>
int jc(int); //函数的声明
int main() {
    int sum = 0;
    for (int i = 1; i <= 10; ++i) {
        sum += jc(i); //函数的调用
    }
    printf("sum = %d\n", sum);
    return 0;
}
// 函数的定义
int jc(int n) {
    int ans = 1;
    for (int i = 1; i <= n; ++i) {
        ans *= i; 
    }
    return ans; //函数的返回值
}

5.8 递归

5.8.1 概念

5.8.2 例题

5.8.2.1 阶乘

一个正整数的阶乘 factorial是所有小于及等于该数的正整数的积,并且0的阶乘为1,自然数n的阶乘写作 n!。输入正整数n,请求出n!

5.8.2.2 斐波那契数列

斐波那契数列的排列是:0,1,1,2,3,8,13,21,34,55,89,144……依次类推下去,你会发现,它后一个数等于前面两个数的和。在这个数列中的数字,就被称为斐波那契数。输入正整数n,请求出第nFibonacci数。

5.8.2.3 倒序输出

例如给出n正整数 {1,2,3,4,5},希望以各位数的逆序形式输出,即输出{5,4,3,2,1}。希望以递归形式输出。

5.8.2.4 汉诺塔

有三根杆子A,B,CA杆上有n个(n>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:

  1. 每次只能移动一个圆盘;
  2. 大盘不能叠在小盘上面。

技术分享图片

输入正整数n,输出n个盘子的移动过程。

5.8.2.5 二分法
5.8.2.5 集合的划分

Description

Input

Output

Sample Input

10 6

Sample Output

22827
5.8.2.6 放苹果一

Description

Input

Output

Sample Input

1
7 3

Sample Output

8
5.8.2.7 放苹果二

Description

Input

Output

Sample Input

1
7 3

Sample Output

4
5.8.2.8 整数划分

Description

Input

Output

Sample Input

1
4 3

Sample Output

4
例题总结:
例题扩展:
  1. 整数划分中,\(n=m_1+m_2+...+m_i\) ,每个数最多只允许使用一次时的方案数?

    • 分析:
      • 此问题是在整数划分的基础上增加了一重限制,即,\(m_1,m_2,...,m_i\) i 个元素互不相同。
      • 整数划分的通用递推公式为:f(n,m)=f(n,m-1)+f(n-m,m)
        • f(n,m-1):表示方案中累加因子里没有m,最大可能为m-1的方案数,显然是一个递归的子问题。
        • f(n-m,m):表示方案中累加的因子里m剩下因子之和为n-m的方案数。
          • 关键问题是,剩下的因子里有没有可能存在m的问题。
          • f(n-m,m) 表示最大因子不超过m,如果n-m>=m,是可能分出等于m的因子的。
          • 那如何让剩下的因子不能为m呢?
          • 显然我们只需修改最大限制即可,即把m编成m-1,即f(n-m,m)修改成 f(n-m,m-1).
      • 递推公式为:f(n,m)=f(n,m-1)+f(n-m,m-1)
  2. 整数划分中,\(n=m_1+m_2+...+m_i\) ,要求\(m_1,m_2,...,m_i\) 均为奇数的方案数。
    • 方法一:
      • 定义f[i][j]:正整数i划分成 j 个奇正整数之和的方案数。
      • g[i][j]:正整数i划分成 j 个偶正整数之和的方案数。
      • 问题分成两个子问题:
        1. 方案中包含至少一个奇数1
          • i中拿出一个1,则剩下的i-1分出j-1 个奇数之和
          • 正好是一个递归的子问题
          • 方案数为:f[i-1][j-1]
        2. 方案中没有奇数1,即最小的奇数至少为3
          • i中拿出j1 放到每一份中。
          • 剩下的i-j,我们只需分成j个偶数即可
          • 方案数为:g[i-j][j]
      • 递推公式:f[i][j]=f[i-1][j-1] + g[i-j][j]
      • 临界条件:
        1. i<j时:f[i][j]=0, g[i][j]=0
        2. i==j时:
    • 方法二:
      • f[n][k]表示n的划分中最大值为k的划分数。
        1. k = 1 时,其结果只能为n1
        2. k 是偶数时,有f[n][k] == f[n][k-1]
        3. k > n 时,有f[n][k] = f[n][n]
        4. n >= k 时,我们可以把问题分为两个子问题:
          1. 方案中有奇数k,拿出奇数k,剩下的n-k,分出不超过k的方案数:f[n-k][k]
          2. 方案中没有奇数k,则方案数为f[n][k-1],因为k为奇数,可以直接写上f[n][k-2]
五种模型代码实现:
//1.将n划分成若干正整数之和的划分数,结果对Mod取余。
int Part1(int n,int m){//把n分成最大因子不超过m的划分,若干相当于m==n
    if(f[n][m])return f[n][m];//记忆化
    if(n==1||m==1||n==0)return f[n][m]=1;//临界
    if(n<m)return f[n][m]=Part1(n,n)%Mod;//没有负数,最大因子不可能大于n
    return f[n][m]=(Part1(n-m,m)%Mod+Part1(n,m-1)%Mod)%Mod;//方案中有m和没有m进行递归解决
}
//2.将n划分成k个正整数之和的划分数,结果对Mod取余。
int Part2(int n,int k){
    if(f[n][k])return f[n][k];//记忆化
    if(k==1||n==k)return f[n][k]=1;//临界
    if(n<k)return 0;//n不可能划分出大于n份的正整数之和
    return f[n][k]=(Part2(n-1,k-1)%Mod+Part2(n-k,k)%Mod)%Mod;//方案中有1和没有1进行递归解决
}
//3.将n划分成最大数不超过k的划分数,结果对Mod取余。
//同Part1
//4.将n划分成若干个奇正整数之和的划分数,结果对Mod取余。
int Part4(int n,int k){//把n分成最大因子不超过k的划分
    if(f[n][k])return f[n][k];//记忆化
    if(k==1||n==0)return f[n][k]=1;//临界
    if(n<k)return f[n][k]=Part4(n,n)%Mod;//分出的因子最大不可能超过n
    if(k%2==0)return f[n][k]=Part4(n,k-1)%Mod;//k为偶数,显然最大因子为k-1
    if(k%2==1)return f[n][k]=(Part4(n-k,k)%Mod+Part4(n,k-1)%Mod)%Mod;//k为奇数,分成有k和没有k递归处理
}
//5.将n划分成若干不同整数之和的划分数,结果对Mod取余。
int Part5(int n,int k){//n分成最大因子不超过k,且最多为一个的划分
    if(f[n][k])return f[n][k];//记忆化
    if(k==1&&n>1)return 0;//最多只能有一个1
    if(n==1||n==0||k==1)return f[n][k]=1;//临界
    if(n<k)return f[n][k]=Part5(n,n)%Mod;//最大因子不可能超过n
    return f[n][k]=(Part5(n-k,k-1)%Mod+Part5(n,k-1)%Mod)%Mod;//方案中有k其递归子问题最大不能超过k-1
}

原文:https://www.cnblogs.com/hbhszxyb/p/12232098.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!