递归算法学习

一、递归简介

  1. 递归是通过重复将原问题分解为同类的子问题而解决问题的方法,在编程语言中常通过函数调用自身实现。
  2. 以阶乘计算为例,展示了递归的计算过程,包括递推过程和回归过程,其基本思想是把规模大的问题不断分解为子问题来解决。

二、递归和数学归纳法
递归的数学模型是数学归纳法,递归终止条件对应数学归纳法第一步,递推过程对应假设部分,回归过程对应推论部分。

三、递归三步走

  1. 写出递推公式:找到原问题分解为子问题的规律并抽象成递推公式,对于复杂问题可假设子问题已解决,考虑如何解决原问题从而确定递推和回归过程,进而写出递推公式。
  2. 明确终止条件:终止条件是递归出口,通常为问题的边界值,找到后应直接给出该条件下的处理方法。
  3. 将递推公式和终止条件翻译成代码:
    • 定义递归函数,明确函数意义、传入参数和返回结果。
    • 书写递归主体,根据递推公式缩小问题规模。
    • 明确递归终止条件,将终止条件和处理方法转换为代码中的条件语句和执行语句。

四、递归的注意点

  1. 避免栈溢出:递归利用堆栈实现,调用次数过多会导致栈空间溢出。可限制递归调用最大深度或改为非递归算法解决。
  2. 避免重复运算:如斐波那契数列递归中可能出现重复运算,可使用缓存保存已求解结果避免重复计算。

五、练习

1.斐波那契

  1. 递推公式
    • 题目中已经明确给出了斐波那契数列的递推公式为F(n) = F(n - 1) + F(n - 2),其中n > 1
  2. 明确终止条件
    • n = 0时,F(0) = 0
    • n = 1时,F(1) = 1
  3. 递归过程
    • 对于给定的整数n,如果n > 1,则通过递归调用函数自身来计算F(n - 1)F(n - 2),然后将它们的和作为F(n)的值返回。
    • 如果n等于01,则直接返回对应的已知值。
class Solution {
public:
    int fib(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        return fib(n - 1) + fib(n - 2);
    }
};

2.爬楼梯

  1. 递推公式
    • 考虑最后一步到达第 n 阶楼梯的情况。如果最后一步是从 n - 1 阶台阶跨一步上来的,那么有 f(n - 1) 种方法到达 n - 1 阶,也就对应 f(n - 1) 种到达 n 阶的方法;如果最后一步是从 n - 2 阶台阶跨两步上来的,那么有 f(n - 2) 种方法到达 n - 2 阶,也就对应 f(n - 2) 种到达 n 阶的方法。所以总的到达 n 阶的方法数 f(n) = f(n - 1) + f(n - 2)
  2. 明确终止条件
  3. 当 n = 1 时,只有一种方法,即直接跨一步到达;当 n = 2 时,有两种方法,可以一次跨两阶或者分两次每次跨一阶。
    class Solution {
    public:
        int climbStairs(int n) {
            if (n == 1) return 1;
            if (n == 2) return 2;
            return climbStairs(n - 1) + climbStairs(n - 2);
        }
    };


3.翻转二叉树

  1. 递归思路
    • 对于给定的二叉树,要翻转它,就是将每个节点的左右子树进行交换。
    • 可以采用递归的方式,先递归地翻转左子树和右子树,然后再交换当前节点的左右子树指针。
  2. 终止条件当节点为空时,直接返回空指针,这是递归的终止条件
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) return nullptr;
        TreeNode* leftInverted = invertTree(root->left);
        TreeNode* rightInverted = invertTree(root->right);
        root->left = rightInverted;
        root->right = leftInverted;
        return root;
    }
};

4.发转链表

  1. 递归思路
    • 反转单链表可以通过递归的方式实现。假设已经反转了链表中从当前节点的下一个节点开始的部分,那么只需要将当前节点插入到反转后的链表的头部即可。
    • 对于链表中的每个节点,都将其下一个节点作为新的递归输入,递归地反转后续链表,然后将当前节点插入到反转后的链表头部。
  2. 终止条件
  3. 当链表为空或者只有一个节点时,不需要反转,直接返回该节点。
  4. class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            if (head == nullptr || head->next == nullptr) return head;
            ListNode* newHead = reverseList(head->next);
            head->next->next = head;
            head->next = nullptr;
            return newHead;
        }
    };

            

5.第k个语法符号

一、递推公式

    • 当 k 小于当前行的中间位置(即 k < mid)时,第 n 行的前半部分字符与第 n - 1 行的对应位置字符相同,所以可以直接递归地在第 n - 1 行中查找第 k 个字符,即 findKthBit(n - 1, k)
    • 当 k 大于当前行的中间位置时,由于对称性,第 n 行的后半部分字符是对第 n - 1 行对应位置字符取反后得到的。所以先在第 n - 1 行中找到对称位置的字符(即 findKthBit(n - 1, mid * 2 - k)),然后如果这个字符是 0,则当前位置字符为 1;如果这个字符是 1,则当前位置字符为 0

二、终止条件

  1. 当 n 为 1 时,无论 k 是多少,第一行只有一个字符 0,所以直接返回 '0'。这是递归的终止条件。
class Solution {
public:
    char findKthBit(int n, int k) {
        if (n == 1) return '0';
        int mid = 1 << (n - 1);
        if (k == mid) return '1';
        char prevChar = findKthBit(n - 1, k < mid? k : mid * 2 - k);
        return k > mid? (prevChar == '0'? '1' : '0') : prevChar;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值