【C语言程序设计笔记】

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值