题目:Reverse a linked list from position m to n. Do it in-place and in one-pass.
先复习一下普通的翻转链表的头插法:
3个变量,head2,新头;cur,遍历变量,next保存需要遍历的下一个结点。4句话。
next= cur->next;
cur->next=head2;
head2=cur;
cur=next;
Reverse Linked List || 的基本框架也是头插法,因为翻转的是链表的一部分,还要考虑和整个链表的连接问题。
1)首先考虑父链表和子链表头的对接问题:
因为头插法里head2变量始终存放翻转后链表头,只需把m-1节点指针域用作head2变量就可以了,对接始终保持。
2)子链表尾和父链表的的对接问题:
最后的状态应该是m节点作为子链表尾与父链表对接,所以可以考虑把m节点的指针域用作next变量。所以这里的头插法从第二个节点(m+1处)开始。
3)总结:算法的主框架就是头插法reverse 链表,只是要用m-1结点指针域作为head2变量, m结点指针域作为next变量, cur指针从m+1节点开始到n节点。如果m=1怎么办?这时候不存在m-1节点。解决办法:dummy节点。
ListNode *reverseBetween(ListNode *head, int m, int n) {
ListNode dummy(-1), *prevM = &dummy;
dummy.next = head;
for (int i = 0; i < m - 1 && prevM; ++i, prevM = prevM->next) ;
if (!prevM || !prevM->next || !prevM->next->next) return head;
auto &head2 = prevM->next, mNode = prevM->next;
auto &next = mNode->next, cur = mNode->next;
for (int i = m + 1; i <= n && cur; ++i) {
next = cur->next;
cur->next = head2;
head2 = cur;
cur = next;
}
return dummy.next;
}
一个更清晰的版本:就是把[m, n]节点一个个插入新链表。
ListNode *reverseBetween(ListNode *head, int m, int n) {
ListNode dummy(-1), *prevM = &dummy;
dummy.next = head;
for (int i = 0; i < m - 1 && prevM->next; ++i, prevM = prevM->next) ;
if (!prevM->next) return head;
auto &head2 = prevM->next, cur = prevM->next, start = prevM->next;
for (int i = m; i <= n && cur; ++i) {
auto next = cur->next;
cur->next = head2;
head2 = cur;
cur = next;
}
start->next = cur;
return dummy.next;
}
还是递归逻辑最清晰
ListNode *reverseBetween(ListNode *head, int m, int n) {
if (head == nullptr || m >= n) return head;
if (m > 1) {
head->next = reverseBetween(head->next, m - 1, n - 1);
return head;
}
ListNode *head2, *p = head;
for (; n > 0 && p; --n) {
auto next = p->next;
p->next = head2;
head2 = p;
p = next;
}
head->next = p;
return head2;
}