跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

面试题 02.04 分割链表题解与详细分析

LeetCode 分割链表问题(02.04)。提供三种解法:基于头插尾插法的原地分割、保持相对顺序的双链表法及原地移动法。通过变量说明、执行过程可视化及复杂度分析,阐述如何在不保留相对位置的前提下将小于 x 的节点移至大于等于 x 的节点之前。涵盖边界处理、指针操作技巧及应用场景,适合链表算法学习。

HadoopMan发布于 2026/3/21更新于 2026/5/821 浏览
面试题 02.04 分割链表题解与详细分析

题目描述

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

你不需要保留每个分区中各节点的初始相对位置。

示例

示例 1:

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

示例 2:

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

提示:
  • 链表中节点的数目在范围 [0, 200] 内
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

解题思路

这道题要求将链表分割成两部分:所有小于 x 的节点在前,大于或等于 x 的节点在后。关键点在于不需要保持每个分区内节点的原始相对顺序。

核心思想

使用双指针方法:

  • 对于小于 x 的节点,采用头插法插入到新链表头部
  • 对于大于等于 x 的节点,采用尾插法插入到新链表尾部
  • 这样就能保证所有小于 x 的节点都在大于等于 x 的节点之前

代码实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* partition(struct ListNode* head, int x) {
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL;
    struct ListNode* newtail = NULL;
    struct ListNode* temp = NULL;
    while(cur) {
        temp = cur->next; // 保存下一个节点
        if(cur->val < x) { // 小于x的节点:头插法
            if(newhead == NULL) {
                newhead = newtail = cur;
                newhead->next = NULL;
            } else {
                cur->next = newhead;
                newhead = cur;
            }
        } else { // 大于等于x的节点:尾插法
            if(newhead == NULL) {
                newhead = newtail = cur;
                newhead->next = NULL;
            } else {
                newtail->next = cur;
                newtail = cur;
                newtail->next = NULL;
            }
        }
        cur = temp; // 移动到下一个节点
    }
    return newhead;
}

代码详解

变量说明
  • cur:当前遍历的节点
  • newhead:新链表的头节点
  • newtail:新链表的尾节点
  • temp:临时保存下一个节点
核心逻辑
while(cur) {
    temp = cur->next; // 保存下一个节点
    if(cur->val < x) { // 头插法:将节点插入到链表头部
        if(newhead == NULL) {
            newhead = newtail = cur;
            newhead->next = NULL;
        } else {
            cur->next = newhead;
            newhead = cur;
        }
    } else { // 尾插法:将节点插入到链表尾部
        if(newhead == NULL) {
            newhead = newtail = cur;
            newhead->next = NULL;
        } else {
            newtail->next = cur;
            newtail = cur;
            newtail->next = NULL;
        }
    }
    cur = temp; // 移动到下一个节点
}
执行过程可视化

以示例 1 head = [1,4,3,2,5,2], x = 3 为例:

初始状态: 链表:1->4->3->2->5->2->NULL newhead = NULL, newtail = NULL

第 1 步:节点 1 (值 1 < 3) 头插法:newhead -> 1 -> NULL, newtail -> 1

第 2 步:节点 4 (值 4 >= 3) 尾插法:newhead -> 1 -> 4 -> NULL, newtail -> 4

第 3 步:节点 3 (值 3 >= 3) 尾插法:newhead -> 1 -> 4 -> 3 -> NULL, newtail -> 3

第 4 步:节点 2 (值 2 < 3) 头插法:newhead -> 2 -> 1 -> 4 -> 3 -> NULL, newtail -> 3

第 5 步:节点 5 (值 5 >= 3) 尾插法:newhead -> 2 -> 1 -> 4 -> 3 -> 5 -> NULL, newtail -> 5

第 6 步:节点 2 (值 2 < 3) 头插法:newhead -> 2 -> 2 -> 1 -> 4 -> 3 -> 5 -> NULL, newtail -> 5

最终结果:2->2->1->4->3->5->NULL

优化与改进

方法二:双链表法(保持相对顺序)

如果我们希望保持每个分区内节点的相对顺序,可以使用两个链表:

struct ListNode* partition(struct ListNode* head, int x) {
    // 创建两个哑节点
    struct ListNode less_dummy, ge_dummy;
    struct ListNode* less_tail = &less_dummy;
    struct ListNode* ge_tail = &ge_dummy;
    less_dummy.next = NULL;
    ge_dummy.next = NULL;
    struct ListNode* cur = head;
    while (cur != NULL) {
        if (cur->val < x) { // 添加到小于x的链表
            less_tail->next = cur;
            less_tail = cur;
        } else { // 添加到大于等于x的链表
            ge_tail->next = cur;
            ge_tail = cur;
        }
        cur = cur->next;
    }
    // 连接两个链表
    less_tail->next = ge_dummy.next;
    ge_tail->next = NULL;
    return less_dummy.next;
}

输出结果: [1,2,2,4,3,5](保持相对顺序)

方法三:原地分割
struct ListNode* partition(struct ListNode* head, int x) {
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    struct ListNode* insert_pos = NULL;
    while (cur != NULL) {
        if (cur->val < x) {
            if (insert_pos != NULL) { // 将当前节点移动到insert_pos之后
                if (prev != NULL) {
                    prev->next = cur->next;
                }
                struct ListNode* temp = insert_pos->next;
                insert_pos->next = cur;
                cur->next = temp;
                insert_pos = cur;
                cur = prev->next;
            } else {
                insert_pos = cur;
                prev = cur;
                cur = cur->next;
            }
        } else {
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}

复杂度分析

原方法
  • 时间复杂度:O(n),只需遍历链表一次
  • 空间复杂度:O(1),只使用常数级别的额外空间
双链表法
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

关键点总结

  1. 头插法与尾插法的结合:小于 x 的节点用头插法,大于等于 x 的节点用尾插法
  2. 边界处理:注意处理空链表和单节点链表的情况
  3. 指针操作:在修改指针前保存下一个节点,避免链表断裂
  4. 尾节点处理:确保尾节点的 next 为 NULL,避免形成环

应用场景

这种链表分割技巧在以下场景中有应用:

  1. 快速排序的链表版本:分割操作是快速排序的核心
  2. 数据过滤:根据条件将数据分成两部分
  3. 负载均衡:将任务根据优先级分配到不同队列

总结

这道题考察了链表的基本操作和双指针技巧:

  1. 核心思路:通过头插法和尾插法的组合实现链表分割
  2. 灵活性:题目不要求保持相对顺序,给了我们更多的操作空间
  3. 指针操作:熟练掌握链表的插入、删除操作是关键
  4. 边界情况:注意处理空链表、单节点链表等特殊情况

掌握这种链表分割技巧对于理解更复杂的链表算法(如链表排序)很有帮助。

目录

  1. 题目描述
  2. 示例
  3. 提示:
  4. 解题思路
  5. 核心思想
  6. 代码实现
  7. 代码详解
  8. 变量说明
  9. 核心逻辑
  10. 执行过程可视化
  11. 优化与改进
  12. 方法二:双链表法(保持相对顺序)
  13. 方法三:原地分割
  14. 复杂度分析
  15. 原方法
  16. 双链表法
  17. 关键点总结
  18. 应用场景
  19. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 DeepSeek 的贪吃蛇游戏开发实战
  • 基于 FPGA 的日志及参数文件存储设计
  • 基于 UnityMCP、Claude 与 VSCode 搭建 AI 游戏开发工作流
  • 首个 Mamba+Transformer 混合架构多模态大模型 LongLLaVA
  • 李沐:大模型发展趋势与创业感悟
  • 大型语言模型能否遵循简单规则:RULES 基准测试分析
  • 企业微信接入 AI 小助手:基于回调接口的群聊机器人实战
  • 使用 LLaMA-Factory 微调 Qwen2.5 模型并转换为 GGUF 格式部署
  • Flutter 集成 eth_sig_util 实现鸿蒙端以太坊签名适配
  • C++ 二叉搜索树详解:核心原理与完整实现
  • 前端状态管理方案对比:Redux Toolkit、Zustand 与 Jotai
  • Stable Diffusion WebUI 本地部署指南(CUDA/cuDNN/PyTorch 配置)
  • 纯 LLM、多模态大模型与 AIGC 的就业路径对比分析
  • 企业微信外部群 Webhook 配置与消息推送指南
  • Android 陀螺仪基础:传感器数据与角度积分计算
  • OpenClaw 对接腾讯 QQ 实战操作详解
  • 鸿蒙金融理财全栈项目:基础架构、数据安全与用户体验
  • C++ AVL 树详解:概念、插入、旋转与实现
  • Android 陀螺仪开发实战:从数据获取到姿态解算
  • Stable-Diffusion-3.5 运行慢?FP8 量化 GPU 优化实战方案

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online