💁♂️个人主页:进击的荆棘
👇作者其它专栏:
目录
1.总结
2.相关题解
1.总结
在解决一个规模为n的问题时,若满足一下条件,我们可以使用递归来解决:
a.问题可以被划分为规模更小的子问题,并且这些子问题具有与原问题相同的解决问题。
b.当我们知道规模更小的子问题(规模为n-1)的解时,我们可以直接计算出规模为n的问题的解。
c.存在一种简单情况,或者说当问题的规模足够小时,我们可以直接求解问题。
一般的递归求解过程如下:
a.验证是否满足简单情况。
b.假设较小规模的问题已经解决,解决当前问题。
上述步骤可以通过数学归纳法来证明。
2.相关题解
1汉诺塔问题
算法思路:
这是一道递归方法的经典题目,我们可以先从最简单的情况考虑:
●假设n=1,只有一个盘子,很简单,直接把它从A中拿出来,移到C上;
●若n=2呢?这时候就需要借助B,因为小盘子必须在大盘子上面,共需要3步(为方便叙述,记A中的盘子从上到下为1号,2号):
a.1号盘子放到B上;
b.2号盘子放到C上;
c.1号盘子放到C上。
至此,C中的盘子从上到下为1号,2号。
●若n>2呢?这时我们需要用到n=2时的策略,将A上面的两个盘子挪到B上,再将最大的盘子挪到C上,最后将B上的小盘子挪到C上就完成了所有步骤。当n=3时如下:

因为A中最后处理的是最大的盘子,所以再移动过程中不存在大盘子在小盘子上面的情况。
则本题可以被解释为:
1.对于规模为n的问题我们需要将A柱上的n个盘子移动到C柱上。
2.规模为n的问题可以被拆分为规模为n-1的子问题:
a.将A柱上面的n-1个盘子移动到B柱上。
b.将A柱上的最大盘子移动到C柱上,然后将B柱上的n-1个盘子移动到C柱上。
3.当问题的规模变为n=1时,即只有一个盘子时,我们可以直接将其从A柱移动到C柱。
●需要注意的是,步骤2.b考虑的是总体问题中的子问题b情况。在处理子问题的子问题b时,我们应将A柱中最上面的盘子移动到C柱,然后再将B柱上的盘子移动到C柱。在处理总体问题的子问题b时,A柱中的最大盘子仍然是最上面的盘子,因此这种做法是通用的。
算法流程:
递归函数设计:void dfs(vector<int>& a, vector<int>& b, vector<int>& c,int n)
1.返回值:无;
2.参数:三个柱子上的盘子,当前需要处理的盘子个数(当前问题规模)。
3.函数作用:将A上面的n个盘子挪到C上。
递归函数流程:
1.当前问题规模为n-1时,直接将A中的最上面盘子挪动到C中并返回;
2.递归将A中最上面的n-1个盘子挪动到B中;
3.将A中最上面的一个盘子挪动到C中;
4.将B中上面的n-1个盘子挪动到C中。
cpp
class Solution {
public:
void hanota(vector<int>& a, vector<int>& b, vector<int>& c) {
dfs(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移到b上
dfs(a,c,b,n-1);
c.push_back(a.back());
a.pop_back();
//将b上的n-1个盘子借助a移到c上
dfs(b,a,c,n-1);
}
};
2.合并两个有序链表
算法思路:
1.**递归函数的含义:**有两链表的头节点,把它们合并起来,并返回合并后的头节点;
2.**函数体:**选择两个头节点中较小的节点作为最终合并后的头节点,然后将剩下的链表交给递归函数去处理;
3.递归出口:当某一链表尾空时,返回另一链表。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(!list1) return list2;
if(!list2) return list1;
if(list1->val<=list2->val) {
list1->next=mergeTwoLists(list1->next,list2);
return list1;
}
else {
list2->next=mergeTwoLists(list1,list2->next);
return list2;
}
}
};
3.反转链表
算法思路:
1.**递归函数的含义:**交给你一个链表的头指针,逆序后,返回逆序后的头节点;
2.**函数体:**先把当前节点之后的链表逆序,逆序完之后,把当前节点添加到逆序后的链表后面即可;
3.**递归出口:**当前节点为空或者当前只有一个节点的时候,不用逆序,直接返回。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head||!head->next) return head;
//将此节点后面的链表逆置
ListNode* newhead=reverseList(head->next);
head->next->next=head;//后面的节点指向它
head->next=nullptr;//它指向空,方便将逆置后的尾指向空
return newhead;
}
};
4.两两交换链表中的节点
算法思路:
1.**递归函数的含义:**有一链表,将这个链表两两交换,然后返回交换后的头节点;
2.**函数体:**先去处理一下第二个节点往后的链表,然后再把当前的两个节点交换,连接上后面处理后的链表;
3.**递归出口:**当前节点为空或者当前只有一个节点的时候,不要交换,直接返回。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(!head||!head->next) return head;
//先把第三个往后的节点交换
ListNode* tmp=swapPairs(head->next->next);
ListNode* ret=head->next;
head->next=tmp;
ret->next=head;
return ret;
}
};
5.Pow(x, n)
算法思路:
1.**递归函数的含义:**求出x的n次方是多少,然后返回;
2.函数体:先求出x的n/2次方是多少,然后根据n的奇偶,得出x的n次方是多少;
3.**递归出口:**当n为0的时候,返回1即可。
cpp
class Solution {
public:
double myPow(double x, int n) {
//return n<0?dfs(x,-n):dfs(x,n);
//当n为无穷小时,变成正数时会存不下,强转为long long
return n<0?1.0/dfs(x,-(long long)n):dfs(x,n);
}
double dfs(double x,long long n){
if(n==0) return 1.0;
double tmp=dfs(x,n/2);
return n%2==0?tmp*tmp:tmp*tmp*x;
}
};