LeetCode 92. 反转链表 II
原题链接
中等
作者:
贺谦
,
2020-05-01 12:53:43
,
所有人可见
,
阅读 998
算法(模拟)
借鉴了y总的图解。
解题思路
- 首先特判,如果(m = n),就没有翻转,直接
return head
即可。
- 因为可能会翻转头结点head,所以构建一个虚拟头结点dummy,来保护头结点。
dummy->next = head
。
a
是第m个节点的前一个节点的位置,b
是第m个节点的位置,c
是第n个节点的下一个位置,d
是第n个节点的位置。这里要注意,a
和d
的初始值是dummy
,它们的next指向的是head
,这样可以更好理解a
和d
具体位置的求解。
- 接下来,对m和n之间的节点进行处理,让
m + 1
到n
这条链上的节点的next指针反转,指向自己的前驱节点,因为a b c d
四个位置是不能变的,所以新建立3个变量p q o
,因为改变的是m + 1 ~ n
个节点的next指针指向,所以让q
作为第m + 1
个节点,o
来存储反转前的后继结点,p
则对应q
的前驱节点。可以理解为q
是当前节点,o
和p
分别维护的是后继和前驱节点。当q
指针指向位置c
的时候,意味着第n
个节点(d)的next指针已经指向了它的前驱,那么反转处理到这里就结束了。
- 最后,将
a
的next指针指向d
,也就是第m个节点的前驱节点指向第n个节点,第m个节点指向第n+1个节点,返回dummy->next,就得到了答案。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n)
{
if(m == n) return head;
ListNode *dummy = new ListNode(-1);
dummy->next = head;
ListNode *a = dummy;
ListNode *d = dummy;
for(int i = 0; i < m - 1; i ++) a = a->next;
// for(int i = 0; i < n - 1; i ++) d = d->next;
for(int i = 0; i < n; i ++) d = d->next;
auto b = a->next;
auto c = d->next;
// for(auto p = b, q = p->next; p != c;)
for(auto p = b, q = b->next; q != c;)
{
auto o = q->next;
q->next = p, p = q, q = o;
}
a->next = d;
b->next = c;
return dummy->next;
}
};
这种写法如果要翻转的是整个链表,相当于要遍历两次
1 2 3 4 5
1
4
那么一开始,dummy->next = head(data:1)
反转后4 3 2 1 5
return dummy->next,获取到的链表不是1 5?
不是啊,dummy的指向已经改变了
没想懂为什么有了dummy就能保护头结点,如果头结点包含进反转的区间里面的话,实际上反转之后头结点已经变了,为什么返回dummy->next还能获得正确结果,好奇怪/