文章目录
- 一、基础知识
- 二、刷题实战
- 1. 汉诺塔问题
- 2. 合并两个有序链表
- 3. 反转链表
- 4. 快速幂
- 三、技巧总结
一、基础知识
什么是递归:
函数自己调用自己
什么时候能用递归:
有重复子问题
如何看待递归函数:
看作一个黑盒
如何编写递归代码:
- 找到子问题
- 设计函数头
- 考虑递归出口
- 编写函数体
二、刷题实战
1. 汉诺塔问题
题目链接
参考代码:
class Solution
{
public:void hanota(vector<int>& a, vector<int>& b, vector<int>& c) {//dfs(x, y, z, n)//把 x 的 n 个盘子 借助 y 放到 zdfs(a, b, c, a.size());}void dfs(vector<int>& a, vector<int>& b, vector<int>& c, int n){//递归出口if(n == 1){c.push_back(a.back());a.pop_back();return;}//把 a 的 n - 1 个盘子 借助 c 放到 bdfs(a, c, b, n - 1);//把 a 的最后一个盘子 放到cc.push_back(a.back());a.pop_back();//把 b 的 n - 1 个盘子 借助 a 放到 cdfs(b, a, c, n - 1);}
};
2. 合并两个有序链表
题目链接
题目要求返回新的升序链表的头结点
- 找到子问题:返回头结点,合并剩下的两个链表。
- 设计函数头:函数返回一个结点,参数是剩下的两个链表。本题给出的接口就满足我们的需求。
- 考虑递归出口:当前合并的两个链表,如有一个为空,则返回另一个链表的头结点。
- 编写函数体:当前两个结点中较小的当作头结点,头结点的 n e x t next next 指向下一次返回的结点,最后返回这个头结点。
参考代码:
class Solution
{
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if(list1 == nullptr) return list2;if(list2 == nullptr) return list1;auto mi = list1->val <= list2->val ? list1 : list2;auto ma = list1->val > list2->val ? list1 : list2;mi->next = mergeTwoLists(mi->next, ma);return mi;}
};
3. 反转链表
题目链接
参考代码:
class Solution
{
public:ListNode* reverseList(ListNode* head) {//递归出口 只有0个或1个结点 无需反转 直接返回if(head == nullptr || head->next == nullptr) return head;//先反转后面的结点//递归函数看作黑盒auto res = reverseList(head->next);//再将当前结点反转head->next->next = head;head->next = nullptr;return res;}
};
4. 快速幂
题目链接
参考代码:
class Solution
{
public:double myPow(double x, int n) {//递归出口if(n == 0) return 1;if(n == 1) return x;if(n == -1) return 1 / x;//先求 x 的 n / 2 次方 x1double x1 = myPow(x, n / 2);//再求 x 的 n % 2 次方 x2double x2 = myPow(x, n % 2);return x1 * x1 * x2;}
};
三、技巧总结
-
先画决策树
-
注意代码细节