递归

递归在算法中具有很重要的地位,也是很多学习编程的初学者非常头疼的问题,看我的这篇文章,希望能为还处于迷雾中的你带来希望

首先我们要知道递归的作用:

1.可替代多重循环

2.解决本来就是用递归形式定义的问题

3.将问题分解为规模更小的子问题进行求解

其实对于我来说,递归非常重要的原因在于可以替代多重循环,当循环过大时,会给计算机相当大的负荷,一时半会很难出结果,这时候我们就需要运用到递归这个手段。这里我会把知识由浅入深,以举例的方式让各位登堂入室,话不多说,直接上干货。

1.求阶乘

递归的基本概念就是一个函数调用其自身
我们都知道如何求阶乘,这是我们初中数学就学了的内容,那么如何用编程的思维解决这个问题呢
对于递归,我们首先要明确问题是什么,把抽象事物具体化

接下来我们需要厘清递归中的表达式或者关系
然后找到递归结束条件,也就是递归出口——不递归的条件。如果没有递归出口,那么函数只会像一个无底洞,结果大失所望
这是对应的C代码

#include<stdio.h>int Factorial(int n)
{if(n == 0)return 1;elsereturn n * Factorial(n - 1);
}int main()
{printf("%d\n",Factorial(5));return 0;
}

其实递归很耗费内存资源的,因为每次调用一下自身就会需要创建一个新的栈来存放。
为了加深印象,我把程序改一下

#include<stdio.h>int f(int n)
{if(n > 1)f(n - 1);printf("n  =  %d\n",n);
}int main()
{f(5);printf("%d\n",f(5));        //printf的值是递归函数最后一行的双引号内的字符串的长度 return 0;
}

具体函数调用分析如下图

每次一个递归调用完后,所在的栈空间也会被立即释放,得到的结果会返回上一个栈,直到函数结束
既然基本套路熟悉了,我想带大家聊聊递归中很经典的问题,不用猜也知道,汉诺塔问题

2.Hanoi问题


这个问题为什么说是经典呢,因为它是个简单又很精髓的递归问题
通过该问题,你可以知道递归还有一个重要特点,就是把细节模块化,以整体的思想看待问题。

首先分情况讨论,
1.原目标塔上只有一个盘子,那么我们直接将盘子从A移到C即可
2.原目标塔上有不止一个盘子,又由于大盘在下小盘在下的规定,我们可以先将A座的盘子都移到B座以C为中间“变量”,当A座只剩最后一个盘子,也就是全场最大的盘子,将其移到C座,然后以A为中间“变量”,将B座盘子移到C座

具体C代码如下

#include<stdio.h>void Hanoi(int n, char src, char mid, char dest)
{if(n == 1){printf("%c -> %c \n", src, dest);}else{Hanoi(n - 1, src, dest, mid);
//      printf("%c -> %c \n", src, dest);Hanoi(1, src, mid, dest);Hanoi(n - 1, mid, src, dest);}}int main()
{int n;printf("Please enter number : ");scanf("%d",&n);Hanoi(n, 'A', 'B', 'C');return 0;
}

对于递归,记住不要纠结具体细节,把握整体框架最为重要!

这里还有一个小练习,关于求最大公约数的问题,比方说求12和8的最大公约数,你可以先想想,然后再看我的代码答案,记得,要用递归做哦!

#include<stdio.h>int gcd(int a, int b)
{if(b == 0)return a;elsereturn gcd(b, a % b);
}int main()
{printf("%d\n",gcd(12,8));
}

是不是很简单,递归有的时候就有点像函数调用,它只是比较特殊,它只是总是在调用自己罢了。如果除数等于零,那么被除数就是最大公约数,如果除数不等于零,那么就把b的值给a,b的值变为a % b。递归还有一个特点,就是以十分简易的代码实现了值的互换或者修改

3.N皇后问题

N皇后问题是八皇后问题的延伸,要求在NxN格的国际象棋上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

#include<stdio.h>
#include<math.h>int queenPos[100];
int N;void NQueen(int k)        //在0~k-1行皇后摆好的情况下摆第k行的皇后
{int i;if(k == N){for(i = 0; i < N; i++)printf("%d ",queenPos[i] + 1);printf("\n");}else{for(i = 0; i < N; i++)      //逐步尝试第k个皇后的列位置 {int j;for(j = 0; j < k; j++){if(queenPos[j] == i || abs(k - j) == abs(queenPos[j] - i))break;}if(j == k){queenPos[k] = i;NQueen(k + 1);}}}
} int main()
{scanf("%d",&N);NQueen(0);return 0;
}

这里我们不妨设最多有100个皇后,定义数组queenPos[100],然后再设全局变量N用于记录有多少皇后,NQueen(int k)是用于记录在0~k-1行皇后摆好的情况下摆第k行的皇后。如果全都摆好了,即k == N,那么把可能摆放位置的其中一种输出,如果栈都退完了那么就结束了,否则销毁当前这个栈再往回退栈。如果k != N,那么先循环遍历N个可能的位置给第k行的皇后,然后再把暂时确定的第k行皇后的位置与前k-1行所有皇后比较,如果有产生冲突的则再改变第k行皇后的位置。如果j==k,那么就确定第k行皇后的位置,同时进入下一层NQueen(k+1)。
这就是这个递归的思路
四皇后是N皇后最小的底线,大家可以试一试,答案是

4.逆波兰表达式

逆波兰表达式的定义:
1)一个数是一个逆波兰表达式,值为该数;
2)“运算符 逆波兰表达式 逆波兰表达式”是逆波兰表达式,值为两个逆波兰表达式的值运算结果。

这里我不妨设整个表达式所占的大小不超过20个字符,代码如下:

#include<stdio.h>
#include<stdlib.h>double exp()
{char s[20];scanf("%s",s);switch(s[0]){case '+' :return exp() + exp();case '-' :return exp() - exp();case '*' : return exp() * exp();case '/' : return exp() / exp();default : return atof(s);break;}
}int main()
{printf("%lf",exp());return 0;
}

atof()函数是stdlib.h中的函数,将字符串内容转化成double类型。
当输入* + 1 2 - 8 6
结果为

5.爬楼梯

LiHua爬楼梯,每次可以走1级或者两级,要求输入楼梯级数,求不同的走法数

这里我们可以吧问题拆分细小化

n级台阶的走法 = n - 1 级台阶走法 + n - 2 级台阶走法

例如共有5级台阶,可拆分成第一步走一级和第一步走两级的走法,然后再从第一步走一级再细分之后的台阶走法。

C代码如下:

#include<stdio.h>int N;int stairs(int n)
{if(n < 0)return 0;if(n == 0)return 1;if(n == 1)return 1;if(n == 2)return 2;return stairs(n - 1) + stairs(n - 2);
}int main()
{scanf("%d", &N);printf("%d\n", stairs(N));return 0;
}

这里还有一个类似爬楼梯的题目,可以参照这篇博客2020 计蒜客蓝桥杯省赛 B 组模拟赛(一)题解2.爬楼梯

6.放苹果


关于递归最重要的无非是递归表达式和递归终止条件。
首先分析,需要输入苹果数和盘子数。关于两者的数目就存在两种情况:
1、苹果数n大于盘子数m,n > m时,f(n,m) = f(m,m);
2、苹果数n小于等于盘子数m,n <= m时,f(n,m) = f(n, m - 1) + f(n - m,m),即有盘子为空的放法 + 无盘子为空放法。

详细代码如下:

#include<stdio.h>int f(int n, int m)
{if(n < m)return f(n, n);if(n == 0)return 0;if(m == 0)return 1;elsereturn f(n, m - 1) + f(n - m, m);
}int main()
{int t, n, m, i;scanf("%d", &t);for(i = 0; i < t; i++){scanf("%d %d", &n, &m);printf("%d\n", f(n, m));}return 0;
}

三个苹果三个盘子就是4

经过这么多练习可以发现,递归很多时候都用于直接就出结果,不在意输出过程的题目。

7.全排列

全排列是递归中非常重要的知识点,在深度学习中的用处很广。比方说我对1、2、3、4、5进行全排列,那么如下图所示

首先让第一个位置有n种摆法,然后就只用考虑p+1到q的全排列了,把一个大的全排列分解为一层层嵌套的子问题这便是递归思想,注意最后还要把交换过的元素换回来,要不然会使一些情况重复。
详细代码如下

#include<stdio.h>void swap(int A[], int i, int j)
{int temp = A[i];A[i] = A[j];A[j] = temp;
} void printArray(int A[], int n)
{int i;for(i = 0; i < n; i++){printf("%d ", A[i]);}printf("\n");
}void perm(int A[], int p, int q)
{if(p == q)printArray(A, q + 1);else{int i;for(i = p; i <= q; i++){swap(A, p, i);perm(A, p + 1, q);swap(A, p, i);}}
}int main()
{int A[5] = {1, 2, 3, 4, 5};perm(A, 0, 4);
}

这里还有一个类似的全排列题型,可以参看博文蓝桥杯2015第六届C语言B组省赛习题题解——习题E.九数组分数

这里我列出九数组分数的代码,你们可以观察一下,思路其实都是一样的,先swap然后递归再swap换回来。

#include <stdio.h>void test(int x[])
{int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];if(a*3==b) printf("%d / %d\n", a, b);
}void f(int x[], int k)
{int i,t;if(k>=9){test(x);return;}for(i=k; i<9; i++){{t=x[k]; x[k]=x[i]; x[i]=t;}f(x,k+1);t=x[k]; x[k]=x[i]; x[i]=t; // 填空处}
}int main()
{int x[] = {1,2,3,4,5,6,7,8,9};f(x,0); return 0;
}

如果喜欢我的文章,请记得三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持,下期更精彩!!!

算法一:递归(包含Hanoi问题、N皇后问题、逆波兰表达式、爬楼梯、放苹果、全排列)相关推荐

  1. 成长轨迹59 【ACM算法之路 百炼poj.grids.cn】【递归】【2694:逆波兰表达式】

    http://poj.grids.cn/practice/2694/ [ce代码] 1 #include <stdio.h> 2 #include <math.h> 3 4 d ...

  2. 1198:逆波兰表达式(递归,前缀表达式,波兰表达式)

    前言:题干描述有问题 题干明明是前缀表达式呀,前缀表达式就是波兰表达式,怎么能叫逆波兰表达式呢?所以题目描述有问题! 逆波兰表达式,也叫后缀表达式 : 举例:a+b  --> a b +. 波兰 ...

  3. 1198:逆波兰表达式(递归)

    [题目描述] 逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3.逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + ...

  4. java递归分苹果_递归应用示例(放苹果)[较难 选听]

    ok,下面我们再看一个例子. 这个例子呢比较有意思. 这个例子名字叫放苹果.这是open jar 的上面比较经典的一道题目. 我们来看一下这个题目.说啊,有M个同样的苹果. 这M个苹果呢一模一样没有区 ...

  5. Java算法一:汉诺塔

    Java算法一:汉诺塔(河内塔Towers of Hanoi) 问题说明: 汉诺塔:又称河內塔(Towers of Hanoi)是法国人M.Claus(Lucas)于1883年从泰国带至法国的,河內为 ...

  6. Solr相似度算法一:Lucene TF-IDF 相关性算分公式

     Solr相似度算法一:Lucene TF-IDF 相关性算分公式 Lucene在进行关键词查询的时候,默认用TF-IDF算法来计算关键词和文档的相关性,用这个数据排序 TF:词频,IDF:逆向文 ...

  7. 深度学习AI美颜系列---AI美颜磨皮算法一

    深度学习AI美颜系列---AI美颜磨皮算法一 转自:https://blog.csdn.net/trent1985/article/details/80661230 首先说明一点,为什么本结内容是&q ...

  8. 数据结构与算法一:时间频度和时间复杂度

    数据结构系列博客涉及内容: 一.初识数据结构和算法: 1.数据结构: 数据结构:是相互之间存在一种或多种关系的数据元素的集合 研究什么:数据的逻辑结构与物理结构以及它们之间的相互关系 数据结构包括:线 ...

  9. 大数据处理算法一:BitMap算法

     腾讯面试题:给20亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中并且所耗内存尽可能的少?  解析:bitmap算法就好办多了 所谓b ...

最新文章

  1. CCS代码编辑的几个常用设置
  2. Semver(语义化版本号)扫盲
  3. 做科研,到底应该看什么?
  4. python 读取csv文件转成字符串_从CSV读取到 dataframe pandas python时dict对象转换为字符串...
  5. PAT乙级1037 在霍格沃茨找零钱
  6. 【深度学习再突破】让计算机一眼认出“猫”:哈佛提出新高维数据分析法
  7. vb.net 局域网传文件_没网盘咋传输文件?全平台高效传输方案一网打尽!
  8. 蓝桥杯 ADV-148算法提高 排队打水问题(贪心)
  9. 8位十六进制转换32位十六进制_网络中的数制系统--二进制十六进制与十进制之间的相互转换...
  10. Centos7 设置静态IP后重启网络服务出错
  11. 连接mysql的各种方式
  12. 我爱生煎包!--上海生煎
  13. 服务器系统win12可以安装CAD,技术员教你解决win10系统安装CAD2012的教程介绍
  14. java bitset_Java1.8-BitSet源码分析
  15. wxpy 扫码登录微信报错 KeyError: pass_ticket(网页版微信登录失败)
  16. 聊聊龙芯Java开源
  17. Clouda开发随笔之block标签
  18. debian9.6安装virtualbox
  19. 【Web】Excle题库抽题组卷在jsp显示
  20. 内核tty框架_串口_tty_shell的关系

热门文章

  1. 计算机病毒 笑话,轻松一刻:因为男生的电脑容易中病毒
  2. ensp 移动主机搜索不到AP信道_案例WiFi信道上的一个坑
  3. 2. linux安装(1)
  4. 常用指令linux总结
  5. 今天好无聊……悟空……你变成女妖精吧……
  6. PostgreSQL 聚合函数讲解 - 3 总体|样本 方差, 标准方差
  7. matlab绘制二元二次曲线图,Excel:关于二次曲线直角坐标方程图像的描绘
  8. UNITY 模拟手机滑屏功能
  9. Python: numpy tile()函数 可实现ndarray的横向纵向复制
  10. 高德地图(第二篇)测量距离小工具