目录
练习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;
}
总结:
- 许多问题是以递归的形式进行解释的,这只是 因为它比非递归的形式更为清晰。
- 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
- 当一个问题相当复杂,难以用迭代实现时,此时递归实现的间接性便可以补偿它所带来的运行时开销。
练习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;
}