划分链表


// 给你一个链表的头节点 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]

✅ 六、进阶拓展建议

想挑战的话可以试试:

  1. 保留原链表结构复用而不是 new 新节点

  2. 实现一个三段链表划分:小于 / 等于 / 大于某个值

  3. 配合 Java 单元测试验证链表顺序

posted @ 2025-07-17 14:25  ji415  阅读(8)  评论(0)    收藏  举报