C语言 递归练习1:编写函数不允许创建临时变量,求字符串的长度;练习2:求n的阶乘。练习3:求第n个斐波那契数。练习4练习5……

目录

练习1:编写函数不允许创建临时变量,求字符串的长度。

递归与迭代

练习2:求n的阶乘。(不考虑溢出)

练习3:求第n个斐波那契数。(不考虑溢出)

练习4:编写一个函数reverse_string(char* string),实现:将参数字符串中的字符反向排列,不是逆序打印。要求:不能使用C语言库中的字符串操作函数。

比如:char arr[] = "abcdef;逆序之后数组的内容变成:fedcba

练习5:写一个递归函数DigitSum(n),输入一个非负数,返回组成它的数字之和。

例如:调用DigitSum(1729),则应该返回1+7+2+9,它的和是19。输入:1729,输出:19.


练习1:编写函数不允许创建临时变量,求字符串的长度。

解析:

先看看怎么用库函数strlen()求字符串长度。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main(void)
{
	char arr[] = "bit";
	//对于数组arr来说,我们得知道它里面放了下面四个元素:
	//['b'] ['i'] ['t'] ['\0']  
	//['\0']是字符串结束标志,计算长度的时候不算它
	printf("%d\n", strlen(arr));  //输出3
	return 0;
}

接下来模拟实现一个strlen()函数

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')  //*str 是指通过str指针拿到数组里的第一个字符
	{
		count++;
		str++;
	}
	return count;
}
int main(void)
{
	char arr[] = "bit";
	//对于数组arr来说,我们得知道它里面放了下面四个元素:
	//['b'] ['i'] ['t'] ['\0']  
	//['\0']是字符串结束标志,计算长度的时候不算它
	//模拟实现一个strlen()函数
	printf("%d\n", my_strlen(arr));  
	//arr是数组名,数组在传参的时候,传的是数组首元素的地址,函数定义的时候对应参数处应该写指针
	//因为计算的是长度,所以my_strlen()函数返回类型是int
	return 0;
}

不创建临时变量怎么做?

大事化小思路:

my_strlen("bit");   

//my_strlen帮我求字符串“bit”的长度,my_strlen要拿到字符串第一个字符的地址是很简单的,*str 拿到的就是第一个字符的地址,这时候可以判断这个字符是不是“\0”,如果不是,就可以分解为:

1 + my_strlen("it");

//将取得的 1 剥离下来,继续去求字符串“it" 的长度,同样*str 拿到的就是第一个字符的地址,这时候可以判断这个字符是不是“\0”,如果不是,就可以分解为:

1 + 1 +my_strlen("t");

//同理,分解为:

1 + 1 +1+my_strlen(""); 

//此时,my_strlen函数一看,已经读到“\0”了,已经没有东西了,所以长度是0,所以:

1 +1 +1 + 0 = 3

不创建变量,用递归方法:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int my_strlen(char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1); 
//str+1是下一个字符的地址,此处不能写str++,或++str,会导致传进去和留下来的值不一样
	else
		return 0;
}
int main(void)
{
	char arr[] = "bit";
	//对于数组arr来说,我们得知道它里面放了下面四个元素:
	//['b'] ['i'] ['t'] ['\0']  
	//['\0']是字符串结束标志,计算长度的时候不算它
	//模拟实现一个strlen()函数
	printf("%d\n", my_strlen(arr));  
	//arr是数组名,数组在传参的时候,传的是数组首元素的地址,函数定义的时候对应参数处应该写指针
	//因为计算的是长度,所以my_strlen()函数返回类型是int
	return 0;
}

递归与迭代

练习2:求n的阶乘。(不考虑溢出)

解析:

不用递归的方式:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	int n = 0;
	scanf("%d", &n);
	int i = 0;
	int ret = 1;
	//循环是一种迭代的方式
	for (i = 1; i <= n; i++)
	{
		ret = ret * i;
	}
	printf("%d", ret);
	return 0;
}

用递归的方式:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int fac(int n)
{
	if (n <= 1)
		return 1;
	else
		return n * fac(n - 1);
}
int main(void)
{
	int n = 0;
	scanf("%d", &n);
	int ret = fac(n);
	printf("%d\n", ret);
	return 0;
}

有一些功能:可以使用迭代的方式实现,也可以使用递归。

练习3:求第n个斐波那契数。(不考虑溢出)

解析:

 代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return fib(n-1) + fib(n-2);
}
int main(void)
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n", ret);
	return 0;
}

如果此时求第50个斐波那契数,程序会运行很久,因为计算机要进行重复大量的计算,效率太低。要求第50个,得先求出49和48的值,求49的值又要求48和47的值,求48的值又要先求47和46的值,47的值就算了两次……一直要求到1,中间有大量重复计算。

怎么优化呢?哈哈哈,那就不要用递归解决了,递归可以求解,但是效率太低。

用迭代的方式,效率非常高:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;  //n不减的话就死循环了
	}
	return c;
}
int main(void)
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n", ret);
	return 0;
}

总结:

  1. 许多问题是以递归的形式进行解释的,这只是 因为它比非递归的形式更为清晰。
  2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
  3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的间接性便可以补偿它所带来的运行时开销。

练习4:编写一个函数reverse_string(char* string),实现:将参数字符串中的字符反向排列,不是逆序打印。要求:不能使用C语言库中的字符串操作函数。

比如:char arr[] = "abcdef;逆序之后数组的内容变成:fedcba

用循环方法实现:

#define _CRT_SECURE_NO_WARNINGS 1
#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 = my_strlen(str) - 1;
	while (left < right)
	{
		char tmp = str[left];  //str[left]也可以写成*(str+left)
		str[left] = str[right];
		str[right] = tmp;
		left++;
		right--;
	}
}
int main(void)
{
	char arr[] = "abcdef";
	reverse_string(arr);  //数组名arr是数组arr首元素的地址
	printf("%s\n", arr); //fedcba
	return 0;
}

用递归方法:

大事化小思路:

abcdef 的逆序 = a 和 f 的交换 + bcde的逆序

bcde 的逆序 = b 和 e 的交换 + cd的逆序

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
void reverse_string(char* str)
{
	char tmp = *str;  //第1步
	int len = my_strlen(str);
	*str = *(str + len - 1);   //第2步
	*(str + len - 1) = '\0';   //第3步
	//判断条件
	if (my_strlen(str + 1) >= 2)  //大于等于两个字符的时候才要交换
	{
		reverse_string(str + 1);   //第4步
	}
	*(str + len - 1) = tmp;    //第5步
}
int main(void)
{
	char arr[] = "abcdef";
	reverse_string(arr);  //数组名arr是数组arr首元素的地址
	printf("%s\n", arr); //fedcba
	return 0;
}

练习5:写一个递归函数DigitSum(n),输入一个非负数,返回组成它的数字之和。

例如:调用DigitSum(1729),则应该返回1+7+2+9,它的和是19。输入:1729,输出:19.

解析:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int DigitSum(unsigned int n)
{
	if (n > 9)
	{
		return DigitSum(n / 10) + n % 10;
	}
	else
	{
		return n;
	}
}
int main(void)
{
	unsigned int num;
	scanf("%u", &num);
	int sum = DigitSum(num);
	printf("%d\n", sum);
	return 0;
}

练习6:编写一个函数实现n的k次方,使用递归实现。

解析:

 代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
double Pow(int n, int k)
{
	if (k == 0)
	{
		return 1.0;
	}
	else if (k > 0)
	{
		return n * Pow(n, k - 1);
	}
	else
		return 1.0 / (Pow(n, -k));  
//要考虑到有小数,所以这个函数返回的是double类型
}
int main(void)
{
	int n = 0;
	int k = 0;
	scanf("%d  %d", &n, &k);
	double ret = Pow(n, k);
	printf("%lf\n", ret);
	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值