面试经典150题[066]:分隔链表(LeetCode 86)

分隔链表(LeetCode 86)

题目链接:分隔链表(LeetCode 86)

难度:中等

1. 题目描述

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

示例:

复制代码
输入: head = [1,4,3,2,5,2], x = 3
输出: [1,2,2,4,3,5]

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

2. 问题分析

2.1 规律

  • 这是一个链表的划分问题,需要将节点根据其值与 x 的关系分成两部分。
  • 关键要求是保持每个分区内部节点的 原始相对顺序。这意味着我们不能简单地通过交换节点值来解决,而需要移动节点。

2.2 双链表(双指针)思路

最直观且高效的方法是创建两个新的链表,一个用来存放所有小于 x 的节点,另一个用来存放所有大于或等于 x 的节点。

  • 创建哑节点(Dummy Nodes)

    • less_head:作为 "小于 x" 链表的头节点。
    • greater_head:作为 "大于等于 x" 链表的头节点。
    • 使用哑节点可以简化边界条件的处理,避免对头节点的特殊判断。
  • 创建遍历指针

    • less_ptr:指向 "小于 x" 链表的尾部。
    • greater_ptr:指向 "大于等于 x" 链表的尾部。
  • 遍历原链表

    • 用一个指针 current 遍历原链表 head
    • 对于每个节点 current
      • 如果 current.val < x,则将其链接到 less_ptr 的后面,并移动 less_ptr
      • 如果 current.val >= x,则将其链接到 greater_ptr 的后面,并移动 greater_ptr
  • 拼接链表

    • 遍历结束后,我们得到了两个独立的链表。
    • 将 "小于" 链表的尾部 (less_ptr) 指向 "大于等于" 链表的头部 (greater_head.next)。
    • 重要 :将 "大于等于" 链表的尾部 (greater_ptr) 的 next 指针设为 None,以防止产生环。
    • 返回 less_head.next作为新链表的头节点。

3. 代码实现

Python

python 复制代码
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        # 创建两个哑节点,分别作为两个新链表的头
        less_head = ListNode(-1)
        greater_head = ListNode(-1)
        
        # 创建两个指针,用于构建新链表
        less_ptr = less_head
        greater_ptr = greater_head
        
        current = head
        while current:
            if current.val < x:
                # 将节点加入到 'less' 链表
                less_ptr.next = current
                less_ptr = less_ptr.next
            else:
                # 将节点加入到 'greater' 链表
                greater_ptr.next = current
                greater_ptr = greater_ptr.next
            
            # 移动到下一个节点
            current = current.next
        
        # 将 'greater' 链表的末尾指向 None,断开与原链表的连接
        greater_ptr.next = None
        
        # 将 'less' 链表的末尾连接到 'greater' 链表的头部
        less_ptr.next = greater_head.next
        
        return less_head.next

C++

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        // 创建哑节点
        ListNode* less_head = new ListNode(-1);
        ListNode* greater_head = new ListNode(-1);
        
        // 创建构建指针
        ListNode* less_ptr = less_head;
        ListNode* greater_ptr = greater_head;
        
        ListNode* current = head;
        while (current != nullptr) {
            if (current->val < x) {
                less_ptr->next = current;
                less_ptr = less_ptr->next;
            } else {
                greater_ptr->next = current;
                greater_ptr = greater_ptr->next;
            }
            current = current->next;
        }
        
        // 断开 'greater' 链表的尾部
        greater_ptr->next = nullptr;
        
        // 拼接两个链表
        less_ptr->next = greater_head->next;
        
        // 释放哑节点内存(可选,但好习惯)
        ListNode* new_head = less_head->next;
        delete less_head;
        delete greater_head;
        
        return new_head;
    }
};

4. 复杂度分析

  • 时间复杂度:O(n),其中 n 是链表的节点数。我们只需要遍历一次原链表。
  • 空间复杂度 :O(1),我们只使用了常数个额外指针(less_head, greater_head, less_ptr, greater_ptr),并没有创建新的节点,只是重新组织了原链表的节点。

5. 总结

  • 链表划分 问题,特别是要求保持相对顺序时,双链表(或双指针+哑节点) 方法是一个非常经典且有效的解决方案。
  • 使用哑节点可以极大地简化代码逻辑,避免对头节点的空指针判断和特殊处理。
  • 最后一定要记得处理好两个链表的拼接以及新链表尾部的 next 指针(设置为 None/nullptr)。

复习

面试经典150题[006]:旋转数组(LeetCode 189)

面试经典150题[036]:旋转图像(LeetCode 48)

面试经典150题[051]:用最少数量的箭引爆气球(LeetCode 452)

相关推荐
少睡点觉2 小时前
LeetCode 238. 除自身以外数组的乘积 问题分析+解析
java·算法·leetcode
吃饺子不吃馅2 小时前
优化:如何避免 React Context 引起的全局挂载节点树重新渲染
前端·面试·github
Hello_Embed3 小时前
FreeRTOS 入门(四):堆的核心原理
数据结构·笔记·学习·链表·freertos·
Wenhao.4 小时前
LeetCode 救生艇
算法·leetcode·golang
夏鹏今天学习了吗4 小时前
【LeetCode热题100(69/100)】字符串解码
linux·算法·leetcode
小白程序员成长日记5 小时前
2025.11.18 力扣每日一题
算法·leetcode·职场和发展
代码程序猿RIP5 小时前
【C++开发面经】全过程面试问题详解
java·c++·面试
ssshooter6 小时前
传参优于外部变量
前端·后端·面试
未若君雅裁7 小时前
LeetCode 18 - 四数之和 详解笔记
java·数据结构·笔记·算法·leetcode