Leetcode [92][206]. 反转链表ii&i-输入一个链表,反转链表后,输出链表的所有元素。
1 /* 2 struct ListNode { 3 int val; 4 struct ListNode *next; 5 ListNode(int x) : 6 val(x), next(NULL) { 7 } 8 };*/ 9 class Solution { 10 public: 11 ListNode* ReverseList(ListNode* pHead) { 12 ListNode* res=NULL; 13 ListNode* pre=NULL; 14 if(pHead==NULL) return res; 15 while(pHead!=NULL){ 16 pre=pHead->next; 17 pHead->next=res; 18 res=pHead; 19 pHead=pre; 20 } 21 return res; 22 23 } 24 };
思路:递归反转链表的一部分
实现代码:
class Solution { public: ListNode* reverseList(ListNode* head) { if(head==nullptr||head->next==nullptr) return head; ListNode* last=reverseList(head->next); head->next->next=head; head->next=nullptr; return last; } };
对于递归算法,最重要的就是明确递归函数的定义。具体来说,我们的reverse
函数定义是这样的:
输入一个节点head
,将「以head
为起点」的链表反转,并返回反转之后的头结点。
那么输入reverse(head)
后,会在这里进行递归:
ListNode* last=reverseList(head->next);
按照定义,这个reverse(head.next)
执行完成后,整个链表应该变成了这样:
并且根据函数定义,reverse
函数会返回反转之后的头结点,我们用变量last
接收了。
现在再来看下面的代码:
head.next.next = head;
接下来进行的操作:
head.next = null;
return last;
神不神奇,这样整个链表就反转过来了!递归代码就是这么简洁优雅,不过其中有两个地方需要注意:
1、递归函数要有 base case,也就是这句:
if (head.next == null) return head;
意思是如果链表只有一个节点的时候反转也是它自己,直接返回即可。
2、当链表递归反转之后,新的头节点是last
,而之前的head
变成了最后一个节点,别忘了链表的末尾要指向 null:
head.next = null;
反转链表ii
反转链表ii
思路:递归反转链表的一部分
1、首先实现反转链表的前N个节点
// 将链表的前 n 个节点反转(n <= 链表长度) ListNode reverseN(ListNode head, int n)
比如说对于下图链表,执行reverseN(head, 3)
:
解决思路和反转整个链表差不多,只要稍加修改即可:
ListNode successor = null; // 后驱节点 // 反转以 head 为起点的 n 个节点,返回新的头结点 ListNode reverseN(ListNode head, int n) { if (n == 1) { // 记录第 n + 1 个节点 successor = head.next; return head; } // 以 head.next 为起点,需要反转前 n - 1 个节点 ListNode last = reverseN(head.next, n - 1); head.next.next = head; // 让反转之后的 head 节点和后面的节点连起来 head.next = successor; return last; }
具体的区别:
1、base case 变为n == 1
,反转一个元素,就是它本身,同时要记录后驱节点。
2、刚才我们直接把head.next
设置为 null,因为整个链表反转后原来的head
变成了整个链表的最后一个节点。但现在head
节点在递归反转之后不一定是最后一个节点了,所以要记录后驱successor
(第 n + 1 个节点),反转之后将head
连接上。
2、实现反转一部分
首先,如果m == 1
,就相当于反转链表开头的n
个元素嘛,也就是我们刚才实现的功能:
ListNode reverseBetween(ListNode head, int m, int n) { // base case if (m == 1) { // 相当于反转前 n 个元素 return reverseN(head, n); } // ... }
如果m != 1
怎么办?如果我们把head
的索引视为 1,那么我们是想从第m
个元素开始反转对吧;如果把head.next
的索引视为 1 呢?那么相对于head.next
,反转的区间应该是从第m - 1
个元素开始的;那么对于head.next.next
呢……
区别于迭代思想,这就是递归思想,所以我们可以完成代码:
ListNode reverseBetween(ListNode head, int m, int n) { // base case if (m == 1) { return reverseN(head, n); } // 前进到反转的起点触发 base case head.next = reverseBetween(head.next, m - 1, n - 1); return head; }
最终提交代码:
class Solution { public: ListNode* reverseBetween(ListNode* head, int left, int right) { if(left==1) { return reverseN(head,right); } head->next=reverseBetween(head->next,left-1,right-1); return head; } 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; } ListNode* successor=NULL; };