题目说明
https://leetcode-cn.com/problems/reverse-linked-list-ii/description/
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
解法1
首先遍历到m的前一个结点,由于m点可能处理第一个位置,所以需要创建虚拟头结点,指向m结点,然后反转[m,n]的结点。
反转结点:
1、记录当前点的下一个结点,防止下一点位置丢失
2、当前点的下一个结点指向前一个结点
3、前一个结点指向当前结点,为下一次反转作准备
4、当前结点指向下一个结点,即当前点后移
5、以上过程反复执行,即完成反转过程
注意:反转的第一个点(即m结点)的下一个结点需要指向最后一点(即n结点)的原来的下一结点,完成衔接。
/*
* 时间复杂度:O(n)
* 对于m=1的情况需要作特殊处理。
*/
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode *pm_pre = head;
//记录m前一个结点
for (int i = 1; i < m - 1; i ++){
pm_pre = pm_pre->next;
}
//创建虚拟头结点,其下一个结点为m位置结点
ListNode *newhead = new ListNode(0);
if (m == 1)
newhead->next = pm_pre;
else
newhead->next = pm_pre->next;
//反转m至n的结点
ListNode *pre = newhead;
ListNode *cur = newhead->next;
ListNode *next = NULL;
ListNode *temp = cur;//m结点位置
for (int i = m; i <= n; i ++){
if (cur == NULL)
break;
next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
//将m结点的next指向原来n的下一个结点
temp->next = cur;
if (m == 1)
{
//若m==1,即n结点即头结点
head = pre;
}else{
//将m前一个结点的next指向反转后的第一个结点(即n结点)
pm_pre->next = pre;
}
delete newhead;
return head;
}
解法2
以上方法还是过于冗余了,下面这种方法,是在链表头结点之前建立虚拟头结点。
遍历到m点之前,记录m前一点pre。
很巧妙的是,反转过程中,使用pre->next来记录当前点,即下一点的前一点。但对于m点,指向自身;对于(m,n]指向其前一点。这样就完成了对于m结点的特殊处理。
最后再将m点下一结点指向n的下一结点。完成衔接。
/*
* 时间复杂度:O(n)
* 创建虚拟头结点
* 先将m的下一点指向自身,最后再处理m的下一个结点
*/
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode *dummy = new ListNode(0);
dummy->next = head;
ListNode *pre = dummy;
//m点前一点pre
for (int i = 1; i <=m - 1; i ++){
pre = pre->next;
}
ListNode *cur = pre->next;
ListNode *pm = cur;
ListNode *next = NULL;
for (int i = m; i <= n; i ++){
next = cur->next;//记录当前点的下一点
cur->next = pre->next;//对于m点,指向自身;对于(m,n]指向其前一点
pre->next = cur;//记录当前点,即下一点的前一点
cur = next;//当前点移动到下一点
}
pm->next = cur;//m点下一结点指向n的下一结点
return dummy->next;//dummy点始终未变
}