🧠 需要对链表节点进行分类,并且必须保持原有相对顺序的问题

一句话结论:
👉 双链表法用于 在算法中同时维护两条(或多条)逻辑链表 的问题。
双链表法不是链表类型,而是一种解题模型:
当题目需要对链表节点进行「筛选 / 分类 / 合并」,
并且要求保持原有顺序时,
就通过 dummy + tail 的尾插方式,所有的节点都是插在 tail 之后,然后把 tail 重新指向新的节点,最后把所有节点重新连接起来。,把节点重新组织成新链表。

⭐ 模板

创建 若干 dummy 头节点(每一类一个)
为每个 dummy 准备一个 tail 指针

curr 指向原链表头

while curr 不为空:
if curr 满足条件 A:
A_tail.next = curr
插入完需要移动尾部的指针
A_tail = A_tail.next
else:
B_tail.next = curr
B_tail = B_tail.next
curr = curr.next

断开所有尾部的 next(防止成环)

按题意顺序拼接各个子链表

返回第一个 dummy.next

在“重新拼一条新链表”,而不是“只改原链表的 next 关系”时,
建议在进入 while 循环后,分类前先保存 next 并断开当前节点:
ListNode next = curr.next;
curr.next = null;
最后拼接写 curr = next;
以防止原链表指针污染新链表结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public ListNode partition(ListNode head, int x) {

// 1️⃣ 两个头锚点(永远不动)
ListNode smallDummy = new ListNode(0);
ListNode bigDummy = new ListNode(0);

// 2️⃣ 两个尾指针(负责接)
ListNode small = smallDummy;
ListNode big = bigDummy;

// 3️⃣ 遍历指针(负责看)
ListNode curr = head;

while (curr != null) {
if (curr.val < x) {
small.next = curr;
small = small.next;
} else {
big.next = curr;
big = big.next;
}
curr = curr.next;
}

// 4️⃣ 断尾(防环)
big.next = null;

// 5️⃣ 拼接
small.next = bigDummy.next;

// 6️⃣ 返回真正的头
return smallDummy.next;
}
}


相关练习题

1. LeetCode 86 - 分隔链表

题意: 给定一个链表和一个值 x,将链表分成两部分,使所有 小于 x 的节点出现在 大于等于 x 的节点之前,且两部分内部的相对顺序保持不变。

2. LeetCode 328 - 奇偶链表

题意: 将链表按节点位置的奇偶性重新排列,使所有奇数位置节点在前,偶数位置节点在后,且保持各自的相对顺序不变。

3. LeetCode 21 - 合并两个有序链表

题意: 将两个升序链表合并为一个新的升序链表,并返回新链表的头节点。

双链表法解决的,是「需要分类,并且必须保持原有相对顺序」的链表问题。