划分链表
// 给你一个链表的头节点 head 和一个特定值 x
// 请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
// 你应当 保留 两个分区中每个节点的初始相对位置
// 测试链接 : https://leetcode.cn/problems/partition-list/
public class PartitionList {
// 不要提交这个类
public static class ListNode {
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
class Solution {
public static ListNode partition(ListNode head, int x) {
ListNode leftHead = null, leftTail = null; // < x的区域
ListNode rightHead = null, rightTail = null; // >=x的区域
ListNode next = null;
while (head != null) {
next = head.next;
head.next = null;
if (head.val < x) {
if (leftHead == null) {
leftHead = head;
} else {
leftTail.next = head;
}
leftTail = head;
} else {
if (rightHead == null) {
rightHead = head;
} else {
rightTail.next = head;
}
rightTail = head;
}
head = next;
}
if (leftHead == null) {
return rightHead;
}
// < x的区域有内容!
leftTail.next = rightHead;
return leftHead;
}
}
}
这段代码实现的是链表的划分 partition 操作,它的目标与 LeetCode 第 86 题 Partition List 完全一致:
✅ 一、问题描述:链表划分
给定一个链表和一个整数
x,将链表重新排列,使得:
所有小于
x的节点出现在所有大于等于x的节点之前。且两个区域内部节点的相对顺序不变。
✅ 二、整体思路(核心思想)
将链表分为两个子链:
-
一个子链装
< x的节点,记作 left -
一个子链装
>= x的节点,记作 right
遍历原链表,将节点分类插入这两个子链,最后将两个子链连接起来即可。
这种思路叫做 "链表分区 + 拼接",类似于数组的双指针 partition,但是是链表版本。
✅ 三、代码逐步讲解
public static ListNode partition(ListNode head, int x)
这个方法接受:
-
head: 原始链表头 -
x: 划分的基准值
🧱 变量定义
ListNode leftHead = null, leftTail = null; // 存放 < x 的链表头和尾
ListNode rightHead = null, rightTail = null; // 存放 >= x 的链表头和尾
你要动态构建两个子链:left 与 right,分别记录头和尾,便于连接。
ListNode next = null;
临时变量,保存原链表的下一个节点,防止断链。
🔁 遍历原链表并划分
while (head != null) {
next = head.next; // 保存下一个节点
head.next = null; // 断链,避免旧链残留
▶ 分类逻辑
if (head.val < x) {
if (leftHead == null) {
leftHead = head;
} else {
leftTail.next = head;
}
leftTail = head;
}
-
如果当前值 < x,就插入 left 区域。
-
首次插入初始化头指针,后续用
leftTail.next插入。
else {
if (rightHead == null) {
rightHead = head;
} else {
rightTail.next = head;
}
rightTail = head;
}
- 如果当前值 ≥ x,就插入 right 区域。
最后移动原链表的指针:
head = next;
🔗 拼接两个链表
if (leftHead == null) {
return rightHead;
}
leftTail.next = rightHead;
return leftHead;
-
如果
< x的区域为空,就直接返回rightHead。 -
否则将
leftTail.next = rightHead接上,返回leftHead即新链表头。
📌 举个例子模拟一下
输入链表:
head = 1 -> 4 -> 3 -> 2 -> 5 -> 2, x = 3
执行过程:
-
小于 3:
1 -> 2 -> 2 -
大于等于 3:
4 -> 3 -> 5
拼接后:
1 -> 2 -> 2 -> 4 -> 3 -> 5
✅ 四、总结这段代码的亮点
| 特性 | 说明 |
|---|---|
| 🎯 精准分类 | 按值 < x 或 ≥ x 分到两个链 |
| 🔗 拼接巧妙 | 使用 tail 指针轻松拼接两链 |
| ⚡ 不新建节点 | 全部是原地操作,空间复杂度 O(1) |
| 📦 顺序保留 | 保证两个区域内部的原始顺序不变 |
| 📐 健壮性处理 | 考虑 left 区域为空的特殊情况 |
✅ 五、整体图示结构变化
初始链表:
head → [1] → [4] → [3] → [2] → [5] → [2]
分成两部分:
leftHead → [1] → [2] → [2]
rightHead → [4] → [3] → [5]
连接后:
leftHead → [1] → [2] → [2] → [4] → [3] → [5]
✅ 六、进阶拓展建议
想挑战的话可以试试:
-
保留原链表结构复用而不是 new 新节点
-
实现一个三段链表划分:小于 / 等于 / 大于某个值
-
配合 Java 单元测试验证链表顺序

浙公网安备 33010602011771号