【LeetCode】86. 分隔链表
解题思路
本问题要求将链表按给定值 x
分割为两部分,所有小于 x
的节点必须出现在大于或等于 x
的节点之前,且需保留原始相对顺序。核心思路是通过 双链表合并法 实现:
- 构建两个子链表:分别存储小于
x
和大于等于x
的节点。 - 遍历原链表:根据节点值与
x
的关系,将节点追加到对应子链表。 - 合并链表:将小于
x
的子链表尾部连接到大于等于x
的子链表头部,并断开可能的循环引用。
关键步骤
- 虚拟头节点:简化链表操作,避免处理头节点为空的边界情况。
- 双指针维护:
lessTail
和greaterTail
分别跟踪两个子链表的尾部,实现 O(1) 时间复杂度的追加操作。 - 循环遍历:遍历原链表,根据值大小分配节点到子链表,保持相对顺序。
- 尾部处理:合并前需将
greaterTail.Next
置空,防止原链表中残留指针引发循环。
复杂度分析
- 时间复杂度:O(n),仅需一次线性遍历链表。
- 空间复杂度:O(1),仅使用固定数量的指针变量。
实现代码
type ListNode struct { Val int Next *ListNode } func partition(head *ListNode, x int) *ListNode { // 创建两个虚拟头节点,用于分别构建小于x和大于等于x的链表 lessHead := &ListNode{} greaterHead := &ListNode{} lessTail := lessHead // 跟踪小于x链表的尾部 greaterTail := greaterHead // 跟踪大于等于x链表的尾部 // 遍历原链表,分配节点到对应子链表 for head != nil { if head.Val < x { lessTail.Next = head lessTail = lessTail.Next } else { greaterTail.Next = head greaterTail = greaterTail.Next } head = head.Next // 移动到下一个节点 } // 处理大于等于链表的尾部,断开可能的循环引用 greaterTail.Next = nil // 合并两个链表 lessTail.Next = greaterHead.Next // 返回合并后的头节点 return lessHead.Next }
代码注释说明
- 虚拟头节点:
lessHead
和greaterHead
作为哨兵节点,简化链表操作。 - 尾部指针维护:通过
lessTail
和greaterTail
动态更新子链表的尾部,确保追加操作高效。 - 循环遍历:将每个节点按值分配到对应的子链表,保持原链表中的相对顺序。
- 合并与断开循环:合并前需将
greaterTail.Next
置空,避免原链表残留指针导致错误循环。
运行示例
// 辅助函数: 打印链表 func printList(head *ListNode) { for head != nil { fmt.Printf("%d->", head.Val) head = head.Next } fmt.Println("nil") } func main() { // 构建输入链表:1 -> 4 -> 3 -> 2 -> 5 -> 2 l1 := &ListNode{Val: 1} l1.Next = &ListNode{Val: 4} l1.Next.Next = &ListNode{Val: 3} l1.Next.Next.Next = &ListNode{Val: 2} l1.Next.Next.Next.Next = &ListNode{Val: 5} l1.Next.Next.Next.Next.Next = &ListNode{Val: 2} printList(partition(l1, 3)) // 输出:1->2->2->4->3->5->nil // 输入链表:2 -> 1 l2 := &ListNode{Val: 2} l2.Next = &ListNode{Val: 1} printList(partition(l2, 2)) // 输出:1->2->nil }