什么时候使用递归
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); // 中
}