递归
1. 概念
递归是一种直接或者间接调用自身函数
第归流程:
- 确定递归公式
- 确定边界条件
2. 递归练习
- 打印1~10
#include <stdio.h>
void PrintN(int n,int count){
if(n>count) return;
printf("%d\n",n);
PrintN(n+1,count); //函数自身调用
}
int main(){
PrintN(1,10);
}
和循环写法的本质是一样的
- 打印数组
#include <stdio.h>
void PrintElement(int* arr,int n,int i){
if(i<n){
printf("%d\n",arr[i]);
PrintElement(arr,n,i+1);
}
}
void PrintArr(int* arr,int n){
PrintElement(arr,n,0);
}
int main(){
int arr[] = {1,2,3,4,5,6,7};
PrintArr(arr,7);
}
和for循环按照下标打印数组是一样的
- 递:打印数组
#include <stdio.h>
void PrintArr(int* arr,int n){
if(0==n) return;
printf("%d/n",arr[0]);
PrintArr(arr+1,n-1);
}
int main(){
int arr[] = {1,2,3,4,5,6,7};
PrintArr(arr,7);
}
- 归:计算数组中数据的和
#include <stdio.h>
void PrintArr(int* arr,int n){
if(0==n) return;
printf("%d\n",arr[0]);
PrintArr(arr+1,n-1);
}
int SumArr(int* arr,int n){
if(0==n) return 0;
return arr[0]+SumArr(arr+1,n-1);
}
int main(){
int arr[] = {1,2,3,4,5,6,7};
PrintArr(arr,7);
printf("%d\n",SumArr(arr,7));
}
- 归:倒叙打印数组
#include <stdio.h>
void PrintArr(int* arr,int n){
if(0==n) return;
PrintArr(arr+1,n-1); //注意执行顺序
printf("%d\n",arr[0]);
}
int main(){
int arr[] = {1,2,3,4,5,6,7};
PrintArr(arr,7);
}
注意函数执行顺序,这是没有返回值的归
3. 链表中的递归
用迭代的方式打印链表
#include <stdlib.h>
typedef struct Node{
int val;
struct Node* next;
}Node;
int main(){
Node* root = NULL; //头结点
for(int i=0;i<5;++i){
Node* node = malloc(sizeof(Node));
node->val = i;
//头插
node->next = root;
root = node;
}
Node* p = root;
while(NULL != p){
printf("%d\n",p->val);
p = p->next;
}
}
用递归的方式打印链表
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int val;
struct Node* next;
}Node;
void Traveral(Node* root){
if(NULL == root) return;
printf("%d\n",root->val);
Traveral(root->next);
}
int main(){
Node* root = NULL; //头结点
for(int i=0;i<5;++i){
Node* node = malloc(sizeof(Node));
node->val = i;
//头插
node->next = root;
root = node;
}
Traveral(root);
}
结果为:
4
3
2
1
0
原本的链表是没有办法从后往前查找数据的,但是使用递归,就能从后向前访问
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int val;
struct Node* next;
}Node;
void Traveral(Node* root){
if(NULL == root) return;
Traveral(root->next); //注意执行顺序
printf("%d\n",root->val);
}
int main(){
Node* root = NULL; //头结点
for(int i=0;i<5;++i){
Node* node = malloc(sizeof(Node));
node->val = i;
//头插
node->next = root;
root = node;
}
Traveral(root);
}
结果为:
0
1
2
3
4
用递归的方式把创建好的链表反向
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int val;
struct Node* next;
}Node;
Node* Reverse(Node* root){
if(NULL == root) return root; //没有结点
if(NULL == root->next) return root; //到了最后一个结点
Node* header = Reverse(root->next);
Node* next = root->next;
next->next = root;
root->next = NULL;
return header;
}
int main(){
Node* root = NULL; //头结点
for(int i=0;i<5;++i){
Node* node = malloc(sizeof(Node));
node->val = i;
//头插
node->next = root;
root = node;
}
root = Reverse(root);
Node* p = root;
while(NULL != p){
printf("%d\n",p->val);
p = p->next;
}
}
反方向连接过程图
用递归的方式尾添加,用递归的方式释放指针
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int val;
struct Node* next;
}Node;
//递归的方式尾添加
Node* Append(Node* root,int val){
if(NULL == root){ //空链表添加
Node* node = malloc(sizeof(Node));
node->val = val;
node->next = NULL;
return node;
}
if(NULL == root->next){ //最后结点添加
Node* node = malloc(sizeof(Node));
node->val = val;
node->next = NULL;
root->next = node;
}else{
Append(root->next,val);
}
return root;
}
/*
//释放结点(递),从当前往后释放
Node* Destory(Node* root){
if(NULL == root) return NULL;
Node* next = root->next;
return Destory(next); //把root再指向现在的next
}
*/
//从后向前释放(归),先找到最后,然后向前释放
Node* Destory(Node* root){
if(NULL == root) return NULL;
Destory(root->next);
free(root);
return NULL;
}
Node* Reverse(Node* root){
if(NULL == root) return root; //没有结点
if(NULL == root->next) return root; //到最后一个结点
Node* header = Reverse(root->next);
Node* next = root->next;
next->next = root;
root->next = NULL;
return header;
}
int main(){
Node* root = NULL; //头结点
for(int i=0;i<5;++i){
Node* node = malloc(sizeof(Node));
node->val = i;
//头插
node->next = root;
root = node;
}
root = Reverse(root);
root = Append(root,100);
Node* p = root;
while(NULL != p){
printf("%d\n",p->val);
p = p->next;
}
root = Destory(root);
}
最终返回的root是最外面那层的root
4. 经典题:斐波那契数列
在第一个月有一对刚出生的小兔子,在第二个月小兔子变成大兔子并开始怀孕,第三个月大兔子会生下一对小兔子,并且以后每个月都会生下一对小兔子。 如果每对兔子都经历这样的出生、成熟、生育的过程,并且兔子永远不死,那么兔子的总数是如何变化的?
解题:
个数找规律:1、1、2、3、5、8、13
f(n) = f(n-1)+f(n-2)
#include <stdio.h>
int Fib(int n){
if(0 == n) return 0;
if(1==n || 2==n) return 1;
return Fib(n-1)+Fib(n-2);
}
int main(){
int n;
scanf("%d",&n);
printf("%d\n",Fib(n));
}
结果为:
6
8
迭代
1. 迭代练习
斐波那契数列题用迭代的方式做
#include <stdio.h>
int Fib(int n){
if(0 == n) return 0;
if(1==n || 2==n) return 1;
int a = 1;
int b = 1;
for(int i=2;i<n;++i){
int m = a+b;
b = a;
a = m;
}
return a;
}
int main(){
int n;
scanf("%d",&n);
printf("%d\n",Fib(n));
}
2. 递归的缺点
- 递归可能比较耗费内存
每次调用都需要占用字节 - 递归可能效率低
有些地方重复计算,运算时间很久