92. Reverse Linked List II**
https://leetcode.com/problems/reverse-linked-list-ii/
题目描述
Reverse a linked list from position m to n. Do it in one-pass.
Note: 1 ≤ m ≤ n ≤ length of list
.
Example:
Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL
C++ 实现 1
朴素的思路. 将整个过程分为三步:
- 对于
i < m
的节点, 按顺序挂载到dummy
链表中; - 对于
m <= i <= n
的节点, 进行翻转 - 对于
i > n
的节点, 按顺序挂载到dummy
链表中;
class Solution {
private:
ListNode* reverse(ListNode *head) {
ListNode *prev = nullptr;
while (head) {
auto tmp = head->next;
head->next = prev;
prev = head;
head = tmp;
}
return prev;
}
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode *dummy = new ListNode(0);
auto p = dummy;
int i = 1;
// 第一步
while (i < m) {
p->next = head;
p = p->next;
head = head->next;
++ i;
}
// 第二步, start 指向 m <= i <= n 的节点, 注意最末尾的指针指向 nullptr
auto start = head;
while (i < n) {
head = head->next;
++ i;
}
auto tmp = head->next;
head->next = nullptr;
head = tmp;
p->next = reverse(start);
// 第三步
while (p->next) p = p->next;
p->next = head;
return dummy->next;
}
};
C++ 实现 2
使用 start
找到第 m
个节点的前一个节点, 使用 end
找到第 n
个节点的后一个节点. 然后使用 reverse 函数翻转 start->next
与 end
之间的节点. 对常规的 reverse
函数进行了修改. 嗯, 两年前的代码.
现在再想想, reverse 函数中 tail
接收的是第 n
个节点的后一个节点, 是非常符合直觉的, 看我在 206. Reverse Linked List* 中对 reverse 的实现方法, 其中 prev
初始化为 nullptr
, 也可以看成是要翻转的链表中的最后一个节点的下一个节点.
class Solution {
private:
ListNode* reverse(ListNode *root, ListNode *tail) {
auto prev = tail;
while (root != tail) {
auto tmp = root->next;
root->next = prev;
prev = root;
root = tmp;
}
return prev;
}
public:
ListNode* reverseBetween(ListNode *head, int m, int n) {
if (!head || !head->next) return head;
auto dummy = new ListNode(0);
dummy->next = head;
int count = 0;
ListNode *ptr = dummy;
ListNode *start, *end;
while (ptr) {
count ++;
if (count == m)
start = ptr;
if (count == n + 1) // 由于ptr初始值指向 dummy, 所以 count 要等于 n+1
end = ptr->next;
ptr = ptr->next;
}
start->next = reverse(start->next, end);
return dummy->next;
}
};
C++ 实现 3
引入虚拟节点 dummy
, 另外 p
指向当前访问节点的前一个节点, 比如 [1, 2, 3, 4, 5], left=2, right=4
这个例子, 最终的效果是 prev
指向 1
, tail
指向 5
.
代码中 tail = p->next->next;
这一句之所以不需要判断 p->next
是否为空在于两点, 首先 right
节点按题目要求中说的, 是存在的, 另一方面, p
始终指向当前访问节点的前一节点; 由于当前访问的 right
节点存在, 即 p->next
存在, 因此可以直接访问 p->next
.
class Solution {
private:
ListNode* reverse(ListNode *head, ListNode *tail) {
ListNode *prev = tail;
while (head != tail) {
auto tmp = head->next;
head->next = prev;
prev = head;
head = tmp;
}
return prev;
}
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
ListNode *prev, *tail;
int i = 0;
ListNode *dummy = new ListNode(0);
dummy->next = head;
auto p = dummy;
while (p) {
i += 1;
if (i == left) prev = p;
if (i == right) {
tail = p->next->next;
break;
}
p = p->next;
}
prev->next = reverse(prev->next, tail);
return dummy->next;
}
};