C语言程序设计笔记---009
C语言函数递归
/知识点汇总/
1、函数递归
(1)程序用自身的编程技巧称为递归。递归作为一种算法在程序中广泛应用。简单地说,理解为直接或间接的调用自身的一种方法。
(2)主要思想:大事化小
(3)递归的两个必要条件:
1.存在限制条件,当满足这个限制条件时,递归便不在继续。
2.每次递归调用之后越来越接近这个限制条件。
1.1、函数递归例程1
说明:用递归写一个,接受无符号整型数据,按照顺序打印它的每一位。
例如:输入:1234,输出:1 2 3 4
思路:
1234/10 = 123--1234%10 = 4
123/10 = 12--123%10 = 3
12/10 = 1---12%10 = 2
1/10 = 0--1%10 = 1
printf(1234)
printf(123) 4
printf(12) 3 4
printf(1) 2 3 4
1.1、函数递归例程1:
/*例程1.1*/
#include <stdio.h>
//实现打印每一位功能
void print(unsigned int n)//1234
{
//递归限制进入条件
if (n > 9)//n>9表示n是一位数以上
{
//递归调用
//递归逼近跳出条件,使越来越接近这个n>9限制条件
print(n / 10);// (1234>9) 1234/10 = 123 // (123>9) 123/10 = 12 // (12>9) 12/10 = 1 //(1<9) printf(1%10)--1
}//(1<9) printf(1%10)--1 //printf(12%10)--2 //printf(123%10)---3 //printf(1234%10)---4
printf("%d ", n % 10);//1 2 3 4
}
int main()
{
unsigned int num = 0;
printf("请输入一个无符号整数:");//1234
scanf("%u", &num);//%u格式符对应
//递归--函数自己调用自己
print(num);//自定义print()函数是打印每一位数据
printf("\n");
return 0;
}
1.2、函数递归例程2
栈区:放程序中临时的变量,如局部变量、函数形参
堆区:放动态内存分配的,如:malloc/free/calloc/realloc
静态区:放全局变量、静态变量
栈溢出:栈区空间被占用完,使得溢出。
图示:
/*例程1.2*/
#include <stdio.h>
void test(int n)
{
if (n < 10000)
{
test(n+1);
}
}
int main()
{
test(1);
printf("栈溢出");
return 0;
}
图示:
小结:
所以写递归代码时:
a、不能死递归,需满足必要条件:要有跳出限制条件,要有每次递归逼近条件
b、递归的层次防止太深,防止栈区溢出。
c、相当于秩序员知乎:www.stackoverflow.com 全英文
1.3、函数递归例程3
说明:编写函数不允许创建临时变量,求字符串长度
1.3.1、复习strlen()函数
/*例程1.3.1*/
#include <stdio.h>
#include <string.h>
int main()
{
char ch[] = "abc";
printf("%d\n",strlen(ch));
return 0;
}
1.3.2、平时使用临时变量的写法
/*例程1.3.2*/
#include <stdio.h>
int my_strlen(char* pa)
{
int count = 0;
while (*pa != '\0')//循环判断直到计数完毕
{
count++;
*pa++;
}
return count;
}
int main()
{
char ch[] = "abc";
//用函数模拟计算字符串长度
printf("%d\n", my_strlen(ch));
return 0;
}
1.3.3、使用递归来解决,不用变量
大事化小的思路:
strlen("abc")
1 + strlen("bc")
1 + 1 + strlen("c")
1 + 1 + 1 +strlen("")
1 + 1 + 1 + 0 = 3
/*例程1.3.3*/
#include <stdio.h>
int my_strlen(char* pa) //读取首地址
{
//递归的限制条件
if (*pa != '\0')//判断第一个字符是否为结束符
{
//递归越来越接近这个限制条件
return 1 + my_strlen(pa+1);//递归累加逻辑//pa+1下一个字符的首地址,依次类推
}
else
{
return 0;//判断到为'\0'
}
}
int main()
{
char arr[] = "abc";
printf("%d\n",my_strlen(arr));
return 0;
}
2、函数递归与迭代
迭代:重复的做一件事,循环属于迭代
2.1、函数递归与迭代例程1
说明:写一个求n的阶乘
方法一:
/*例程2.1*/
#include <stdio.h>
int main()
{
int i = 0;
int n = 0;
printf("请输入一个整数:");
scanf("%d",&n);
int ret = 1;
//迭代--循环
for (i = 1; i <= n; i++)//产生1~n个数字
{
ret = ret * i;
}
printf("阶乘为:%d\n",ret);
return 0;
}
方法二:
/*例程2.1*/
#include <stdio.h>
//递归方式
int Fac(int m)
{
//递归的限制条件
if (m <= 1)
{
return 1;
}
else
{
//递归越来越接近这个限制条件
return m*Fac(m - 1);
}
}
int main()
{
int n = 0;
printf("请输入一个整数:");
scanf("%d", &n);
int ret = Fac(n);
printf("阶乘为:%d\n", ret);
return 0;
}
2.2、函数递归与迭代例程2
说明:求第n个斐波那契数:前两个数的和等于第三个
如:1 1 2 3 5 8 13 21 34 55…
方法一:
/*例程2.2*/
#include <stdio.h>
//依据公式得知
//计算n = 50时,10min 这知这种效率低---重复大量的计算
int count = 0;//统计计算次数
int Fib(int n)
{
//统计第三个斐波那契数的计算次数
if (n == 3)
count++;
//递归的限制条件
if (n <= 2)
{
return 1;
}
else
{
//递归越来越接近这个限制条件
return Fib(n-1)+Fib(n-2);
}
}
int main()
{
int n = 0;
printf("请输入一个整数:");
scanf("%d", &n);
int ret = Fib(n);
printf("斐波那契数为:%d\n", ret);
printf("count = %d\n",count);
return 0;
}
小结:通过此代码说明:递归可以求解,但效率低
方法二: 现在通过循环函数尝试解决问题
/*例程2.2*/
#include <stdio.h>
int Fib(int n)
{
int a = 1;
int b = 1;
int c = 1;//c=1保证了当n<2时,输出1
while (n > 2)
{
c = a + b;
a = b;
b = c;
n--;//越来越接近这个限制条件,也避免n不变造成死循环,即算一次少一次
}
return c;
}
int main()
{
int n = 0;
printf("请输入一个整数:");
scanf("%d", &n);
int ret = Fib(n);
printf("斐波那契数为:%d\n", ret);
return 0;
}
扩展:1.汉诺塔 2.青蛙跳台(斐波那契数列)
3、函数递归与迭代综合练习题
3.1、练习题1
说明: 写一个计算100以内数字9的个数
如:9 19 29 39 49 59 69 79 89 99
90 91 92 93 94 95 96 97 98 99
#include <stdio.h>
int main()
{
int i = 0;
int count = 0;
for (i = 1; i < 100; i++)
{
if (i % 10 == 9)//个位上为9
{
count++;
}
if (i / 10 == 9)//十位数上是9
{
count++;
}
}
printf("%d\n",count);//20--个位10个 + 十位10个
return 0;
}
3.2、练习题2
说明: //计算1/1-1/2+1/3-1/4+1/5…+1/99-1/100的值
思路:假设为全加号
方法一:
#include <stdio.h>
int main()
{
int i = 0;
double sum = 0.0;
for (i = 1; i <= 100; i++)//分母是1~100
{
if (i % 2 == 0)//为偶数
{
sum -= 1.0/i;//注意:C语言中除号两端决定精度,故这里用1.0
}
else//为奇数
{
sum += 1.0 / i;
}
}
printf("%f\n",sum);
return 0;
}
方法二:
#include <stdio.h>
int main()
{
int i = 0;
double sum = 0.0;
int flag = 1;//标志算术符号的转变,默认为正1
for (i = 1; i <= 100; i++)//分母是1~100
{
sum += flag*1.0 / i;//奇变偶不变
flag = -flag;//负号的转变
}
printf("%f\n", sum);
return 0;
}
3.3、练习题3
说明: 求10个整数的最大值
#include <stdio.h>
int main()
{
int arr[] = { 1, -2, -3, 4, -5, -6, 7, 8, -9, -10 };
//int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int i = 0;
//此时为数组有负数,那么会出错
//int max = 0;
//for (i = 0; i<10; i++)
//{
// if (arr[i]>max)
// {
// max = arr[i];
// }
//优化:兼容性
int max = arr[0];
for (i = 1; i < 10; i++)
{
if (arr[i]>max)
{
max = arr[i];
}
}
printf("%d\n",max);
return 0;
}
3.4、练习题4
说明: 写一个9*9乘法口诀表
#include <stdio.h>
int main()
{
int i = 0;
//行数
for (i = 0; i < 10; i++)
{
//打印每一行
int j = 0;
//列数
for (j = 1; j <= i; j++)
{
printf("%d*%d=%2d ",i,j,i*j);//%2d对齐,i*j--行乘以列
}
printf("\n");//打完一行后换行
}
return 0;
}
3.5、练习题5
说明: 编写一个函数reverse_string(char* string),以递归实现
将参数字符串中的字符反向排列,不是逆序打印
不能用C函数库中的字符串操作函数
如:char arr[] = “abcdef”
逆序后:fedcba
方法一:
#include <stdio.h>
//计算字符串长度
int my_strlen(char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;//移动指针指向下一位字符
}
return count;
}
//使得数组内容逆序
void reverse_string(char* str)
{
int left = 0;
//int right = strlen(str) - 1;//根据题目,所以strlen()不可用
int right = my_strlen(str) - 1;
while (left < right)
{
int temp = str[left];//str[left]等价于*(str+left)
str[left] = str[right];//str[right]等价于*(str+right)
str[right] = temp;
left++;
right--;
}
}
int main()
{
char arr[] = "abcdef";
reverse_string(arr);
printf("%s\n",arr);
return 0;
}
方法二:
#include <stdio.h>
//计算字符串长度
int my_strlen(char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;//移动指针指向下一位字符
}
return count;
}
//使得数组内容逆序
void reverse_string(char* str)
{
char temp = *str;//步骤1//将字符串首地址a给temp
int len = my_strlen(str);//计算长度
*str = *(str + len - 1);//步骤2
//*(str + len -1)表示最后一个字符的地址解引用,这里指f
//len -1,因为需要减掉'\0'
*(str + len - 1) = '\0';//步骤3
//递归实现交换逆序//步骤4
//表示字符串到了最后1个字符交换逆序,不变仍然是本身
if (my_strlen(str + 1) >= 2)//所以,只要大于等于两个字符就会执行交换逆序
{
reverse_string(str + 1);//即,又从b开始,按照步骤12345,依次类推
}
*(str + len - 1) = temp;//步骤5
}
int main()
{
char arr[] = "abcdef";
reverse_string(arr);
printf("%s\n", arr);
return 0;
}
3.6、练习题6
说明: 写一个递归函数Digitsum(n),输入一个非负整数,返回组成它的数字之和
如:调用Digitsum(1729),返回1+7+2+9 = 19
即:输入:1729,输出:19
#include <stdio.h>
//实现返回组成它的数字之和
int Digitsum(int num)//1729
{
//if (num>0)
//{
// int a = num % 10;
// int b = num / 10;
// return a + Digitsum(b);
//}
//else
//{
// return 0;
//}
//优化代码
if (num>9)
{
return num % 10 + Digitsum(num / 10);
}
else
{
return num;
}
}
int main()
{
int n = 0;
printf("请输入一个整数:");
scanf("%d",&n);
//递归调用
int sum = Digitsum(n);
printf("sum = %d\n",sum);
return 0;
}
3.7、练习题7
说明: 编写一个函数实现n的k次方,使用递归实现
#include <stdio.h>
//实现n的k次方计算
double my_pow(int n, int k)
{
//不考虑负整数的写法
//if ((n > 0) && (k > 0))
//{
// n = n*my_pow(n, k-1);
// return n;
//}
//else if (n == 0)
//{
// return n;
//}
//else if (k == 0)
//{
// return 1;
//}
//else//不考虑负整数的写法
//{
// return -1;
//}
//优化代码
if (k == 0)
{
return 1.0;//理解为任何数的0次方均为1
}
else if (k > 0)
{
return n*my_pow(n, k-1);//理解为n*n,乘一次k少一次
//如:5^3 -- n=5,k=3--5*my_pow(5, 3-1)--5*5*my_pow(5, 2-1)--5*5*5
}
else
{
return 1.0 / my_pow(n, -k);//理解为1/n^k
}
}
int main()
{
int n = 0;
int k = 0;
printf("请输入一个整数和次方数:");
scanf("%d %d",&n,&k);
double ret = my_pow(n, k);
printf("number = %lf\n",ret);
return 0;
}