🏝️专栏:《C语言逆袭新手村》
🌅主页:f狐o狸x
目录
前言
C语言函数是一种函数,用来编译C语言,一般包括字符库函数,数学函数,目录函数,进程函数,诊断函数,操作函数等
这里这个函数和我们高中时期学的函数类似,高中的函数是这样
F(x)=5x+21
这里是在括号里输入x,输出F(x)计算的值
比如这里输入100,那么结果就是521
在C语言中函数也是这样的
int add(int x)
{return y=5x+21;
}
同样都是给函数输入x的值,输出y
函数的运用
C语言中把函数分为两类,一类是库函数,另一类是自定义函数
1.1 库函数
库函数就是在C语言中已经有的一些函数,比如:printf()、scanf()、srtlne()……但是要调用这些函数的时候我们需要先在程序最开头交代一下用到的库函数对应#include的头文件,这就是为什么我们现在的程序第一排都有#include<stdio.h>,是因为printf、scanf这两个库函数的头文件是他。
想学习其他更多的库函数可以去http://www.cplusplus.com/reference/
1.2 自定义函数
一定不能当白嫖怪呀!如果所有的操作全都在库函数里面可以直接调用的话,那还要程序员干什么。因此我们也要学会写我们自己的函数。自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是这些都是我们自己来设计。这给我们一个很大的发挥空间
int fun_name(int x)
{
printf("haha\n");
}
int 返回类型
fun_name 函数名
int x 函数参数
于是我们就可以用函数来干很多事情了(先以最简单的比大小来当例子)
int get_max(int x, int y)
{
return x > y ? x: y;
}
int main()
{
int a = 0;
int b = 0;
int max = 0;
scanf("%d %d", &a, &b);
max = get_max(a, b);
printf("%d", max);
return 0;
}
再来一个例子,将输入的数字互换位置
//错误的交换这里的“a”“b”并不是主函数中的“a”“b”
//所以这里函数里面看似交换了ab的值,但是主函数里面还是没有
void swap(int a, int b)
{
int tmp = 0;
tmp = a;
a = b;
b = tmp;
return 0;
}
//正确的交换:直接用变量“a”“b”的地址进行交换
void swap(int* a, int* b)
{
int tmp = 0;
tmp = *a;
*a = *b;
*b = tmp;
return 0;
}
int main()
{
int a = 0;
int b = 0;
printf("交换前:");
scanf("%d %d", &a, &b);
swap(&a, &b);
printf("交换后:%d %d\n",a,b);
return 0;
}
递归思想
开始上强度,知道函数的基本概念之后,我们就要学一个新的东西:函数递归
循环有两种方式,一个是迭代,另一个就是递归
2.1 递归是什么
给大家讲一个故事:从前有座山,山上有座庙,庙里有个老和尚在给小和尚讲故事,讲的是:从前有座山,山上有座庙,庙里有个老和尚在给小和尚讲故事,讲的是:从前有座山,山上有座庙,庙里有个老和尚在给小和尚讲故事,讲的是:……
这个故事就是一个递归
递归是不断调用自身,假设递归自己是一个函数的话,那递归的返回值会当作参数再次传入自身。 迭代是将某一个初值设定,不断放入某一个循环体,得到的值成为一个新值再次放入循环体中,通过循环体中的操作,逐步得到我们想要的结果。
迭代是人的思考方式,而递归是神的思考方式
理解递归是我们封神的第一步
程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接 调用自身的 一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
递归,顾名思义需要先递推再回归,就是先一步步把难解决的问题递推到简单好处理的问题上,在再一步步反推上去,最终计算出全部的结果。
其实我们在高中时期就遇到过递归思想:证明数列时用到的数学归纳法就是其中之一
最简单和常见的数学归纳法是证明当n等于任意一个自然数时某命题成立。证明分下面两步:
1.证明当n= 1时命题成立。
2.假设n=m时命题成立,那么可以推导出在n=m+1时命题也成立。(m代表任意自然数)
2.2 递归的两个必要条件
1.存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2.每次递归调用之后越来越接近这个限制条件。
满足上面的条件递归函数不一定正确,但是不满足上面两个条件的函数递归一定是错的
2.3 递归的运用
接受一个整型值(无符号),按照顺序打印它的每一位。
void print(int a)
{
if (a > 9)
{
print(a / 10);
}
printf("%d ", a%10);
}
int main()
{
int a = 5678;
print(a);
return 0;
}
程序运行的原理图如下:
这里可以看到print函数不断调用自己打印出对应的数字,这就是递归
再来一题练练手
编写一个函数实现n的k次方,使用递归实现
int my_pow(int n, int k)
{
if (k > 0)
return n * my_pow(n, k - 1);
return 1;
}
int main()
{
int n = 0;
int k = 0;
int ret = 0;
printf("请输入n k:");
scanf("%d %d", &n, &k);
ret = my_pow(n,k);
printf("%d的%d次方为:%d\n", n, k, ret);
return 0;
}
这个问题也可以用循环来解决:
int my_pow(int n, int k)
{
int i = 0;
int ret = 1;
for (i = 0; i < k; i++)
{
ret = ret * n;
}
return ret;
}
int main()
{
int n = 0;
int k = 0;
int ret = 0;
printf("请输入n k:");
scanf("%d %d", &n, &k);
ret = my_pow(n,k);
printf("%d的%d次方为:%d\n", n, k, ret);
return 0;
}
这里的循环其实就是迭代,我们可以从中看出迭代和递归两个方法的优缺点:
1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
2.4 汉诺塔问题
汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
求当有n个圆盘的时候,需要挪动的最少步数x
这个问题一样可以用递归来解决
如果只有一个圆盘的时候,只需要一步就能解决问题,如果我们把计算步数的函数命名为hannuo()
也就是hannuo(1)=1
如果有两个圆盘,
就先变成一个大圆盘加上一个小圆盘,
再把大圆盘移到其他柱子上,
最后把小圆盘移到大圆盘上
显然需要3步
那么这里就是hannuo(2)=hannuo(1)+1+hannuo(1)=3
我们继续把圆盘的数量增加到3个
第一步:先把整体分为大圆盘+n-1个圆盘(步数为:hannuo(2))
第二步:把大圆盘换位置(步数为:1)
第三步:再把这n-1个圆盘移到大圆盘上(步数为:hannuo(2))
所以总步数为:hannuo(3)=hannuo(2)+1+hannuo(2)
相信聪明的你已经发现规律了,那么为n个圆盘的时候呢?
hannuo(n)=2hannuo(n-1)+1
这就是一个典型的递归思想
代码为:
int hannuo(int n)
{
if (n > 1)
return 2*hannuo(n-1)+1;
return 1;
}
int main()
{
int n = 0;
printf("请输入圆盘个数:");
scanf("%d", &n);
int x = hannuo(n);
printf("%d个圆盘需要%d步\n", n, x);
return 0;
}
2.5 青蛙跳台阶问题
题目为:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
假设计算多少种跳法的函数命名为:ways(x)
显然ways(1)=1,ways(2)=2
如果有n级台阶的时候,我们把计算步数分为两大类:
第一类:如果青蛙第一次只跳一级台阶,那么就还剩下n-1级台阶(ways(n-1)种跳法)
第二类:如果青蛙第一次跳了二级台阶,那么就还剩下n-2级台阶(ways(n-2)种跳法)
所以总的跳法就应该有ways(n)=ways(n-1)+ways(n-2)
此时问题就被我们解决了,代码为:
int ways(int n)
{
if (n > 2)
return ways(n - 1) + ways(n - 2);
else if (2 == n)
return 2;
else
return 1;
}
int main()
{
int n = 0;
int ret = 0;
printf("请输入青蛙要跳的台阶数n:");
scanf("%d", &n);
ret = ways(n);
printf("青蛙一共有%d种跳法\n", ret);
return 0;
}
2.6 斐波那契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列 [1],因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称“兔子数列”,其数值为:1、1、2、3、5、8、13、21、34……在数学上,这一数列以如下递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。
其实上一个青蛙跳台阶问题就是斐波那契数列问题,我们除了用递归的方法来完成斐波那契数列,也可以用循环的方法:
int feibo(int n)
{
int a = 1;
int b = 1;
int ret = 1;
while (n > 2)
{
ret = a + b;
a = b;
b = ret;
n--;
}
return ret;
}
int main()
{
int n = 0;
int ret = 0;
printf("输入:");
scanf("%d", &n);
ret = feibo(n);
printf("第%d个斐波那契数为:%d", n, ret);
return 0;
}
都看到这里了,还不给个小赞?QAQ