题目要求
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
题解
https://github.com/soulmachine/leetcode
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode dummy(-1);
dummy.next=head;
ListNode *prev=&dummy;
for(int i=0;i<m-1;i++)
prev=prev->next;
ListNode *head2=prev; //要反转的部分的前一个元素
prev=head2->next; //要反转部分的第一个元素
ListNode *cur=prev->next;
for(int i=m;i<n;i++){
prev->next=cur->next;
cur->next=head2->next;
head2->next=cur; //头插法
cur=prev->next;
}
return dummy.next;
}
};
出于对于此代码的想象力有限,利用leetcode的playground我将整个步骤打印了一遍查看细节,供读者参考。
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode dummy(-1);
dummy.next=head;
ListNode *prev=&dummy;
for(int i=0;i<m-1;i++){
prev=prev->next;
cout << "i"<<" "<<i<<" "<< "prev" << " "<< prev->val << " ";
}
ListNode *head2=prev;
cout << "head2"<< " " << head2->val << " ";
prev=head2->next;
cout << "prev" << " "<< prev->val << " ";
ListNode *cur=prev->next;
cout << "cur" << " "<< cur->val << " "<<endl;
for(int i=m;i<n;i++){
string out = listNodeToString(head);
cout << "i"<<" "<<i <<"Listfromhead1"<< out << endl;
prev->next=cur->next;
cout << "i"<<" "<<i<<" "<< "prev" << " "<< prev->val << " "<< "prev->next" << " "<< prev->next->val<<endl;
out = listNodeToString(head);
cout << "i"<<" "<<i <<"Listfromhead2"<< out << endl;
cur->next=head2->next;
out = listNodeToString(head);
cout << "i"<<" "<<i <<"Listfromhead3"<< out << endl;
cout << "cur" << " "<< cur->val << " "<< "cur->next" << " "<< cur->next->val<<endl;
head2->next=cur; //头插法
out = listNodeToString(head);
cout << "i"<<" "<<i <<"Listfromhead4"<< out << endl;
cur=prev->next;
out = listNodeToString(head);
cout << "i"<<" "<<i <<"Listfromhead5"<< out << endl;
}
return dummy.next;
}
};
int main() {
string line;
while (getline(cin, line)) {
ListNode* head = stringToListNode(line);
getline(cin, line);
int m = stringToInteger(line);
getline(cin, line);
int n = stringToInteger(line);
ListNode* ret = Solution().reverseBetween(head, m, n);
string out = listNodeToString(ret);
cout << out << endl;
}
return 0;
}
Listfromhead分别对应执行完某一条语句后链表的状态,以下为打印结果。
i 0 prev 1 head2 1 prev 2 cur 3
i 2Listfromhead1[1, 2, 3, 4, 5]
i 2 prev 2 prev->next 4
i 2Listfromhead2[1, 2, 4, 5]
i 2Listfromhead3[1, 2, 4, 5]
cur 3 cur->next 2
i 2Listfromhead4[1, 3, 2, 4, 5]
i 2Listfromhead5[1, 3, 2, 4, 5]
i 3Listfromhead1[1, 3, 2, 4, 5]
i 3 prev 2 prev->next 5
i 3Listfromhead2[1, 3, 2, 5]
i 3Listfromhead3[1, 3, 2, 5]
cur 4 cur->next 3
i 3Listfromhead4[1, 4, 3, 2, 5]
i 3Listfromhead5[1, 4, 3, 2, 5]
[1, 4, 3, 2, 5]
大致总结下思路:
- 将head2定位到需要进行反转部分之前的位置,如本例中[2,3,4]前1的位置。
- 将prev指向需要进行反转部分的头部,如[2,3,4]中2的位置。
- 将cur指向prev后,如如[2,3,4]中3的位置。
- prev->next=cur->next,即将cur位置元素从链表中删除如[2,3,4]->[2,4]。状态对应Listfromhead2
- cur->next=head2->next,将cur链接向head2的下一个元素,即被反转区的第一个元素,如此时[2,4]的2,注意此时原链表并未改变,状态对应Listfromhead3
- head2->next=cur;,此时才将cur的元素插入到head2后面,亦即被改变区的首位,如[2,4]->[3,2,4]。状态对应Listfromhead4
- cur=prev->next,将当前指针移向改变后区间的下一位,如[3,2,4]由2->4,接下来将4的节点插到head2的后面,也就是被反转区的首位。
- 重复4~6,直到i=n