力扣递归题解——汉诺塔问题详解

面试题 08.06. 汉诺塔问题

此题是一道经典的递归问题
在这里插入图片描述

确定大致思路

递归的原理就是需要将问题的规模缩小,直到最简的情况,此题便是只有一个盘的情况。
动画演示思路:链接: 图解汉诺塔的故事

遵循递归的思路,由结果推过程。
首先我们看最简单的情况,就是只有一个汉诺塔。毫无疑问,只需将A处的盘挪到C即可。

然后再是两个塔的情况:
在这里插入图片描述

就是先将最后一个盘上的那个盘移动到B,再将底盘移动到C,最后将上面那个盘移动到C即可
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

再推广,解决5个汉诺塔问题,可以将上面四个盘视为一个整体,像两个盘的情况一样,先将4个盘挪到B,将红色盘挪到C,再进行一系列操作将四个盘挪到C,图解如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

至此,我们便大致分析出了递归思路:

  • 将移n个塔问题简化为将n-1个塔挪到B
  • 再将第n个盘挪到C
  • 再经过一系列操作将n-1个塔挪到C

此时,看似又有一个新问题出现了:一系列操作又是什么?
很简单,我们不需要仔细分析这一系列操作的步骤,只需关注它实现的功能即可。将n-1个盘挪到B,这个步骤是不是有些眼熟?没错,就和问题开始将A处的盘挪到C一摸一样,这个操作就是将我们原本的递归函数又调用了一遍。至此我们完全找到了递归的思路。

注意:递归问题中尤其要注意递归方法本身的含义。

  • 递归函数本身就是一个功能完善的方法,此题递归函数的作用就是解决n个塔从A移动到C的问题。
  • 即这个函数的功能就是将给定的n个盘从指定位置挪到目标位置
  • 我们通过上述分析简化了问题,让问题规模越来越小,试想一下,将盘移动到最后,是不是就是将最上面的小盘放到C?这一步的操作我们肯定会写。至此我们便完成了递归的全过程。

递归三部曲

递归终止条件

上面我们分析到简化为只有一个盘的情况,再把那一个盘挪到C,我们便解决了整个问题。
所以,我们的递归终止条件就是:只有一个盘的情况

再进一步思考,我们该如何知晓只剩一个盘的情况呢? 很简单,我们只需设置一个参数size表示需要挪动的塔的数量即可

    if(size==1){
           target.add(start.remove(start.size()-1));
           return ;
       }

确定传入参数

  • size:上面我们已经分析到,我们需要知道要移动的汉诺塔的个数
  • A B C :通过上面的过程分析,我们每次需要将塔移动到不同的柱子中,我们就自然要根据移动汉诺塔到不同的位置这一动作,用三个不同的参数表示

根据分析,我们确定最后的参数如下:

 private void remove(int size,List<Integer> start,List<Integer> auxiliary,List<Integer> target)

确定完成递归的步骤

将最底下的一个盘挪到目标位置,需要进行三个步骤:

  • 将上面n-1个盘挪到中间柱子
  • 将最后一个盘挪到目标柱子
  • 再将n-1个盘挪到目标柱子

根据上述三个步骤,挪动过程代码如下:

 //先把第一根柱子上size-1个盘移到第二根柱子上
       remove(size-1,start,target,auxiliary);
       //再把第一根柱子上的最后一个盘放到第三根柱子中
       target.add(start.remove(start.size()-1));
       remove(size-1,auxiliary,start,target);

整体代码

class Solution {
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
    remove(A.size(),A,B,C);
    }
    private void remove(int size,List<Integer> start,List<Integer> auxiliary,List<Integer> target){
        //递归终止条件
       if(size==1){
           target.add(start.remove(start.size()-1));
           return ;
       }
       //单层中进行的操作
       //先把第一根柱子上size-1个盘移到第二根柱子上
       remove(size-1,start,target,auxiliary);
       //再把第一根柱子上的最后一个盘放到第三根柱子中
       target.add(start.remove(start.size()-1));
       remove(size-1,auxiliary,start,target);
    }
}

在这里插入图片描述

总结:

  • 想要理清递归的思路,需要找到将问题规模化小且重复同一个动作的方法
  • 找出递归过程后,分析递归三部曲,可以更好地理清思路,写出最终的代码
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目描述: 给出两个非空链表来表示两个非负整数。其中,它们各自的位数是按照逆序的方式存储的,并且它们的每个节点只能存储一位数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以零开头。 示例: 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807 解思路: 目中要求我们按照逆序的方式存储两个非负整数,也就是说链表的头节点表示该数的个位,第二个节点表示该数的十位,以此类推。 因此,我们可以从两个链表的头节点开始,对应位相加,并记录进位,将结果添加到新的链表中。如果有进位,需要在下一位相加时加上进位。 需要注意的是,当两个链表的长度不一致时,可以将较短的链表的缺失位看作是 0。 最后,如果最高位有进位,还需要在新链表的最高位添加一个值为 1 的节点。 C 语言代码实现: /** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){ struct ListNode *head = NULL, *tail = NULL; int carry = 0; while (l1 || l2) { int n1 = l1 ? l1->val : 0; int n2 = l2 ? l2->val : 0; int sum = n1 + n2 + carry; if (!head) { head = tail = malloc(sizeof(struct ListNode)); tail->val = sum % 10; tail->next = NULL; } else { tail->next = malloc(sizeof(struct ListNode)); tail = tail->next; tail->val = sum % 10; tail->next = NULL; } carry = sum / 10; if (l1) l1 = l1->next; if (l2) l2 = l2->next; } if (carry > 0) { tail->next = malloc(sizeof(struct ListNode)); tail = tail->next; tail->val = carry; tail->next = NULL; } return head; }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值