前序:
递归解决的特点:大问题里面有子问题,子问题里面有子问题,找到重复子问题是关键
对于递归问题的解决,一定要先关注于宏观层面的解决,利用问题的解决
一:汉诺塔
题目:有三个柱子,分别为A,B,C,起初A柱子上有着按照大小顺序堆放的盘子,编写一个代码使得A柱子上的盘子全部转移到C柱子上。
规则:(1)大盘子必须在小盘子的下方
(2)一次只能移动一只盘子
分析(令盘子的数量为N):
若N = 1,直接将盘子从A移动到C即可
若N = 2,先把一个盘子移动到B,然后把最大的盘子移动到C,最后将B上的盘子移动到C
若N = 3,先把两个盘子移动到B,再将最大的盘子移动到C,最后再将B上的所有盘子移动到C
......
若N = n,先把n-1个盘子 移动到B,再将最大的盘子移动到C,最后再将B上的n-1个盘子移动到C
代码实现(该代码主要使用栈):
首先,应该分为总体分为三个部分,函数头(重复的递归子问题),函数体(重复子问题在做什么),以及递归出口(递归的结束)
函数头:void psf(vector<int>& a, vector<int>& b, vector<int>& c,int n)
(要将A,B,C三个柱子传过去,以及盘子的个数)
函数体的实现:
void psf(vector<int>& a, vector<int>& b, vector<int>& c, int n)
{
if (n == 1)
{
c.push_back(a.back());
a.pop_back();
return;
}
psf(a, c, b, n - 1);
c.push_back(a.back());
a.pop_back();
psf(b, a, c, n - 1);
}
递归出口:
if (n == 1)
{
c.push_back(a.back());
a.pop_back();
return;
}
二:合并两个有序链表
题目:将两个有序链表合并为一个有序链表
规则:不可以改变每个节点的地址,即不可以开创新节点
分析:
令两个链表的节点分别为m和n
先将两个链表的首节点进行比较,然后再将其后面的进行比较接在比较出来的节点后面
重复子问题:不断将两个链表中后面的节点进行比较以及合并
函数头:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2)
函数体如下
typedef struct ListNode
{
int val;
struct ListNode* next;
}ListNode;
//递归解决合并链表
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if (list1 == nullptr)
return list2;
if (list2 == nullptr)
return list1;
if (list1->val >= list2->val)
{
list2->next = mergeTwoLists(list1, list2->next);//将后面n-1个节点进行比较和合并
return list2;
}
else
{
list1->next = mergeTwoLists(list1->next, list2);
return list1;
}
}
递归出口:当节点为空指针,结束
三:反转链表
题目:将一个链表的所有节点进行反转
规则:不可以创建新节点,所有节点的地址不变
分析(令节点个数为n):
重复子问题:不断将前一个节点与后面的节点进行倒置
第一个节点保持不变,将后面的n-1个节点进行倒置,返回值用新节点接受,之后要将头结点连接进链表
函数头:ListNode* reverseList(ListNode* head)
函数体如下:
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;
}
递归出口:当节点为空
四:两两交换链表中的节点
题目:两两交换一个链表中的相邻两个节点
规则:只可以节点进行交换
分析:
重复子问题:不断相邻的两个节点进行交换
可以从n-2个开始进行交换,然后再单独对于前两个节点进行交换,最后再将这两个部分连接起来即可
函数头:ListNode* swapPairs(ListNode* head)
函数体的实现:
ListNode* swapPairs(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return head;
ListNode* newlist = swapPairs(head->next->next);
ListNode* pure = head->next;
head->next = newlist;
pure->next = head;
return pure;
}
递归出口:若两个相邻节点其中有任意一个为空
五:Pow(x,n)快速幂
题目:实现X的N次幂快速计算
分析:
重复子问题:后一个数就等于前一个的X倍
先计算2/n个数的结果,然后根据数据个数是否为偶数,分别进行计算即可,在计算过程中还要考虑n是否为负
函数头:double myPow(double x, int n) double pow(double x, long long n)
函数体实现:
double myPow(double x, int n) {
return n>0?pow(x,n):1/pow(x,-(long long)n);
}
double pow(double x,long long n)
{
if(n==0)
return 1.0;
double tmp = pow(x,n/2);
return n%2==0?tmp*tmp:tmp*tmp*x;
}
递归出口:n==0
总结:
对于递归一类的题目,要将递归函数具体的实现看做一个黑盒子,默认他一定会完成我们宏观上规定的任务,然后直接进行书写即可。