mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
935 字
2 分钟
链表:分隔链表
2026-05-11

题目概览#

范围#

来源内容
class012PartitionList

题型归纳#

题型: 链表分区

题目特征: 需要把小于 x 的节点放到前面,大于等于 x 的节点放到后面,并且保留每个分区内部的原始相对顺序。

解法: 准备两条链表,分别收集 < x>= x 的节点,最后把两条链表拼接起来。

易错点: 节点摘下后要断开 next,追加节点后要更新尾指针,小于区为空时要直接返回大于等于区。

看到”分区且保留相对顺序”就是稳定分区的信号——这里不是排序,而是稳定分区。核心做法是维护两条链表各自的头尾指针,遍历时把节点逐一摘下接入对应区,最后拼接;最容易出错的是摘节点时忘记断开 head.next,导致旧的 next 关系污染新链表结构。

例题:86. 分隔链表#

题目链接

题意#

给你一个链表的头节点 head 和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都出现在大于或等于 x 的节点之前,并且保留两个分区中每个节点的初始相对位置。

示例#

示例 1:

分隔前后对比:小于3的节点提前,内部顺序不变

输入: head = [1,4,3,2,5,2], x = 3

输出: [1,2,2,4,3,5]

示例 2:

输入: head = [2,1], x = 2

输出: [1,2]

提示#

  • 链表中节点的数目在范围 [0, 200]
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

解法#

核心思路#

这道题不是排序,而是稳定分区——>= x 的节点之间不会被重新排列,只是整体移到后面。

准备两条链表:left 收集所有 < x 的节点,right 收集所有 >= x 的节点。遍历原链表时,把每个节点先断开 next,再接到对应链表的尾部。遍历结束后把两条链表拼接即可。

4 -> 3 -> 5 不会变成 3 -> 4 -> 5,因为要求保留原始相对顺序,这是和排序最根本的区别。

双链表分区过程示意一

双链表分区过程示意二

核心模板#

public static ListNode partition(ListNode head, int x) {
ListNode leftHead = null, leftTail = null;
ListNode rightHead = null, rightTail = null;
while (head != null) {
ListNode next = head.next;
head.next = null;
if (head.val < x) {
if (leftHead == null) {
leftHead = head;
leftTail = head;
} else {
leftTail.next = head;
leftTail = head;
}
} else {
if (rightHead == null) {
rightHead = head;
rightTail = head;
} else {
rightTail.next = head;
rightTail = head;
}
}
head = next;
}
if (leftHead == null) {
return rightHead;
}
leftTail.next = rightHead;
return leftHead;
}

变量角色#

leftHeadrightHead 分别记住两条链表的头节点;leftTailrightTail 始终指向各自链表的尾节点,每接入一个新节点就跟着往后移动一位;next 临时保存原链表的下一个节点,在断开 head.next 之前必须先保存,否则后续节点会丢失。

过程拆解#

  1. 保存并断开:每轮先把 head.next 存入 next,再把 head.next 置为 null,把当前节点单独摘出来。
  2. 按值分流head.val < x 接入 left,否则接入 right;第一次接入时同时设置头尾指针,之后只更新尾指针。
  3. 推进遍历head = next 走到下一个节点。
  4. 收尾拼接leftHead == null 时直接返回 rightHead;否则把 leftTail.next 指向 rightHead,返回 leftHead

易错点#

  • 每次追加节点后必须更新尾指针(leftTail = head / rightTail = head),否则尾指针停在第一个节点,后续每次都覆盖同一个 next,前面的节点全部丢失。
  • leftHead == null 时直接返回 rightHead,不需要拼接。反过来 rightHead == null 时不需要特判,因为 leftTail.next = null 本身就是合法的。
摘节点时忘记断开 head.next

错误写法:只保存 next,没有把 head.next 置为 null

ListNode next = head.next;
// 漏写 head.next = null;
if (head.val < x) {
leftTail.next = head;
leftTail = head;
}
head = next;

为什么错:head 带着原来的 next 被接入新链表,它的 next 仍然指向原链表中的后续节点,新链表的结构会被旧连接污染,最终形成环或者串入不该出现的节点。

正确写法:

ListNode next = head.next;
head.next = null; // 先断干净,再接入新链表
口诀

保存 next,断开当前,按值接尾,最后 leftright

复杂度#

  • 时间复杂度:O(n)n 是链表长度
  • 空间复杂度:O(1),只使用有限几个指针
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

链表:分隔链表
https://github.com/algorithmzuo/algorithm-journey/tree/main/src/class012
作者
黎明
发布于
2026-05-11
许可协议
MIT

部分信息可能已经过时

目录