如何写递归程序

教你如何写递归(数学归纳法,干货强推!)

 

引言

对于很多程序员来说,写递归程序是比较头疼的一件事,即使是把程序看懂了,轮到自己写的时候也是一脸懵逼,那么到底写递归有没有方法论呢?当然!本文就将从数学归纳法的角度教你如何写递归程序。 

数学归纳法与递归的关系

有人会疑惑,不是说好写递归嘛,怎么扯到数学归纳法了?别急慢慢往下看你就知道了。 
  
首先我们来温习一下数学归纳法的定义:

数学归纳法:用于证明断言对所有自然数成立。

证明过程:

  • 证明对于N=1成立
  • 证明N>1时:假设对于N-1成立,那么对于N成立

  
咋一看你或许有点蒙,没事,我们来举个例子你就差不多懂了。

  
怎么样,总结一下就是:先证明第一个自然数成立,然后假设对于n-1成立,在用这个假设去推导证明对于n也成立

对于自然数的第一个数字究竟是0还是1,是一个学术界至今没有统一的问题,我们这里使用1作为自然数的第一个数。

  
接下来重点来了!!!我们来看看如何用递归写上面那个例子

 
  
是不是惊人的相似啊!原来递归的思路和数学归纳法的证明思路是一样的啊! 
  
铺垫了这么多,终于引出我们今天要讲的递归书写方法论。 

递归书写方法

1. 先一般,后特殊(边界)

先写递归的主体部分,再回头写边界(一行行读主体部分的代码,寻找特殊的边界情况)

2. 每次调用必须缩小问题规模

3. 每次问题规模缩小程度必须为1(不要贪心)

这里的1并不是狭义的,比方说如果二分查找的时候,每次缩小一半的规模也是可以的

  
上面四点非常重要,为了帮助大家理解,接下来我举几个例子让大家热热身。 

例1:创建链表

我们想要创建一个五节点的单向链表(如下图所示):

 
  
该怎么做呢?首先我们先假设如果已经得到了后四个节点的链表:

 
  
那么此时我们只需要将第一个节点的next指针指向(n-1)结果的头节点即可

 
代码如下:

 

public ListNode createLinkedList(List<Integer> data) {

ListNode firstNode = new Node(data.get(0));

// 将第一个节点的next指针指向n-1已经创建好链表的头节点

firstNode.next = createLinkedList(data.subList(1, data.size()));

return firstNode; // 返回头节点

}

  
此时我们先一般的步骤就完成了,看看是不是每次调用函数都缩小问题规模了,是不是问题规模每次都缩小1,都满足条件!接下来就是后特殊了,划重点:一行行查看此时的代码,想想每一行代码在什么情况下出异常,一旦发现这就是特殊情况了! 
  
例如:上面函数中的第一行,如果data为空的话,是不是就报错了,那么此时就需要做特殊情况处理:

 

public ListNode createLinkedList(List<Integer> data) {

if (data.isEmpty())

return null;

ListNode firstNode = new Node(data.get(0));

// 将第一个节点的next指针指向n-1已经创建好链表的头节点

firstNode.next = createLinkedList(data.subList(1, data.size()));

return firstNode; // 返回头节点

}

  
那么到此为止,我们就完成了创建链表的递归程序编写。不过瘾?那我们再来一个!

例2:反转链表

 
  
还是老样子,我们先假设n-1个链表已经反转完成了:

 
  
那么此时我们应该怎么做呢?此时1这个元素的next指针还是指向2的,因为后面的反转并不会影响1元素的next指针:

 
  
所以此时我们只需要将节点2的next设为1,将节点1的next设为null即可完成反转!

 
代码如下:

 

public ListNode ReverseList(ListNode head) {

// 这里得到n-1反转后链表的头节点,也就是反转前的最后一个节点

ListNode newHead = ReverseList(head.next);

// 将节点2的next设为节点1

head.next.next = head;

// 将节点1的next设为null

head.next = null;

return newHead;

}

  
那么此时先一般就完成了,接下来就是寻找特殊(边界)情况了,一行行代码看下来发现head和head.next为null时会报异常,于是处理异常情况: 

 

public ListNode ReverseList(ListNode head) {

if(head == null || head.next == null)

return head;

ListNode newHead = ReverseList(head.next);

head.next.next = head;

head.next = null;

return newHead;

}

  
到此为止,反转链表函数就已经完成,是不是感觉按照数学归纳法的思路写递归还是蛮清晰的呢!

总结

数学归纳法是递归的依据。

书写递归的思路跟数学归纳法的证明过程相似,不过需要注意的是,先书写一般情况,后根据一般情况考虑特殊情况;同时要牢记递归函数每次缩小规模程度必须为“1”!

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值