函数递归
编写一个函数不允许创建临时变量,求字符串长度.
(字符串是一种特殊的字符数组.必须以 \0 结尾)C语言设计中最大的败笔,就是这个字符串 \0 的设定.
字符串长度:不计算 \0
字符串数组长度(占内存的字节数):算 \0
先写一个创建临时变量的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//void printNum(unsigned int num){
// if (num > 9){
// printNum(num / 10);
// }
// printf("%d ", num % 10);
//}
int Strlen(char str[]){
int i = 0;
//for (; str[1] != '\0'; i++);
while (str[i] != '\0'){
i++;
}
return i;
}
int main(){
char str[] = "hehe";
int len = Strlen(str);
printf("%d\n", len);
system("pause");
return 0;
}
如果遇到不让创建临时变量/不让使用循环 => 递归
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//void printNum(unsigned int num){
// if (num > 9){
// printNum(num / 10);
// }
// printf("%d ", num % 10);
//}
//int Strlen(char str[]){
// int i = 0;
// //for (; str[1] != '\0'; i++);
// while (str[i] != '\0'){
// i++;
// }
// return i;
//}
int Strlen(char str[]){
if (str[0] == '\0'){
return 0;
}
return 1 + Strlen(str + 1);
}
int main(){
char str[] = "abcd";
int len = Strlen(str);
printf("%d\n", len);
system("pause");
return 0;
}
......
递归程序的特点:
1.一定都有递归结束条件.
2.每次递归之后,都会距离这个结束条件更近(收敛).
写递归程序往往就是进行问题的不断拆分.
用递归求 n 的阶乘 3! = 3 * 2 * 1
int ji(unsigned int n){
if (n == 1){
return 1;
}
return n * ji(n - 1);
}
int main(){
printf("%d\n", ji(5));
system("pause");
return 0;
}
求第n个斐波那契数
生兔子:初始情况下有一对小兔子
第一个月,还是一对兔子
第二个月,还是一对兔子
第三个月,两对兔子(一对成年,一对幼年)小兔子第三个月成年生下一对小兔子.
第n个月的时候当前有多少对兔子?
1 1 2 3 5 8 13 21 24(任何一项都是前两项之和)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//void printNum(unsigned int num){
// if (num > 9){
// printNum(num / 10);
// }
// printf("%d ", num % 10);
//}
//int Strlen(char str[]){
// int i = 0;
// //for (; str[1] != '\0'; i++);
// while (str[i] != '\0'){
// i++;
// }
// return i;
//}
//int Strlen(char str[]){
// if (str[0] == '\0'){
// return 0;
// }
// return 1 + Strlen(str + 1);
//}
//问题拆分
//n! => n * n-1!
//n == 1 1! => 1
int ji(unsigned int n){
if (n == 1){
return 1;
}
return n * ji(n - 1);
}
int Fib(int n){
if (n == 1){
return 1;
}
if (n == 2){
return 1;
}
return Fib(n - 1) + Fib(n - 2);
}
int main(){
printf("%d\n", Fib(7));
system("pause");
return 0;
}
计算时间非常长,数字太大了.使用非递归版本避免重复运算.
//使用非递归可以通过其他变量保存中间结果避免重复运算
int Fib(int n){
if(n == 1){
return 1;
}
if(n == 2){
return 1;
}
//当前项的前一项
int last1 = 1;
//当前项的前两项
int last2 = 1;
//当前项
int cur = 0;
for (int i = 3; i <= n; i++){
cur = last2 + last1;
last2 = last1;
last1 = cur;
}
return cur;
}
int main(){
printf("%d\n", Fib(3));
system("pause");
return 0;
}
Fib 函数第二个版本为啥速度变快了?
通过两个变量保存了中间结果,避免重复运算.和 递归 非递归 没有直接的关系.
栈空间多大 字节为单位.
VS默认程度栈空间大小大概就是1M,它是可配置的.
我们当前讨论的栈,是操作系统中的栈.和数据结构中的栈不一样.他俩的堆也不一样.
函数调用也是有开销的,只不过这个开销比较小而一般忽略不计.但递归的调用非常频繁而不能忽略不计,所以平时编程中一般选择非递归函数.
数组
1.一维数组的创建和初始化
2.一维数组的使用
3.一维数组在内存中的存储
4.二维数组的创建和初始化
5.二维数组的使用
6.二维数组在内存中的存储
7.数组作为函数参数
8.数组的应用实:三子棋 扫雷
一维数组的创建和初始化:
数组是批量创建一组相同类型(C语言里是这么要求)的变量.
type_t arr_name [const_n];
数组定义的时候,[]中的表达式也不一定非得是常量表达式.
在C89中要求必须是常量.在C99中允许使用变量表达式.
初始化 和 赋值 不一样.
在创建变量的同时设定值 这叫初始化.
变量已经创建完了,再去设定值,这叫赋值.
普通的数组只能使用{}初始化.字符数组处理使用{}还可以使用""的形式初始化,相当于在初始化一个字符串.
int main(){
int arr1[] = { 'a', 'b', 'c' };
int arr2[] = "abc";
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr2));
system("pause");
return 0;
}
arr1 是3;arr2 是4
像arr1这个数组里面没有 \0 就不是一个字符串.不该对他使用strlen.如果强行使用,最终结果不可预期.这叫做未定义行为.