先说下我的学习方式吧

首先,我想说下我的学习方式,每个人的学习方式都是不同的,有的人悟性比较高,所以学东西比较快,但也有一部分人,学东西比较慢,哈哈,我就是那学东西比较慢的那一部分人。但我也不气馁,认真踏实的学就可以了,千万不能浮躁。
所以在我学习算法的过程中,首先要搞懂这个技术是来解决什么问题的,这是很重要的一步,其次如何使用,最后它的原理是什么。走好这三步我觉得这个技术你也就学会了。

递归

首先第一步,递归这个算法是用来解决什么问题的或者主要用于什么方向的。在我目前的理解,递归主要是将一个大问题分解成小问题,最后各个小问题的解再进行合并。这就是递归的主要作用。

注意两个技巧来编写递归函数:

  • 1.思考这个问题能否分解成小问题
  • 2.递归函数的编写在我看来最重要的是定义递归函数的功能,不要太纠结递归函数是怎么运行的,这么思考会陷入死胡同的,我之前也是纠结这块,弄得头疼。

做几道题目吧(leetcode 343,leetcode 337)

leetcode 343:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

实例:输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

首先如何思考题目:说至少拆分成两个数,那么拆分出来的两个数是否又可以进行拆分?
就好比10拆分成10=4+6,那么6是否可以拆分成4+2?是不是有一点激动,是不是很像递归,将一个大问题拆分成一个个小问题。

那么知道了方法,那思路是怎么的呢?我们可以将一个数从1进行拆分,一直到n-1。打个比方吧,比如10,我们一个个拆分成【1,9】,【2,8】,【3,7】…【9,1】,那么在拆分的过程中,比如拆分成【1,9】,9是否又可以进行拆分?是重复的一个过程,但就是这个过程我们定义成了递归函数,所以递归函数的功能就是给你一个数返回它的乘积最大化结果。
那我们现在就来定义递归函数的功能:

//递归函数的功能定义非常重要:
public int help(int num){   //返回一个传递的正整数num的乘积最大化得结果if(num==2){    //这是临界条件,大家可以动下脑筋就能思考出来了,当时2的时候,只能拆分成1+1return 1;}int res=0;for(int i=1;i<num;i++){//将num拆分成i和num-iint a=i*help(num-i);//num-i是不是又可以拆分成多个数字之和,//那我们在将num-i传递到递归函数中即可,不要忘记递归函数的功能是返回一个//正整数的乘积最大化的结果,所以我们只需要从i遍历到num-1,求出最大值即可int b=i*(num-i); //这边代码是什么意思呢?比如10分成【1,9】,9的确可以再分,但我不分9了,//直接两个数相乘求乘积也可以吧,这题的总体思想就是递归,递归中也包含分与不分两个方面res=Math.max(res,Math.max(a,b)); //比较a和b两个最大值,再和res比较得出res结果}return res;
}

上面那道题就完成了,但是写成那样,时间复杂度比较高,应该不可能通过所有测试用例,所以我们要利用HashMap<Integer,Integer> 来记录一个正整数的最大化乘积的值,避免重复计算。这种方式就是备忘录方法。

HashMap<Integer,Integer> map=new HashMap<>();
public int help(int num){   if(num==2){    return 1;}//主要添加了下面三行代码,你们想要,我求出了10的最大化,//是不是要进行保存起来,因为10的最大化是一个固定值,不是变化的,所以当再用到10这个正整数的时候//就无须再进行计算了,直接返回他的结果就可以了if(map.containsKey(num)){return map.get(num);}int res=0;for(int i=1;i<num;i++){//将num拆分成i和num-iint a=i*help(num-i);int b=i*(num-i); res=Math.max(res,Math.max(a,b)); }//将num这个正整数的最大化乘积res进行保存map.put(num,res);return res;
}

到目前为止,这道题才算是解决了。

接着再来一题,

leetcode 337:在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。


这一题如何思考呢?在我认为递归最要紧的就是定义函数功能,这一题的递归函数功能是什么呢?那就是给你一个二叉树中的任意一个节点,将它当做根节点,返回此节点的最大盗取金额。比如上一张图片中的4,如果传递的节点是4,那我们要返回的就是以4为根节点,小偷在以4为根节点的树上,能盗取的最大金额是多少,当然为了便于理解,3,5,1节点可以舍弃不看。有了这样的思路,我们来编写程序。

public int rob(TreeNode root){ //定义函数功能:以某个节点为根,返回小偷能盗取的最大值if(root==null){  //当节点为空,返回0,初始条件return 0;}//因为小偷不能连续盗窃,所以如果偷根节点,那么根节点的左右子树就不能偷所以分成两类//一类:根节点+根节点的孙子节点  另一类:根节点的儿子节点int left=0;   //计算以根节点的左孙子节点为根节点,小偷能盗取的最大值int right=0;  //计算以根节点的右孙子节点为根节点,小偷能盗取的最大值//计算根节点和根节点左右子树的子树if(root.left!=null){left=rob(root.left.left)+rob(root.left.right);}if(root.right!=null){right=rob(root.right.right)+rob(root.right.left);}int sum1=root.val+left+right;//维护的就是根节点加根节点的左右孙子节点int sum2=rob(root.left)+rob(root.right);//维护的就是根节点的左右儿子节点能获取的最大值return sum1>sum2?sum1:sum2; //返回最大值}

再次强调,必须要定义好递归函数的功能,**rob(root.left.left)**返回的就是以根节点的左孩子的左孩子为根节点小偷能盗取的最大值,定义递归函数功能尤其重要。
画张图就能更好地理解了:

不过上面的递归时间复杂度是有点高的,大家可以想想,以某个节点为根节点,那么能盗取的最大金额是固定的,所以我们可以维护一个HashMap<TreeNode,Integer>来做备忘录。代码就不写了,很简单,和第一题的备忘录几乎一样,大家可以动手试试。

总结:

递归函数在我看来最重要的是要定义递归函数的功能,这个最为重要,其次就是要明白递归是一个不断重复的子过程。不过我们也可以在此基础上画图来更加深入地理解递归。举个简单的例子,如何用递归求一个数组的最大值?我们可以将数组一分为2,左边求最大值,右边求最大值,然后比较返回,此时的递归函数功能是啥?就是给你一个数组中的指定范围,求出指定范围的数组最大值。我们可以举个简单例子,比如只有4个元素的数组,可以进行画图来更加深入的理解上面的递归思想。不过话说回来,每个人都有每个人的看法,大家可以多思考,可以按照文章中提出的方法多写几道递归的题目,总有一天会让你感受到递归的思想。递归是很强大的,比如我们使用的排序,例如归并排序,快速排序都离不开递归。

这边可以稍微提一下两个排序的思想:归并排序就是将数组一分为2,左边先排好序,右边排好序,然后再合并。那归并排序的递归函数功能是什么呢?就是排序。我们要做的就是先将数组一分为2,左半部分调用递归有序,右半部分调用递归有序,我们最后要做的就是将两个有序数组合并成一个有序数组。

那么快速排序的怎么理解呢?快速排序涉及到荷兰国旗问题,就是随机选取一个数,将数组中小于这个数的数字放在左边,大于这个数的数字放在右边,接着再对左右两边继续递归。这两个排序加堆排序我也会进行整理总结,毕竟他们在面试中经常被提问,还是比较重要的。

这是我第一次写博客,可能有些语句不通顺或者逻辑不通,还请大家见谅,也希望大家能多多留言,我们一起进步。

如何才能轻而易举的写出递归函数相关推荐

  1. 如何才能写出优秀作文?猿辅导:生活的观察与感受非常重要

    猿辅导发现,无论是小学还是中学,作文都应该是不少家长跟孩子心里的"老大难".二三年级的孩子,平时能快速做完作业,一到写作文就跟挤牙膏一样,拖拖拉拉,半个小时憋不出一句话.五六年级的 ...

  2. 学会提问,ChatGPT可以帮你写出高质量论文

    前言 ChatGPT 很火,火到大家以为他可以上天入地,上到天文,下到地理无所不能,但实际使用大家是不是会遇到如下的情况. 写论文步骤 今天,我们来探讨下怎样问ChatGPT,才能帮你写出一篇优秀的论 ...

  3. 如何才能写出好的软件设计文档?

    作为一名软件工程师,我花了很多时间在阅读和撰写设计文档上.在磨砺了数百篇文档之后,我发现,优秀的设计文档与项目的成功之间有着密切的联系. 这篇文章将介绍怎样才能写出一份优秀的设计文档. 为什么要写设计 ...

  4. 如何才能写出一手高质量优美的代码

    怎么判断代码是否是优质量的代码呢?下面来简单对代码质量的问题进行一个介绍. 代码质量所涉及的5个方面,编码标准.代码重复.代码覆盖率.依赖项分析.复杂度分析.我们分别来看一下这5方面: 编码标准:一般 ...

  5. 程序员怎样才能写出一篇好的技术文章

    来源:http://droidyue.com/blog/2016/06/19/how-to-write-an-awesome-post/ 首先,这算是一篇回答知乎问题 程序员怎样才能写出一篇好的博客或 ...

  6. 如何才能写出一手逼疯同事的烂代码?

    要是想写个烂代码,我们只需遵守这十九条准则? 「代码写得好」是对机器学习研究者及开发者最好的赞扬.其第一层意思是说,你的模型非常好,有自己的理解与修正:第二层意思是说代码的结构.命名规则.编写逻辑都非 ...

  7. 「傻瓜」才能写出好代码!

    作者 | Esteban Gabriel 译者 | 弯月 责编 | 仲培艺 出品 | CSDN(ID:CSDNnews) 我觉得自己没有想象中那么聪明,而且还是一个健忘的人.正因如此,我写的代码才能一 ...

  8. 如何才能写出“高质量”的代码?

    作为一个已经写了十几年代码的程序员,做好软件不是全部围绕代码而展开,换句话讲一个程序员的程序员优秀不仅仅体现在代码上,更要有内在的编程思想说的层次再高深点就是框架思想.很多初学者都会存在很多疑问,觉得 ...

  9. java好的代码_做java软件工程师,怎样才能写出好的代码?

    原标题:做java软件工程师,怎样才能写出好的代码? Java代码之于java程序员而言就是左膀右臂,java代码写的好的java程序员明显更是企业的欢迎,一个优秀的java程序员的考核标准之一也是看 ...

最新文章

  1. graphpad怎么处理cck8的_Graphpad Prism 的 4 个隐藏技能助你轻松发表 SCI
  2. HDU 2087剪花布条 KMP
  3. STM32 基础系列教程 1- CubeMX+GPIO
  4. 写一个使两个整数进行交换的方法(不能使用临时变量) 【前端每日一题-27】...
  5. 前端模块化(二):模块化编程
  6. 机器阅读理解首次超越人类!云从刷新自然语言处理新纪录
  7. libsvm在matlab中使用的常见错误及libsvm的使用
  8. php 子文件夹如何定义,php-子文件夹的重写规则
  9. 有的朋友问我创业没有资金怎么办?
  10. CVE-2014-6332学习笔记
  11. Ubuntu Linux 环境变量PATH设置
  12. angular环境配置
  13. 私有api调用审核失败 prefs:root
  14. 基于三维点云的机器人杆件目标识别与抓取(三)
  15. AdMob(app内嵌广告)使用入门
  16. CDA LEVEL 1 考试,知识点《机器学习基本概念》
  17. [工具]Snipaste 屏幕截图软件超级利器 - 花3年精心打造的极致截图贴图/编辑/标注工具
  18. appium连接ios手机
  19. 网易我的世界中国版服务器存档文件在哪里,网易我的世界怎么导入存档 网易中国版手游存档位置...
  20. 支付宝支付二维码显示在商家网站页面,不跳转到支付宝?

热门文章

  1. 安全是一个系统问题包括服务器安全,信息安全技术题库:信息泄露对于Web服务器的危害在于( )。...
  2. java 参数三点,java函数参数类型后添加三点的用法
  3. python基础入门:bytes 和 string转换的方法
  4. Python字典循环与字典排序
  5. Python实现图片压缩
  6. Python里三个最高逼格的调试神器
  7. php维持登录,php怎么保持登录状态?
  8. java的多态是什么意思_Java中的多态是什么?
  9. contains java_Java CopyOnWriteArraySet contains()用法及代码示例
  10. java怎么解决页面乱码问题_java页面中文乱码的解决办法