主要参考:
文章目录
递归思想
递归的思路:
重复利用函数本身能实现的功能,按照顺序进行不断地自调用
核心: 建立递归函数
函数主要包含:
1.边界判断
2.实现需要完成的功能
3.对下一跳/项继续进行函数自身的调用
注意!!!这个评论说的很对
1.这个问题的子问题是什么。
2.当前层要干什么事情。
3.递归出口。想清楚这几点就好啦。
很多刚接触递归的同学习惯往深处想,就想想清楚下一层,下下层到底咋回事,千万别!这样永远也想不清楚的,你只要把当前层的事情干好,边界条件找好,深层自然而然就是对的。千万别想那么深。
链表
参考:数据结构之链表与递归
函数模板
一般来说链表递归的终止条件都为 head == NULL || head->next==NULL
递归函数function(当前节点){
终止条件判断(遍历到尽头直接 return)
剪枝条件判断(return)
这一层处理
return 下一层处理的结果
}
主函数( ){
对头节点调用函数function
返回结果
}
leetcode部分题目
链表
leetcode中链表的声明
/**
* 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) {}
* };
*/
206. 反转链表
思路:
原地反转
迭代法:
双指针实现,可以移步我的另一篇博客:leetcode_刷题总结_先后指针/前后指针
递归法:
解法一:按双指针的思路写
因为题目最后需要返回一个链表,故可以设置为有返回的递归
递归法相和双指针法是一样的逻辑,同样是当cur为空的时候循环结束,不断将cur指向pre的过程。
关键是初始化的地方,可能有的同学会不理解, 可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的。
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL)//边界
return pre;
// 保存一下 cur的下一个节点,因为反转当前节点之后还需要继续递归往后走
ListNode* cur_next = cur->next;
cur->next = pre;
//对下一个节点调用reverse
return reverse(cur,cur_next);
}
ListNode* reverseList(ListNode* head) {
return reverse(NULL, head);
}
};
解法二:
直接在题目给定函数上递归:
子问题:
对现在访问的节点(这一层)进行反转构造
当前层要干什么:
- 如果head为空,说明走到尽头,直接返回head
(因为已经反转过了,虽然走到尽头,但是是反转链表的头节点) - 对当前节点(这一层)进行反转,
(head->next)->next=head
head=NULL
后面是打包好的newhead
reverseList要做的是下一层的事情,当前处理了head,head->next 就是下一层。
newhead=reverseList(head->next) 为了防止断链,前面先执行。
递归出口:
当所有节点都执行完之后,任务就完成了,就可以返回了。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// 这里的head就相当于前面的cur
if (head == NULL || head->next == NULL) //边界
return head;
ListNode* newHead = reverseList(head->next);
head->next->next