递归算法和过程的详解
看了很多博主的博文,加上看了几个视频,做了一些题目,感觉对递归有了一些认识,希望大家观看以后会对自己有所帮助。
递归的算法详解
- 递归的定义
如果一个对象部分地由它自身组成或按它自己定义,则称它是递归的,所以说递归就是函数/过程/子过程在运行过程中直接或间接调用自身而产生的重入现象。
- 递归算法包含的两个部分
1 由其自身定义的与原始问题类似的更小规模的子问题(只有数据规模不同),它使递归过程持续进行,称为一般条件。
2 所描述问题的最简单的情况,它是一个能控制递归过程结束的条件,称为基本条件。(递归出口)
- 递归的基本思想
就是把一个规模大的问题分为若干个规模较小的子问题求解,而每一个子问题又可以分为几个规模更小的子问题。
基本上,所有的递归问题都可以用递推公式来表示。
最重要的一点就是假设子问题已经解决了,现在要基于已经解决的子问题来解决当前问题;
或者说,必须先解决子问题,再基于子问题来解决当前问题
或者可以这么理解:递归解决的是有依赖顺序关系的多个问题。
- 递归的优缺点
优点
逻辑清楚,结构清晰,可读性好,代码简洁,效率高(拓展:DFS深度优先搜素,前中后序二叉树遍历)
缺点
函数调用开销大,空间复杂度高,有堆栈溢出的风险。
- 什么样的问题可以用递归来求解?
1.问题的解可以分解为几个子问题的解。(子问题:数据规模更小的问题)
2.问题与子问题,除了数据规模不同,求解思路完全一样
3.存在递归终止条件
递归的过程详解
- 递归的基本过程
首先,我们要了解递归具有堆栈的结构特性:先进后出
当我们在解决问题的过程中发现,这个问题的解,是依赖于一个规模更小的子问题的解,然后我们转向去求一个数据规模小,过程相同的子问题时,发现它的解依赖于规模更小的子问题,之后我们一步一步的求,突然发现最后的子问题的解可以直接求得(这便是递去的过程),然后用这一个子问题的解,来解决与其有关联的上一级子问题的解,最终求得目标问题的解(这便是归来的过程)。
转化为代码中的思想:
函数执行到递归函数入口时,就扩充一段完全一样的代码,执行完扩充的代码并return后,继续执行前一次递归函数中递归函数入口后面的代码。
- 递归中的变量的解释
递去时:每进行一次新的调用,都将创建一批变量,他们将掩盖递归函数前一次调用所创建的变量。可以看出每一级的函数调用都有自己的局部变量,当追踪一个递归函数的执行过程时,必须把不同次调用的变量区分开来,以避免混淆。
如果问题复杂时就有可能有堆栈溢出的风险。
归来时:开始打印输出。然后函数返回,并开始销毁堆栈上的变量值。
- 关于递归的理解
阅读递归函数最容易的方法不是纠缠于它的执行过程,而是相信递归函数会顺利完成它的任务。如果你的每个步骤正确无误,你的限制条件设置正确,并且每次调用之后更接近限制条件,递归函数总是能正确的完成任务。
返回的过程是隐秘的,但是确实存在的过程。
关于递归的基本知识就这些,下面我们来结合一些代码更加深入的了解递归。
- 汉诺塔问题(经典)
Description
汉诺塔(又称河内塔)问题是印度的一个古老的传说。
开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒A、B和C,A上面套着n个圆的金片,最大的一个在底下,其余一个比一个小,依次叠上去,庙里的众僧不倦地把它们一个个地从A棒搬到C棒上,规定可利用中间的一根B棒作为帮助,但每次只能搬一个,而且大的不能放在小的上面。
僧侣们搬得汗流满面,可惜当n很大时这辈子恐怕就很搬完了。
聪明的你还有计算机帮你完成,你能写一个程序帮助僧侣们完成这辈子的夙愿吗?
Input
输入金片的个数n。这里的n<=10。
Output
输出搬动金片的全过程。格式见样例。
Sample
Input
2
Output
Move disk 1 from A to B
Move disk 2 from A to C
Move disk 1 from B to C
Hint
可以用递归算法实现。
第一步:将问题简化–复杂问题求解的基本方法
现在假设A杆上,只有2个圆盘,即汉诺塔有两层,n=2;求解过程如下:
第二步:对于n(n>1)个圆盘的汉诺塔,将n个圆盘分为两部分,“上面n-1个圆盘”看成一个整体(也就是上图中画斜线的部分)
所以就可以写出代码:
#include <iostream>using namespace std;void carry(int n,char a/*起始柱*/,char b/*中间柱*/,char c/*目标柱*/){if(n==1) printf("Move disk %d from %c to %c\n",n,a,c); //结束条件:子问题中将起始柱上的第一个盘子移动到目标柱上 注意当前的a和c不一定代表A柱子和C柱子 else{carry(n-1,a,c,b); //将起始柱上的n-1个盘子移动到中间柱子上 1printf("Move disk %d from %c to %c\n",n,a,c);carry(n-1,b,a,c); //将中间柱子上的n-1个盘子移动到目标柱子上 }
}
int main()
{int n;cin>>n;carry(n,'A','B','C');return 0;}
- 快速排序
- 题目链接
所谓快速排序,效率很快,基本思想,两边交替向中间遍历,把大的放一边,小的放一边,再利用递归最终实现排序。
#include <iostream>using namespace std;
const int N=1e5+10;
int a[N];void q_sort(int l,int r){int i=l;int j=r;int k=a[i];if(l>=r) return ; //如果左边界大于右边界返回空 else{while(i<j){while(i<j&&a[j]>=k)j--; //发现一个大于k的值就让该值放在之前k的位置 a[i]=a[j];while(i<j&&a[i]<=k)i++;a[j]=a[i];}a[i]=k; //把之前提取出来的一个值放到中间 q_sort(l,i-1); //重复这个过程 q_sort(i+1,r);}
}void print(int n)
{for(int i=0;i<n;i++){if(i==n-1) cout<<a[i]<<endl;else cout<<a[i]<<" ";}
}int main()
{int n;cin>>n;for(int i=0;i<n;i++){cin>>a[i];}q_sort(0,n-1);print(n);return 0;
}
- 二分查找
- 题目链接’
二分查找和快排有一些相同的地方,二分查找把要查找的数与数列中中间值进行比较,从而每一次缩小一半。
注意二分查找的序列一定时有序的。
#include <iostream>using namespace std;
const int N=1e7+10;
int a[N];int search(int l,int r,int x){int mid;mid=(l+r)>>1;while(l<=r){if(a[mid]==x) return mid;else if(a[mid]>x) return search(l,mid-1,x);else if(a[mid]<x) return search(mid+1,r,x);}return -1;
}int main()
{int n;cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}int q;int x;cin>>q;while(q--){cin>>x;cout<<search(1,n,x)<<endl;}return 0;}
- 全排列问题
- 题目链接
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
const int N = 1e5 + 10;
const int M = 111;
using namespace std;int a[11];///右移元素
void move_r(int l,int r){if(l>=r) return ;int t=a[r];for(int i=r;i>l;i--) a[i]=a[i-1];a[l]=t;
}///左移元素
void move_l(int l,int r){if(l>=r) return ;int t=a[l];for(int i=l;i<r;i++) a[i]=a[i+1];a[r]=t;
}///递归全排列
void perm(int l,int r){if(l==r){for(int i=1;i<=r;i++){if(i==r) cout<<a[i]<<endl;else cout<<a[i]<<",";}}else{for(int i=l;i<=r;i++){swap(a[l],a[i]);move_r(l+1,i);perm(l+1,r);move_l(l+1,i);swap(a[l],a[i]);}}
}int main()
{int n;cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}perm(1,n);return 0;
}
提升:八皇后问题
- 结语
我意识到用书面语讲这些题非常的麻烦,而且大家可能也听不懂,所以我就详细的介绍了基本的知识,至于题目,我只是稍微讲了一点思路,加上我给出的代码,大家自己思考出来的效果肯定会更好。
递归算法和过程的详解相关推荐
- 【Big Data - Hadoop - MapReduce】通过腾讯shuffle部署对shuffle过程进行详解
摘要: 通过腾讯shuffle部署对shuffle过程进行详解 摘要:腾讯分布式数据仓库基于开源软件Hadoop和Hive进行构建,TDW计算引擎包括两部分:MapReduce和Spark,两者内部都 ...
- goland 方法注释_goland 设置注释模板的过程图文详解
网上找了一些办法 都没有说到关键点: 梳理一下设置步骤如下,以及避免的坑. 新建文件自动加注释: Preferences | Editor | File and Code Templates 打开go ...
- mysql 5.5.18下载_MySQL5.7.18下载和安装过程图文详解
MySql下载 1.打开官网找到下载路口,这里直接给出下载的地址 2.选择64位版本 3.直接下载 MySql5.7.18.1安装过程 1 .运行安装软件,接受协议 2.选择默认安装 3.下一步到 ...
- Nero刻录ISO文件的过程步骤详解
常见的镜像文件格式有ISO.BIN.IMG.TAO.DAO.CIF.FCD. Nero刻录ISO文件的过程步骤详解(附Nero下载地址无需注册) 刻录ISO文件的软件其实很多--以Nero(德国公司出 ...
- 红外接收过程程序详解
先理解以下知识点: 1.参看程序前,先参阅博文红外NEC协议,否则程序不好理解 2.NEC协议重点查看,代码调制过程,以及逻辑"0",逻辑"1"的高低电平时间特 ...
- 【C/C++内功心法】剖析预处理过程,详解其他预处理指令,提升C/C++内功
文章目录 前言 一.#undef 二.命令行定义 三.条件编译 四.文件包含 1.头文件被包含的方式 2.嵌套文件包含 总结 前言 大家好啊,我是不一样的烟火a,今天我将会为大家详细讲解其他的预处理指 ...
- linux子程序标识符,linux过程标识符详解1
linux进程标识符详解1 每个进程都有一个实际用户标识符和一个实际组标识符,它们永远是启动该进程之用户的用户标识符和组标识符. 进程的有效用户标识符和有效组标识符也许更重要些,它们被用来确定一个用户 ...
- 递归算法:兔子繁殖问题详解
面试题:有一对兔子,从出生后第3个月起每月都生一对小兔子,小兔子长到第3个月后每个月又生一对小兔子,按此规律,假设兔子没有死亡,第一个月有一对刚出生的小兔子,问第20个月有多少只兔子? 解题思路:第一 ...
- android sdk安装过程,图文详解Android 3.0 SDK安装教程
[51CTO编者按]我们已经为喜欢Android 3.0的朋友提供Android 3.0 SDK下载,本文我们将向大家介绍Android 3.0 SDK安装教程,本教程与 玩模拟器需要比较好的电脑配置 ...
最新文章
- 走进JavaWeb技术世界7:Tomcat中的设计模式
- 如何ping端口_复刻smartbits的国产网络性能测试工具minismb-如何配置Ping报文
- 十周后,62%的PHP网站将运行在一个不受支持的PHP版本上
- WinForm 自定义控件属性
- mysql版本 time类型
- 黑龙江对口学计算机上机,2009年黑龙江省职高对口升学计算机应用专业技能试卷十.doc...
- Hibernate所用15个jar包
- Linux磁盘分区及文件系统格式化和挂载
- Ubuntu本地部署Nebula图数据库
- 直角三角形 纪中 1385 数学_斜率
- lap.lapjv函数cost_limit参数
- Axon框架使用指南(二):入门
- JavaScript——JSON
- linux下贪吃蛇代码,贪吃蛇 linux 程序
- UL测试报告办理流程,UL1017测试范围有那些?
- java动态生成pdf文件的方法
- Linux系统设置用户密码规则(复杂密码策略)方法
- 一文了解什么是FTP协议
- Linux下最简单的动态域名用法
- PhotoScan拼接无人机航拍RGB照片