找链表中点:使用快慢双指针(快指针一次走两步,慢指针一次走一步),当快指针到达末尾时,慢指针正好停在中间(奇数个节点在正中间,偶数个节点在前半段末尾)。
后半部分逆序 :将慢指针后面的链表截断,并使用头插法进行逆序反转。
交替合并:将前半段链表和反转后的后半段链表,使用双指针交替一个接一个地拼接起来。
知识点一:快慢指针找中间结
- 核心操作:慢指针每次走1步,快指针每次走2步。当快指针到达末尾时,慢指针停在前半段最后一个节点。
- 本题细节 :
- 奇偶长度处理:奇数个节点时慢指针在正中间,偶数个节点时慢指针在前半段末尾。
- 循环条件 :必须使用
fast != null && fast.next != null,防止空指针异常。 - 截断链表 :找到中点后,必须用
slow.next = null将前后半段链表彻底断开。
知识点二:头插法逆序链表
- 核心操作:创建虚拟头结点,遍历原链表,将每个节点依次插入到虚拟头结点之后,实现局部反转。
- 本题细节 :
- 保存后继 :遍历断链前,必须用
next = cur.next提前保存下一个节点。 - 反转基准 :以
slow.next作为后半段起点进行反转,原fast指针此时已失效不可再用。 - 虚拟头结点 :使用虚拟头结点(如
new ListNode(0))可以省去判断头结点为空的额外逻辑。
- 保存后继 :遍历断链前,必须用
知识点三:尾插法合并两个链表
- 核心操作:使用双指针分别指向两个链表,通过另一个指针(prev)始终指向合并链表的尾部,交替将节点接在尾部。
- 本题细节 :
- 长度差异 :前半段链表比后半段长(多1个或0个节点),合并时以前半段为准,需加
if(cur2 != null)判断后半段是否结束。 - 指针后移 :接上节点后,必须同步移动
prev和对应的cur指针。 - 虚拟头结点 :同样使用虚拟头结点简化合并逻辑,最后无需返回(原题要求
void)。
- 长度差异 :前半段链表比后半段长(多1个或0个节点),合并时以前半段为准,需加
附:重排链表核心代码骨架
// 1. 找中间结点
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 2. 头插法逆序后半段
ListNode head2 = new ListNode(0);
ListNode cur = slow.next;
slow.next = null; // 断开链表
while (cur != null) {
ListNode next = cur.next;
cur.next = head2.next;
head2.next = cur;
cur = next;
}
// 3. 尾插法交替合并
ListNode cur1 = head, cur2 = head2.next;
ListNode ret = new ListNode(0);
ListNode prev = ret;
while (cur1 != null) {
prev.next = cur1; // 先接前半段
prev = cur1;
cur1 = cur1.next;
if (cur2 != null) { // 再接后半段
prev.next = cur2;
prev = cur2;
cur2 = cur2.next;
}
}