题目描述:(来源力扣92)
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
先说一下,这道题自己做的过程,在这道题之前做了类似的反转链表(力扣206),那道题是反转整个链表,比较简单。这里就只贴出代码了:
力扣206:反转链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
while (cur != nullptr){//反转整个链表
ListNode* next = cur->next;
cur->next=pre;
pre=cur;
cur=next;
}
return pre;
}
};
反转局部链表的思路很简单,思路如下:
1)遍历链表,当遍历到m点位置,开始反转链表;
2)当遍历到n点位置,结束反转,并返回新的头结点;
但是实现的时候,就出现了很多不兼容的状况,比如:
1)m==n时,这时候不需要反转,直接返回就可以了;
2)m==1,即m是头结点;
3)链表为空;
尝试结果:
具体分析:
1)在到达m点之前,即m-1点,会使用left
指针记录m点的前一个节点。如果,在反转结束我们需要将1点连接到4点。
2)在到达m点,使用right
指针记录反转部分链表的尾节点,这样最后才能找到2点和5点进行连接。
具体实现:
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head== nullptr){
return head;
}
ListNode* dummyhead= new ListNode(-1);//设立虚拟头结点
dummyhead->next=head;
ListNode* pre= dummyhead;
ListNode* cur= head;
int i=0;
ListNode* left= nullptr;
ListNode* right=nullptr;
while (cur!=nullptr){
if(i==m-1)
left=pre;//记录left指针
if(i==m)
right=pre;//记录right指针
while (i>=m&&i<n){//到达m点时,反转链表开始
ListNode* next=cur->next;
cur->next=pre;
pre=cur;
cur=next;
++i;
}
if(i==n){//到达n点,反转链表结束
left->next=pre;
right->next=cur;
break;
}
//如果没有到达m点之前,即没有开始反转时
//每次只向后遍历
pre=cur;
cur=cur->next;
++i;
}
return dummyhead->next;
}
};
官方实现:
官方的实现更加体现了一次遍历的过程,清晰易懂:
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head== nullptr){
return head;
}
ListNode* pre= nullptr;
ListNode* cur= head;
while (m>1){
pre=cur;
cur=cur->next;
m--;
n--;
}
ListNode* left=pre;
ListNode* right=cur;
ListNode* next= nullptr;
while (n>0){
next=cur->next;
cur->next=pre;
pre=cur;
cur=next;
n--;
}
if(left!=nullptr)
left->next = pre;
else
head=pre;
right->next=cur;
return head;
}
};