硅基计划4.0 算法 递归

在这里插入图片描述



一、基础认知

1. 看待递归

我们对于递归,按照三步理解
理解递归展开图-->做二叉树的题-->用宏观视角看待递归(不在意细节、递归看成未知惊喜、相信这个惊喜能够返回正确结果)

2. 写好递归

  1. 首要:将大问题细分,找到相同的子问题,这关乎函数头(参数)的设计
  2. 其次:我们只关心每一个子问题是怎么解决的,这关乎函数体的书写
  3. 最后:设置递归出口,即设计地柜的结束条件,避免死递归

3. 深度优先遍历——DFS&宽度优先遍历——BFS

对于DFS
顾名思义,就是一条路走到底,像我们之前写的二叉树的三种遍历就是这样
只有遇到空节点,我们才开始返回
之前在网上听到的回溯,本质上就是指的深搜时的返回情况
比较经典的有全排列问题以及迷宫问题
对于BFS
我们之前写过层序遍历,就是一层一层抽丝剥茧,逐步深入

二、小试牛刀

1. 汉诺塔问题

题目链接
这题我们之前有讲过,我们这里再讲一遍
image-20251015102740232
因此我们在递归的时候,柱子的顺序是会变的
但是递归后,我们每一步的操作还是一样的,就是借助一个中间柱子,把一个盘子从一个柱子移动到另外一个柱子

class Solution {
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
        hanotaChild(A,B,C,A.size());
    }

    private void hanotaChild(List<Integer> A, List<Integer> B, List<Integer> C,int count){
        if(count == 1){
            C.add(A.remove(A.size()-1));//只剩一个盘子的时候直接:A-->C
            return;
        }
        hanotaChild(A,C,B,count-1);//把A柱上的n-1个盘子:A-->C-->B(C是辅助)
        C.add(A.remove(A.size()-1));//此时A柱上只有一个大盘子:A-->C
        hanotaChild(B,A,C,count-1);//此时再把B柱上的n-1个盘子:B-->A-->C(A是辅助)
    }
}

2. 合并两个有序链表

题目链接
这一题核心就是寻找两个指针的最小值
两个指针分别是两个链表头节点
我们函数体就是
如果list1指针的值大于list2指针的值,我们直接list1.next = dfs(list1.next,list2)
反之,我们list2.next = dfs(list1,list2.next)
最后出口如果一个指针空了,返回另一个指针,如果都空了,递归就结束了

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        //终止条件
        if(list1 == null){
            return list2;
        }else if(list2 == null){
            return list1;
        }
        if(list1.val >= list2.val){
            list2.next = mergeTwoLists(list1,list2.next);
            return list2;//作为新的头节点
        }else{
            list1.next = mergeTwoLists(list1.next,list2);
            return list1;//作为新的头节点
        }
    }
}

3. 反转链表

题目链接
这一题我们要递归到最后一个节点
然后让最后一个节点作为新的头节点
修改每个节点指针指向,然后一直返回这个新的头节点就好
image-20251015104501711

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            //head == null处理的是空链表的情况,而head.next == null才是递归终止的真正条件
            return head;
        }
        //注意newHead是在递归最深处被确定的,之后回归的时候不会再改变
        ListNode newHead = reverseList(head.next);
        //反转链表操作
        head.next.next = head;
        head.next = null;
        //注意每一次return的newHead都是同一个节点,即原链表的尾节点,从未改变
        return newHead;
    }
}

4. 两两反转链表中的节点

题目链接
这一题我们需要记录当前的两个节点,而且回溯的时候返回两个节点反转后的节点
而且不要忘记最先要记录两个节点后面的一个节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        //先从最后一个子问题开始
        ListNode tmp = swapPairs(head.next.next);
        //两两反转
        ListNode node = head.next;
        node.next = head;
        //再把后面剩下链表接上
        head.next = tmp;
        //再返回当前的反转后的第一个链表
        return node;
    }
}

5. 快速幂计算

题目链接
这一题我们可以把一个幂进行拆分
3^8 = 3^4*3^4 = 3^2*3^2 = 3^1*3^1*3^1*3^1 = 3^0*3^0*3^0*3^0*3^0*3^0*3^0*3^0
如果我们次方不是二的倍数,比如3^5,我们可以拆分成3^4*3即可
如果是负数次幂呢?我们直接1/正数次幂计算结果

class Solution {
    public double myPow(double x, int n) {
        return n < 0 ? 1.0/pow(x,n) : pow(x,n);
    }

    private double pow(double x,int n){
        if(n == 0){
            return 1.0;
        }
        double tmp = pow(x,n/2);
        return n % 2 == 0 ? tmp*tmp : tmp*tmp*x;
    }
}

希望本篇文章对您有帮助,有错误您可以指出,我们友好交流

END
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值