难度中等651
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4 输出: 1->4->3->2->5->NULL
从labuladong大佬那学到的方法必须大赞!!!居然给我讲明白了递归,我哭了都,感动!!!
/**
* 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* reverseBetween(ListNode* head, int m, int n) {
if(m == 1){
return reverseN(head, n);
}
head->next = reverseBetween(head->next, m-1, n-1);
return head;
}
ListNode* successor = nullptr;
ListNode* reverseN(ListNode* head, int n){
if(n == 1){
successor = head->next;
return head;
}
ListNode* last = reverseN(head->next, n-1);
head->next->next = head;
head->next = successor;
return last;
}
};
精讲
1、首先是reverse()函数,反转整个链表。
ListNode* reverse(ListNode* head){
if(head == nullptr || head->next == nullptr){
return head;
}
ListNode* last = reverse(head->next);
head->next->next = head;
head->next = nullptr;
return last;
}
看到这个函数一开始觉得不可思议,递归解决的反转链表不是我能理解的。但后来在大佬的题解里面,看到
对于递归算法,最重要的就是明确递归函数的定义
在这个反转链表的递归函数里面,输入链表的head节点,将以head为起点的链表反转,然后返回之后的头结点。
紧接着看到的就是下面这个代码
ListNode* last = reverse(head->next);
看到这我很容易就会到这个代码里面去想递归怎么递归的,然后然后。。。就陷进去了。
大佬题解中给出的建议是,不要跳进递归里去,要根据之前的函数定义,看看这段代码会产生什么结果。
想到这,这段代码产生的结果就是,把head->next 后面的所有节点都反转一下。
然后接着又是下面这个代码:
head->next->next = head;
这个有点绕,但是仔细理理就能弄清楚,就是把head的下一个节点的下一个节点指向自己,也就是反转链表里对head本身进行反转。至此链表已经全部反转完毕!
那么千万不要忘记,还要将head->next = nullptr,还要置为空。
总结一下这里面递归函数需要注意的地方:
递归函数需要有的基础条件:如果链表只有一个节点时,反转自己,直接返回。
if(head == nullptr){
return head;
}
2. 接着是 reverseN()函数,这个函数是反转链表前N个节点。
ListNode* successor = nullptr;
ListNode* reverseN(ListNode* head, int n){
if(n == 1){
successor = head->next;
return head;
}
ListNode* last = reverseN(head->next, n-1);
head->next->next = head;
head->next = successor;
return last;
}
与上面函数定义是类似的道理,
不同的是 head->next = succressor, 将前N个节点都反转之后,head节点的下一个节点应该指向第N个节点之后的节点。
然后这里的基础条件是: 当只要反转前一个节点时,successor节点是等于头结点之后的节点,也就是第二个节点,这样是没有问题的。也就是反转一个元素,同时记录它的后驱节点。
并且这里的n 是不断减少的。所有基础条件n == 1 是没问题的。
3. 反转部分链表
如果m == 1 就是反转链表开头n个元素。如果m != 1, 把head的索引看成1,那么从第m 个元素开始反转;那么,head->next的索引看成1 那么就从第m-1个元素开始。后面一次类推。
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(m == 1){
return reverseN(head, n);
}
head->next = reverseBetween(head->next, m-1, n-1);
return head;
}
感谢有这样写的很详细仔细的题解帮助,也感谢大佬们愿意分享这些去帮助他人,感谢!!!