一、理解链表结构
假设链表节点定义为:
class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
二、迭代法反转链表
核心思路
逐步反转每个节点的指针方向,最终使整个链表反向。
步骤拆解
-
初始化三个指针:
-
prev
:指向已反转部分的头节点(初始为null
)。 -
current
:指向待反转的当前节点(初始为头节点)。 -
next
:临时保存current.next
,防止断链。
-
-
循环操作:
-
保存
current
的下一个节点:next = current.next
。 -
反转指针:
current.next = prev
。 -
移动
prev
和current
:prev = current
,current = next
。
-
-
终止条件 :当
current
为null
时,prev
就是新链表的头节点。
代码实现
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode current = head;
while (current != null) {
ListNode next = current.next; // 保存下一个节点
current.next = prev; // 反转指针
prev = current; // prev 前移
current = next; // current 前移
}
return prev; // 新头节点
}
示意图
初始状态:1 -> 2 -> 3 -> null
反转过程:
Step 1: prev=null, current=1 → 1.next=null → prev=1, current=2
Step 2: prev=1, current=2 → 2.next=1 → prev=2, current=3
Step 3: prev=2, current=3 → 3.next=2 → prev=3, current=null
最终结果:3 -> 2 -> 1 -> null
三、递归法反转链表
核心思路
假设头节点后的子链表已反转,只需处理头节点与子链表的关系。
步骤拆解
-
终止条件:链表为空或只有一个节点,直接返回头节点。
-
递归反转子链表 :
newHead = reverseList(head.next)
。 -
调整指针:
-
将头节点的下一个节点的
next
指向自己:head.next.next = head
。 -
断开原头节点的
next
指针:head.next = null
。
-
代码实现
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head; // 终止条件
}
ListNode newHead = reverseList(head.next); // 递归反转子链表
head.next.next = head; // 反转指针
head.next = null; // 断开原指针
return newHead; // 始终返回新头节点
}
示意图
初始链表:1 -> 2 -> 3 -> null
递归过程:
递归到最深层:处理节点3 → 返回3
回归时处理节点2:2.next.next=2 → 3 -> 2,断开2.next → 3 -> 2 -> null
回归时处理节点1:1.next.next=1 → 2 -> 1,断开1.next → 3 -> 2 -> 1 -> null
四、分组反转链表
问题分析
目标:每 k 个节点为一组 反转链表,剩余不足 k 个的节点保持原顺序。
示例:
-
原链表:
1 → 2 → 3 → 4 → 5
,k=2
→ 反转后:2 → 1 → 4 → 3 → 5
-
原链表:
1 → 2 → 3 → 4 → 5
,k=3
→ 反转后:3 → 2 → 1 → 4 → 5
核心思路
-
分段处理:将链表按 k 个一组切割,逐组反转。
-
边界控制:
-
检查剩余节点是否足够 k 个。
-
反转后正确连接各组头尾节点。
-
-
递归或迭代:两种方法均可实现,迭代法更直观。
迭代法实现
步骤拆解
-
辅助节点 :创建虚拟头节点
dummy
,简化头节点处理。 -
指针定义:
-
pre
:指向当前组的前一组的尾节点(初始为dummy
)。 -
start
:当前组的起始节点。 -
end
:当前组的结束节点。
-
-
循环处理:
-
定位每组起点
start
和终点end
。 -
若剩余节点不足 k 个,直接结束。
-
反转当前组,并调整
pre
、start
、end
的指针。
-
代码实现
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || k == 1) return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy; // 前一组反转后的尾节点
ListNode start = head; // 当前组的起始节点
while (start != null) {
ListNode end = pre; // 定位当前组的 end 节点
for (int i = 0; i < k; i++) {
end = end.next;
if (end == null) return dummy.next; // 不足 k 个,直接返回
}
// 保存下一组的起始节点,并断开当前组
ListNode nextGroup = end.next;
end.next = null;
// 反转当前组,并连接前一组的尾节点
pre.next = reverse(start); // pre → 新头节点
start.next = nextGroup; // 新尾节点 → 下一组
// 移动 pre 和 start 到下一组
pre = start;
start = nextGroup;
}
return dummy.next;
}
// 反转单链表(基础迭代法)
private ListNode reverse(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
递归法实现
核心思路
-
递归反转每组:先反转前 k 个节点,再递归处理剩余链表。
-
边界检查:剩余节点不足 k 个时直接返回原链表。
代码实现
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || k == 1) return head;
// 检查剩余节点是否足够 k 个
ListNode curr = head;
int count = 0;
while (curr != null && count < k) {
curr = curr.next;
count++;
}
if (count < k) return head; // 不足 k 个,不反转
// 反转前 k 个节点
ListNode newHead = reverse(head, k);
// 递归处理剩余链表,并连接已反转的部分
head.next = reverseKGroup(curr, k);
return newHead;
}
// 反转前 k 个节点
private ListNode reverse(ListNode head, int k) {
ListNode prev = null;
ListNode curr = head;
while (k-- > 0) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
关键边界条件
-
链表长度不足 k:直接返回原链表。
-
k=1:无需反转。
-
头尾连接:反转后需要将前一组的尾节点连接到当前组的新头节点,当前组的尾节点连接到下一组的头节点。
代码对比
我自己对于迭代法进行优化,减少了一个linknode start。
java
public static ListNode reverseKGroup(ListNode head, int k) {
if (head==null || k==1)return head;
ListNode dummy = new ListNode(0);
ListNode prev = dummy; // 用来记录每个分组的前节点,用于连结分组,赋值end移动等
dummy.next = head; // 初始化虚拟头节点
while (head!=null){ ///
ListNode end=prev; // end节点 反转的最后一个节点
for (int i = 0; i < k; i++) {
end=end.next;
if (end==null)return dummy.next; // 保持返回链表的最初头节点
}
ListNode next = end.next; // 用于保存下一组起始
end.next=null;
prev.next=reverse(head);// pre--> 新头节点 head用来反转的头节点,prev作为上一组来连结
head.next=next; // 新尾点--> 下一组
// 对pre重新赋值
prev=head; // 上一组的尾节点
head=next; //下一组的头节点 或者说往下移一步
}
return dummy.next;
}
public static ListNode reverse(ListNode head) {
ListNode prev=null;
ListNode curr=head;
ListNode next=head.next;
while(curr!=null){
next=curr.next;
curr.next=prev;
prev=curr;
curr=next;
}
return prev;
}
力扣上官方代码
java
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode hair = new ListNode(0);
hair.next = head;
ListNode pre = hair;
while (head != null) {
ListNode tail = pre;
// 查看剩余部分长度是否大于等于 k
for (int i = 0; i < k; ++i) {
tail = tail.next;
if (tail == null) {
return hair.next;
}
}
ListNode nex = tail.next;
ListNode[] reverse = myReverse(head, tail);
head = reverse[0];
tail = reverse[1];
// 把子链表重新接回原链表
pre.next = head;
tail.next = nex;
pre = tail;
head = tail.next;
}
return hair.next;
}
public ListNode[] myReverse(ListNode head, ListNode tail) {
ListNode prev = tail.next;
ListNode p = head;
while (prev != tail) {
ListNode nex = p.next;
p.next = prev;
prev = p;
p = nex;
}
return new ListNode[]{tail, head};
}
}