力扣刷题-86.分隔链表

一、问题现象:AddressSanitizer 报 SEGV 错误

1. 错误日志

复制代码
Line 17: Char 22:
AddressSanitizer:DEADLYSIGNAL
==22==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x560396d93c39 bp 0x7ffef0182410 sp 0x7ffef01823d0 T0)
==22==The signal is caused by a READ memory access.
==22==Hint: address points to the zero page.
    #0 0x560396d93c39 in Solution::partition(ListNode*, int) solution.cpp:17:22
SUMMARY: AddressSanitizer: SEGV solution.cpp:17:22 in Solution::partition(ListNode*, int)

2. 错误解读

  • SEGV on unknown address 0x000000000000:核心问题是空指针解引用(尝试读取地址为 0 的内存,这是系统保护的无效内存);
  • 错误定位:solution.cpp 第 17 行的 partition 函数,是链表遍历过程中触发的内存访问错误。

二、初始错误代码分析

我的初始思路是用 vector 收集 "小于 x" 和 "大于等于 x" 的节点,再拼接成新链表,代码如下:

cpp 复制代码
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(!head||!head->next) return head;
        vector<ListNode*> vec;
        // 第一遍遍历:收集小于x的节点(逻辑正常)
        for(ListNode *l=head;l;l=l->next){
            if(l->val<x)
            vec.push_back(l);
        }
        // 第二遍遍历:收集大于等于x的节点(致命错误)
        for(ListNode* l;l;l=l->next){ // 未初始化l!
            if(l->val>=x)
            vec.push_back(l);
        }
        // 拼接链表(次要问题:尾节点未置空)
        for(int i=0;i<vec.size()-1;++i){
            vec[i]->next=vec[i+1];
        }
        return vec[0];
    }
};

3. 核心问题拆解

(1)致命错误:循环变量未初始化(直接触发 SEGV)

第二个 for 循环中 ListNode* l; 仅声明了指针,但未初始化,此时 l 的值是随机的垃圾值(大概率是 0x0,即空指针)。

  • 循环条件 l; 会直接判断这个垃圾值,若为 0 则循环不执行,若为非 0 则访问 l->next,触发空指针解引用;
  • 这是导致 AddressSanitizer 报 SEGV 的直接原因(第 17 行的核心问题)。
(2)次要但关键的问题:链表尾节点未置空

拼接链表后,最后一个节点的 next 仍指向原链表的节点,会导致:

  • 链表出现环,遍历时报错;
  • 访问原链表的无效内存,触发潜在的内存错误。

三、修复思路与正确代码

1. 修复核心原则

  • 所有指针使用前必须初始化;
  • 访问 node->val/node->next 前,先检查 node 是否为空;
  • 链表拼接完成后,尾节点的 next 必须置空。

2. 修复后的完整代码

cpp 复制代码
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        // 边界条件:空链表或单节点链表直接返回
        if(!head || !head->next) return head;
        
        vector<ListNode*> vec;
        // 第一遍遍历:收集小于x的节点(初始化l=head)
        for(ListNode *l = head; l; l = l->next) {
            if(l->val < x) {
                vec.push_back(l);
            }
        }
        // 第二遍遍历:收集大于等于x的节点(关键修复:初始化l=head)
        for(ListNode *l = head; l; l = l->next) {
            if(l->val >= x) {
                vec.push_back(l);
            }
        }
        
        // 拼接链表
        for(int i = 0; i < vec.size() - 1; ++i) {
            vec[i]->next = vec[i+1];
        }
        // 关键修复:尾节点next置空,避免环或无效访问
        vec.back()->next = nullptr;
        
        return vec[0];
    }
};

3. 关键修改点

表格

问题点 修复方案
循环变量 l 未初始化 第二个循环中 l 初始化为 head,确保从链表头开始遍历
尾节点 next 未置空 添加 vec.back()->next = nullptr;,终止链表

四、进阶优化:O (1) 空间复杂度方案

上述方案用 vector 存储节点,空间复杂度为 O (n),链表题的最优解通常要求 O (1) 空间,可通过虚拟头节点实现:

cpp 复制代码
ListNode* partition(ListNode* head, int x) {
    // 定义两个虚拟头节点,分别存储小于x和大于等于x的节点
    ListNode* dummy1 = new ListNode(0);
    ListNode* dummy2 = new ListNode(0);
    ListNode* p1 = dummy1; // 遍历dummy1的指针
    ListNode* p2 = dummy2; // 遍历dummy2的指针
    
    // 一次遍历完成分区,避免重复遍历
    while(head) {
        if(head->val < x) {
            p1->next = head;
            p1 = p1->next;
        } else {
            p2->next = head;
            p2 = p2->next;
        }
        head = head->next;
    }
    
    // 尾节点置空 + 拼接两个链表
    p2->next = nullptr;
    p1->next = dummy2->next;
    
    // 释放虚拟节点,避免内存泄漏
    ListNode* res = dummy1->next;
    delete dummy1;
    delete dummy2;
    
    return res;
}

优化点:

  1. 仅遍历链表一次,时间复杂度保持 O (n);
  2. 无需额外容器存储节点,空间复杂度降为 O (1);
  3. 虚拟头节点避免了空指针判断的冗余代码,逻辑更简洁。
相关推荐
CoovallyAIHub15 小时前
Moonshine:比 Whisper 快 100 倍的端侧语音识别神器,Star 6.6K!
深度学习·算法·计算机视觉
CoovallyAIHub17 小时前
速度暴涨10倍、成本暴降6倍!Mercury 2用扩散取代自回归,重新定义LLM推理速度
深度学习·算法·计算机视觉
CoovallyAIHub17 小时前
实时视觉AI智能体框架来了!Vision Agents 狂揽7K Star,延迟低至30ms,YOLO+Gemini实时联动!
算法·架构·github
CoovallyAIHub17 小时前
开源:YOLO最强对手?D-FINE目标检测与实例分割框架深度解析
人工智能·算法·github
CoovallyAIHub17 小时前
OpenClaw:从“19万星标”到“行业封杀”,这只“赛博龙虾”究竟触动了谁的神经?
算法·架构·github
刀法如飞17 小时前
程序员必须知道的核心算法思想
算法·编程开发·算法思想
徐小夕19 小时前
pxcharts Ultra V2.3更新:多维表一键导出 PDF,渲染兼容性拉满!
vue.js·算法·github
CoovallyAIHub20 小时前
OpenClaw一脚踩碎传统CV?机器终于不再只是看世界
深度学习·算法·计算机视觉
CoovallyAIHub20 小时前
仅凭单目相机实现3D锥桶定位?UNet-RKNet破解自动驾驶锥桶检测难题
深度学习·算法·计算机视觉
zone773920 小时前
002:RAG 入门-LangChain 读取文本
后端·算法·面试