【反转链表系列】力扣206,92,25
文章目录
- 一、前言
- 二、反转链表
- 三、反转链表 II
- 四、K 个一组翻转链表
一、前言
本文涉及的力扣题目,涉及简单、中等、困难三个难度
- 力扣206.反转链表
- 力扣92.反转链表 II
- 力扣25.K 个一组翻转链表
反转链表这类型的题目,折磨了挺久,经常弄懂了一道题,但是遇到下一道反转链表的题目还是不会。
所以我整理了三道比较经典的反转链表的题目,从浅到深一步一步领悟反转链表的处理思路。
二、反转链表
力扣206.反转链表
这是一道力扣上的简单
难度的反转链表的题目,要求我们将给定的链表进行反转。
这道题解题思路也非常简单,就是将指针反转即可,思路虽然简单,但是编写代码的时候却很容易乱。
我们思考一个问题,反转一个链表,肯定是每两个节点之间的指针先进行反转,然后遍历下来就可以了。
那么需要多少个节点指针呢?是的,需要三个!
pre
:表示前一个结点cur
:表示当前节点next
:表示下一个节点
为什么需要
next
指针呢?答:是因为当
pre
和cur
之间的指针反转后,如果没有next
指针记录cur
的下一个节点,反转后就无法找到cur
的下一个节点
如下图所示,第一次反转之前先记录cur
的下一个节点
那么循环中第一次反转,cur
的下一个节点指向pre
反转后,cur
所指向的节点就是下一次反转的pre
,next
指针所指向的节点,就是下一次的当前节点,next
也要进行更新。
于是第一次反转后链表变成下图所示
同理,进行第二次反转
第三次反转
第四次反转
第五次反转
当cur
为null
的时候,说明链表已经反转完成,此刻pre
就是链表的头部!
要非常熟悉这个反转整个链表的流程,因为后面两道题反转链表的流程和这道题差不多,只是不是处理一整条链表而已!
记住这个结论:当链表反转完成后,
pre
就是链表的头部。后面会是使用到这个结论
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
ListNode next = null;
while(cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
- 时间复杂度:O(n),n为链表长度
- 空间复杂度:O(1)
相信看到这里,你已经完全掌握了链表反转的方法,后面会一直沿用这个思路去解决问题!
三、反转链表 II
力扣92.反转链表 II
反转链表 II和上一题的区别就是不是反转一整个链表,而是反转从位置 left
到位置 right
的链表节点
那么我们就必须先找到位置位于left - 1
上的节点p0
,然后对[left,right]
位置上的链表节点进行反转即可,反转的步骤和上面的反转链表
的思路一样,然后再把反转后的链表连接起来即可!
为什么要找位于
left - 1
上的节点?答:因为题目要求反转
[left,right]
上的节点,那么原链表就被划分成了三部分,分别是原左链表、需要反转的链表、原右链表。找到位于left - 1
位置上的节点是为了记录原左链表的最后一个节点的指针,方便反转后链接链表使用
反转[left,right]
位置上之后,就会如下图所示,这时候需要将p0.next.next
指向cur
和p0.next
指向pre
,这样就完成了链表的部分反转。
这里需要注意,假如链表的长度是1的话,那么就不会存在就不会有位于
left - 1
上的节点,于是我们需要定义一个哨兵节点,用作指向这个链表的头节点
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
head = new ListNode(-1, head);
ListNode cur = head;
ListNode pre = null;
for(int i = 0; i < left - 1 ; i ++){
cur = cur.next;
}
ListNode p0 = cur;
cur = cur.next;
for(int i = left; i <= right; i ++){
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
p0.next.next = cur;
p0.next = pre;
return head.next;
}
}
- 时间复杂度:O(n),n为链表长度
- 空间复杂度:O(1)
四、K 个一组翻转链表
力扣25.K 个一组翻转链表
这道题结合了上面两道题,需要对链表每K个为一组,进行翻转。
既然是分组,那么我们就需要先计算链表的长度,然后看看链表能分成多少组,记录一个组数。
然后根据反转链表 II
的做法,进行一组一组反转即可,每次反转完一组后,需要更新p0
的所在位置
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
// 计算链表的长度
int count = 0;
ListNode cur = head;
while (cur != null) {
count++;
cur = cur.next;
}
// 处理的次数
int n = count / k;
head = new ListNode(-1, head);
ListNode p0 = head;
ListNode pre = null;
ListNode next = null;
cur = head.next;
while (n-- > 0) {
for (int i = 0; i < k; i++) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
ListNode nxt = p0.next;
p0.next.next = next;
p0.next = pre;
p0 = nxt;
}
return head.next;
}
}