<数据结构与算法分析>读书笔记--递归
一、什么是递归
程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回(引用百度百科)。
由此可概括递归有如下几个特点?
(1)一个函数或者某个额过程直接或者间接的调用自己;
(2)大型问题小型化;
(3)类似于循环;
(4)需要边界条件,当边界条件满足时,返回对应的值,当不满足时,继续前进。
1.一个简单的递归方法
代码示例:
package cn.recursive.example;
public class RecursiveExample {
/**
* 一个递归方法
* @param x
* @return
*/
public static int f(int x) {
if (x == 0) {
return 0;
}
return 2 * f(x - 1) + x * x;
}
public static void main(String[] args) {
//调用该方法,当x=2时,输出为6
System.out.println(RecursiveExample.f(2));
}
}
描述:
以x值为基准,当x=0时,返回0,否则 执行 2 * f(x-1) + x * x
其中 f(x-1)实际就是上述的静态方法,只不过在此做了计算处理。
2.递归容易混淆的概念
比较常见的问题就是:它是否就是循环推理。
我的回答是:比如1中的代码,从某种角度上看,每一次给x赋值后,到最后通过类.方法进行调用输出,在此过程中推理的表现比如对值比较判断,比如当x==0的时候返回0,不为0的时候执行else,而else中2 * f(x-1) + x * x的 f(x-1)循环调用了f(x),只不过该f(x)做了计算处理减1。
书中作者给的回答是:
虽然我们定义一个方法用的是这个方法的本身,但是我们并没有用方法本身定义该方法的一个特定的实例。换句话说,通过f(5)来得到f(5)的值才是循环的。通过f(4)来得到f(5)不是循环的,当然了,除非f(4)的值又要用到对f(5)的计算。
再来一段无终止递归方法代码示例:
package cn.recursive.example;
public class RecursiveExample {
/**
* 无终止递归方法
* @param n
* @return
*/
public static int bad(int n) {
if (n==0) {
return 0;
}else {
return bad(n/3+1) +n-1;
}
}
public static void main(String[] args) {
//无终止递归方法,会报错,报错信息主要是栈异常
System.out.println(RecursiveExample.bad(1));
}
}
运行后,报的错与前面f(-n)是一致的。而只有当n=0时才不会报错。
作者这样分析,以bad(1)为例,bad(1)究竟是多少,这个定义给不出任何答案,因此,计算机将会反复调用bad(1)以期望解出它的值。最后,计算机簿记系统占满内存,程序崩溃。
不管b(n)中的n处于何值,它们的值都不能求出,比如bad(100),bad(100)会一直调用b(99)、b(98)、b(97)等等,最后它们的值还是求不出来。
事实上,除了0以外,这个程序对n的任何非负值都无效。对于递归程序,不存在像“特殊情形“这样的情况。
由此我们根据上面的讨论可以推出递归的前两个基本法则:
(1)基准情形。必须总要有某些基准情形,它们不用递归就能求出正解。
(2)不断推进。对于那些要递归求解的情形,递归调用必须总能够朝着一个基准情形推进。
递归的第三个法则是设计法则
(3)设计法则。假设所有的递归调用都能运行。
这是一条重要的法则,因为它意味着,当设计递归程序时一般没有必要知道簿记管理的细节,你不必试图追踪大量的递归调用。追踪具体的递归调用的序列常常是非常困难的。当然,在许多情况下,这正是使用递归好处的体现,因为计算机能够算出复杂的细节。
递归的主要问题是隐含的簿记开销。虽然这些开销几乎总是合理的(因为递归程序不仅简化了算法设计而且也有助于给出更加简洁的代码),但是递归绝不应该作为简单for循环的替代品。
当编写递归例程时,关键要牢记递归的四条基本法则(将上面的合成到这里来,顺便补充一条):
(1)基准情形。必须总要有某些基准情形,它无需递归就能解出。
(2)不断推进。对于那些需要递归求解的情形,每一次递归调用都必须要使状况朝向一种基准情形推进。
(3)设计法则。假设所有的递归调用都能运行。
(4)合成效益法则。在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性工作。
上述代码示例可在我的Github上找的到,代码地址为:https://github.com/youcong1996/The-Data-structures-and-algorithms/tree/master/Introduction
二、递归的应用场景
(1)以我Linux曾经犯的一个低级错误来说,常常使用rm -rf 删除文件以至于最后不小心删除了boot,其中rm -rf就是递归删除文件。
(2)在代码专利申请的时候,通常我们需要将代码输出到一个txt文件然后将其转成pdf或者word,这时如果代码量几十万行或者三四万行,一个个手动复制将是一件多么可怕的事情,这个时候就可以用递归找到*.java文件并将其输出到对应的txt上。
<数据结构与算法分析>读书笔记--递归的更多相关文章
- <数据结构与算法分析>读书笔记--最大子序列和问题的求解
现在我们将要叙述四个算法来求解早先提出的最大子序列和问题. 第一个算法,它只是穷举式地尝试所有的可能.for循环中的循环变量反映了Java中数组从0开始而不是从1开始这样一个事实.还有,本算法并不计算 ...
- <数据结构与算法分析>读书笔记--运行时间计算
有几种方法估计一个程序的运行时间.前面的表是凭经验得到的(可以参考:<数据结构与算法分析>读书笔记--要分析的问题) 如果认为两个程序花费大致相同的时间,要确定哪个程序更快的最好方法很可能 ...
- <数据结构与算法分析>读书笔记--函数对象
关于函数对象,百度百科对它是这样定义的: 重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象.又称仿函数. 听起来确实很难懂,通过搜索我找到一篇 ...
- <数据结构与算法分析>读书笔记--利用Java5泛型实现泛型构件
一.简单的泛型类和接口 当指定一个泛型类时,类的声明则包括一个或多个类型参数,这些参数被放入在类名后面的一对尖括号内. 示例一: package cn.generic.example; public ...
- <数据结构与算法分析>读书笔记--数学知识复习
数学知识复习是<数据结构与算法分析>的第一章引论的第二小节,之所以放在后面,是因为我对数学确实有些恐惧感.不过再怎么恐惧也是要面对的. 一.指数 基本公式: 二.对数 在计算机科学中除非有 ...
- <数据结构与算法分析>读书笔记--运行时间中的对数及其分析结果的准确性
分析算法最混乱的方面大概集中在对数上面.我们已经看到,某些分治算法将以O(N log N)时间运行.此外,对数最常出现的规律可概括为下列一般法则: 如果一个算法用常数时间(O(1))将问题的大小削减为 ...
- <数据结构与算法分析>读书笔记--要分析的问题
通常,要分析的最重要的资源就是运行时间.有几个因素影响着程序的运行时间.有些因素(如使用编译器和计算机)显然超出了任何理论模型的范畴,因此,虽然它们是重要的,但是我们在这里还是不能考虑它们.剩下的主要 ...
- <数据结构与算法分析>读书笔记--实现泛型构件pre-Java5
面向对象的一个重要目标是对代码重用的支持.支持这个目标的一个重要的机制就是泛型机制:如果除去对象的基本类型外,实现的方法是相同的,那么我们就可以用泛型实现来描述这种基本的功能. 1.使用Object表 ...
- <数据结构与算法分析>读书笔记--模型
为了在正式的构架中分析算法,我们需要一个计算模型.我们的模型基本上是一台标准的计算机,在机器中指令被顺序地执行.该模型有一个标准的简单指令系统,如加法.乘法.比较和赋值等.但不同于实际计算机情况的是, ...
随机推荐
- 记录怎样把安全证书导入到java中的cacerts证书库
这次项目上需要去证书中解析公钥所以这里分享下方法: 首先准备一个证书文件比如叫:test.crt(一般是cer结尾) 下一步准备把证书导入到导入java中的cacerts证书库里 方法如下: 比如本地 ...
- 月经贴:当落魄的.NET基佬遇上不可一世的JAVA派 developer
事先声明,这篇文章很没有营养,大家当笑话看就好,不要搞骂战污染博客园了谢谢. 背景: .NET(以下简称N)心里想:现在企业级应用.NET用的少,但起码.net的语法特性优美,IDE宇宙最强吧,啧啧. ...
- Java - "JUC"之Condition源码解析
Java多线程系列--“JUC锁”06之 Condition条件 概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition ...
- EF框架的code first
需要添加EntityFramework的引用,在NuGet(在工具--库程序包管理--程序包管理控制台) 控制台输入Install-Package EntityFramework,目前版本是6.1.3 ...
- 初学HTML-4
img标签:<img src=" "> src——source缩写,告诉img标签需要显示的图片名称 属性:width:宽度 height:高度.若未指定宽和高,则 ...
- Django-url反向解析和命名空间
一.urls硬编码 在反向解析和命名空间之前我们先来说说URLS硬编码,用django 开发应用的时候,可以完全是在urls.py 中硬编码配置地址,在views.py中HttpResponseRed ...
- Webpack 常用命令总结以及常用打包压缩方法
前言:Webpack是一款基于node的前端打包工具,它可以将很多静态文件打包起来,自动处理依赖关系后,生成一个.js文件,然后让html来引用,不仅可以做到按需加载,而且可以减少HTTP请求,节约带 ...
- cf280C. Game on Tree(期望线性性)
题意 题目链接 Sol 开始想的dp,发现根本不能转移(貌似只能做链) 根据期望的线性性,其中\(ans = \sum_{1 * f(x)}\) \(f(x)\)表示删除\(x\)节点的概率,显然\( ...
- Echarts地图绘制(散点,色卡)
echarts绘制地图时,提供了js内部注册,也提供了json数据手动注册,这两种都可以绘制对应地图,但有一点不同的是,js内部只注册了中国地图和世界地图,而json数据提供了世界,中国,中国城市的数 ...
- PGIS下载离线地图 SQLite+WPF
项目是超高分辨率屏幕墙,实时在线加载PGIS地图速度会比较慢,造成屏幕大量留白.于是使用地图缓存,事先把这个区块的地图全部down下来,使用Sqlite数据库保存.留存. //Task taskDow ...