实现要求:给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
例如下图:
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]
例示2:
输入:head = [5], left = 1, right = 1 输出:[5]
反转链表问题的实现核心是利用 头插法 实现单链表来实现链表的逆置。
实现代码:
struct ListNode* reverseBetween(struct ListNode* head, int left, int right) {
if(head==NULL||left==right){ //当链表为空或者只包含一个元素的时候直接返回链表
return head;
}
struct ListNode dummy; //定义哑结点,对于哑结点的定义我已经在前面的反转字符串问题文章中有过介绍
dummy.next=head;
struct ListNode *pre=&dummy;
// 定位到left的前躯结点,实现头插法的关键步骤之一就是找到要插入所要插入元素的头插结点,由于链表的独特构造因此要想找到前驱结点只能逐个遍历
for(int i=0;i<left-1;i++){
pre=pre->next;
}
//pre是要插入位置的前驱结点,cur是当前结点是位于所要进行插入操作元素的前驱结点,nex是指当前要插入的结点
struct ListNode *cur=pre->next;
//头插法的实现过程
for(int i=0;i<right-left;i++){
struct ListNode *nex=cur->next; //更新nex结点,指向要插入的元素
cur->next=nex->next;
nex->next=pre->next;
pre->next=nex;
}
//这里有一个易错点: 对于头插法 nex->next=pre->next 这句代码是否可以换成nex->next=cur呢?答案是不能,因为cur和·pre->next 在循环中不总是相等的。
例如:链表为1->2->3->4->5,反转区间为left=2 ,right=4(即反转2->3->4)
循环之前的cur是2,pre是1,nex是3(所在位置)
第一次循环,就是把3插入1,2之间,链表变为1->3->2->4->5,此时pre->next已更新为3,但是cur依然为2,要是再进行第二次循环,那么要把4,插入1,3之间,要是代码为nex->next=cur,会导致结点3的消失,链表变为:1->4->2->5。因此代码的逻辑漏洞主要在于没有考虑到cur在后续循环中是保持不变的,只是考虑了第一次循环cur=pre->next的特殊情况。
return dummy.next; //返回逆转后的链表
}