题解 | #链表内指定区间反转#
链表内指定区间反转
https://www.nowcoder.com/practice/b58434e200a648c589ca2063f1faf58c
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类
* @param m int整型
* @param n int整型
* @return ListNode类
*/
ListNode* reverseBetween(ListNode* head, int m, int n) {
// write code here
if(!head || m==n) //无需反转,直接返回。
return head;
int count=1; //指针向后移动时自增,标识当前是第几个结点。
ListNode* loop=head; //loop:遍历链表结点的指针。
//first_end:指向翻转区域前一个结点,为了第一部分和翻转部分拼接。
// mid_end:指向翻转区域翻转前的第一个结点,也是翻转后的最后一个结点,为了翻转部分和后面部分拼接。
ListNode* first_end, *mid_end, *tmp;
struct ListNode mid_head ={0};
if(m!=1){
//while循环结束后loop指向翻转区域前一个结点
while(count!=m-1){
loop=loop->next;
count++;
}
first_end=loop; //保存翻转区域前一个结点的位置,也是第一部分最后一个结点
loop=loop->next; //loop指向翻转区域第一个结点
mid_end = loop; //保存翻转区域最后一个结点的位置
count++;
}
//使用头插法将翻转区域的结点依次插入一个子链表中,退出while循环时,tmp指向了翻转区域后(第三部分)的第一个结点
while(count<=n){
tmp = loop->next;
loop->next = mid_head.next;
mid_head.next = loop;
loop=tmp;
count++;
}
//拼接输出:第一部分未翻转 -> 翻转后子链 -> 第三部分未翻转
if(m==1){
head->next = tmp;
head = mid_head.next;
return head;
}
first_end->next = mid_head.next;
mid_end->next = tmp;
return head;
}
};
问题分析
题目要求元素翻转,想到头插法;又要求特定区间元素,因此考虑使用while循环移动指针,找到目标区间的第一个元素。
具体思路(结合代码)
当给定链表为空,以及m=n时,无需操作,函数输出即是输入。
m<=n时,有两种不同情况:
m=1,从链表第1个元素开始翻转,这样输入链表的第1个元素是返回链表的第n个元素,第n个元素是返回链表的第1个元素;
m>1,输入链表的前m-1个元素保持不变,这时需要一个指针(frst_end)指向第m-1个元素,方便其指向翻转区域链表的第1个元素,使第1部分未翻转的子链 和 第2段已翻转子链 连接起来。
以上并没考虑翻转区域是否涵盖到链表的最后一个元素,因为无论后面是空指针NULL还是链表结点,只要让翻转后链表最后一个元素指向遍历结束后的tmp就行了。
复杂度分析
时间复杂度:最坏情况下对所有元素进行翻转,时间复杂度和链表元素长度为线性关系,为O(n)。
空间复杂度:只创建了一个ListNode结点作为尾插链表的非值头结点,为O(1)。

