递归专项总结

什么时候使用递归

1.大问题可以拆分为若干个小问题

2.原问题与子问题除数据规模不同,求解思路完全相同

3.存在递归终止条件

递归三部曲:

1.确定递归函数的参数和返回值

2.确定终止条件

3.确定单层递归的逻辑

题目1:

 求N的阶层

思路:

因为N! = (N - 1)! * N,所以适合使用递归解题。根据三部曲,分析得知,N!由它之前的(N - 1)!递推得到,因此追溯到最开始,则可以追溯到1!;所以可以确定终止条件即为递归到1,单层递归的逻辑即为N! = N * (N - 1)! 

int Factorial(int num){
    if (num == 1)     // 终止条件
        return 1;
    return num * Factorial(num - 1);    // 递归公式
}

题目2:

反转链表

思路:

首先,明确题意是要对链表进行反转,也就是前后进行交换,这个交换主要通过修改指针指向完成,想要使用递归完成,也就是要它完成前后节点的交换。因此调用递归函数reverseList(),它的两个参数也就是要交换的两个,即为NULL,head。

class Solution{
public:
    ListNode* reverseList(ListNode* head){
        return reverse(NULL, head);
    }

};

对于reverse()函数,也就是交换的主逻辑代码部分,交换前后两个节点,因此形参为pre和cur,因此定义函数为

ListNode* reverse(ListNode* pre, ListNode* cur){
        
}

根据递归三步走,第一步应该确定递归终止条件,也就是当cur走到最后的nullptr时,递归终止,那么此时函数应该返回的值应该是什么呢,由于递归的每一步是完成pre(前一个节点)和cur(后一个节点)的交换,所以当cur为nullptr时,pre也跟着走到了最后哦i一个节点的位置,那么反转之后,pre也就成为了头节点,因此,这部分代码应该写为:

ListNode* reverse(ListNode* pre, ListNode* cur){
    // 确定递归终止条件
    if(cur = nullptr) return pre;
}

第二步应该完成的是,将大问题拆分成小问题,确定单层递归逻辑

对于每一次递归,应该完成的是pre和cur的交换,这一过程通过修改指针指向完成。因此,这一部分应该写为

ListNode* reverse(ListNode* pre, ListNode* cur){
    // 确定递归终止条件
    if(cur = nullptr) return pre;
    // 将大问题拆解成小问题,这里即为改变cur的指向为指向前一个节点pre
    ListNode* tmp = cur->next;    // 保存当前节点的下一个节点,防止链表断开
    cur->next = pre;              // 当前节点的next指针指向前一个节点,完成反转
}

第三步应该为调用递归函数,同时确定参数。

调用本身,完成递归。前面的递归逻辑已经完成了cur指向pre的操作,而接下来应该继续向前递归,即为使cur->next指向cur。

ListNode* reverse(ListNode* pre, ListNode* cur){
    // 确定递归终止条件
    if(cur = nullptr) return pre;
    // 将大问题拆解成小问题,这里即为改变cur的指向为指向前一个节点pre
    ListNode* tmp = cur->next;    // 保存当前节点的下一个节点,防止链表断开
    cur->next = pre;              // 当前节点的next指针指向前一个节点,完成反转

    // 递归调用,继续反转下一段链表
    // 递归后:前一个节点变成当前节点,当前节点变成下一个节点
    reverse(cur, tmp);
}

完整代码为:

#include <iostream>
#include <vector>
#include <algorithm>

// 递归法 
class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* temp = cur->next;
        cur->next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }
    ListNode* reverseList(ListNode* head) { 
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);
    }

};

// 打印链表
void printList(ListNode* head) {
    ListNode* cur = head;
    while (cur != nullptr) {
        std::cout << cur->val << " ";
        cur = cur->next;
    }
    std::cout << std::endl;
}

int main() {
    // 创建链表: 1 -> 2 -> 3 -> 4 -> 5
    ListNode* head = new ListNode(1);
    head->next = new ListNode(2);
    head->next->next = new ListNode(3);
    head->next->next->next = new ListNode(4);
    head->next->next->next->next = new ListNode(5);

    std::cout << "Original list: ";
    printList(head);

    // 创建解决方案对象
    Solution solution;
    // 反转链表
    ListNode* reversedHead = solution.reverseList(head);

    std::cout << "Reversed list: ";
    printList(reversedHead);

    // 释放链表内存
    ListNode* cur = reversedHead;
    while (cur != nullptr) {
        ListNode* tmp = cur;
        cur = cur->next;
        delete tmp;
    }

    return 0;
}

 题目3:

二叉树递归遍历,要求前序遍历。

思路:

分析题意,要完成二叉树的前序遍历,也就是按照中->左->右的顺序完成整个二叉树的遍历,并将遍历结果保存在一个向量容器里。定义如下的递归入口函数:

class Solution{
public:
    std::vector<int> preorderTraversal(TreeNode* root){
        std::vector<int> result;
        traversal(root, result);
        return result;
    }
}

递归函数traversal()的主逻辑如下:

首先第一步应该明确递归的终止条件是什么,当遍历到nullptr时,递归应该停止,所以有

//递归函数
void traversal(TreeNode* cur, std::vector<int>& vec){
    // 明确终止条件,因为无需返回值,所以这里直接return后面不接任何东西
    if(cur == nullptr) return;
}

第二步应该确定每一步的递归逻辑,因为是要前序遍历,所以按照顺序,应该依次取值然后push到vector中,所以有

//递归函数
void traversal(TreeNode* cur, std::vector<int>& vec){
    // 明确终止条件,因为无需返回值,所以这里直接return后面不接任何东西
    if(cur == nullptr) return;
    vec.push_back(cur->val);      // 中
}

第三步应该确定递归参数,同时考虑顺序,所以有

//递归函数
void traversal(TreeNode* cur, std::vector<int>& vec){
    // 明确终止条件,因为无需返回值,所以这里直接return后面不接任何东西
    if(cur == nullptr) return;
    vec.push_back(cur->val);      // 中
    traversal(cur->left, vec);    // 左
    traversal(cur->right, vec);   // 右
}

扩展:

如果是中序遍历和后序遍历,也是一样的,只需要修改一下代码顺序

// 中序递归
void traversal(TreeNode* cur, std::vector<int>& vec) {
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    vec.push_back(cur->val);    // 中
    traversal(cur->right, vec); // 右
}
// 后序遍历
void traversal(TreeNode* cur, std::vector<int>& vec) {
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    traversal(cur->right, vec); // 右
    vec.push_back(cur->val);    // 中
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值