补充 part 1——防御性编程

链表题丢分最严重的不是逻辑不会,而是"空指针异常(NULL Pointer)"。

你的代码如果能在关键时刻加一句 if 检查,在导师眼里就是"专业"和"稳重"的代名词。


🛡️ 链表防御性编程

核心思想:"只要后面带着 ->,就必须保证前面不是 NULL。"

1. 单链表的"二级跳"防御

场景:你需要访问当前结点的"后继的后继"(即跳过一个结点)。

  • ❌ 危险写法(小白):

    cpp 复制代码
    p->jump = p->next->next; // 如果 p 是最后一个,p->next 就是 NULL,程序直接崩溃!
  • ✅ 防御写法(408标准):

    cpp 复制代码
    if (p->next != NULL) 
      {
        p->jump = p->next->next; // 只有确定有"下一位",才敢去摸"下下位"
      } 
    else 
      {
        p->jump = NULL;          // 后面没人了,直接置空
      }
2. 双向链表的"四步插入"防御(核心考点)

场景 :在结点 p 之后插入结点 s。双向链表的精髓在于不仅要连 next,还要连 prior

  • ❌ 危险写法:

    cpp 复制代码
    s->next = p->next;
    p->next->prior = s; // 报错点:如果 p 是最后一个结点,p->next 为 NULL,NULL 没有 prior!
    s->prior = p;
    p->next = s;
  • ✅ 防御写法(顺序不能错,判空不能省):

    cpp 复制代码
    s->next = p->next; 
    if (p->next != NULL) { // 【防御核心】判断 p 是不是尾巴
        p->next->prior = s; 
    }
      s->prior = p;
      p->next = s;
      
3. 链表删除的"闭环"防御

场景 :删除 p 结点的后继结点 q

  • ✅ 防御写法:

    cpp 复制代码
    q = p->next;
    if (q == NULL) 
       return false;             // 防御:后面没东西,删个寂寞
    
    p->next = q->next;
    if (q->next != NULL)         // 【防御核心】如果要删的不是最后一个
      {                          
        q->next->prior = p; 
      }
    free(q);

📊 防御性编程对照表(烂熟于心)

操作场景 风险代码 潜在后果 防御措施
访问后继 p = p->next; pNULL 后继续操作 检查 p != NULL
访问后继的后继 p->next->next 空指针崩溃 检查 p->next != NULL
修改前驱 q->next->prior = p 空指针崩溃 检查 q->next != NULL
头插/尾插 直接修改 L->next 丢失原链表 引入 r 指针或备份 L->next

📝 2026 年真题 Q2 逻辑复刻

真题里那个 D 选项为什么是对的?因为它完美执行了刚说的逻辑:

题目背景: 双向链表 [p2, data, p1]p1nextp2 是我们要修改的指针。要把 p2 指向 p1->p1(即后继的后继)。

标准执行逻辑:

cpp 复制代码
while (cu != NULL) {          // 只要还没走到头
    if (cu->p1 != NULL) {     // 防御:如果有下一个结点
        cu->p2 = cu->p1->p1;  // 让 p2 指向"下下个"(哪怕下下个是 NULL 也没关系)
    } else {
        cu->p2 = NULL;        // 如果没有下一个,那更不可能有下下个
    }
    cu = cu->p1;              // 指针正常后移
}

这是一段双向链表删除节点 的防御性实现,目标是删除链表中节点 p直接后继节点 q

在 C/C++ 这类手动管理内存的语言中:

  • 链表节点通常是通过 malloc/calloc 等函数动态分配在堆内存中的。

  • q 被从链表中移除后,它不再被任何指针引用,但堆内存不会自动回收。

  • 如果不调用 free(q),这块内存会一直被占用,直到程序结束,造成内存泄漏。长期运行的程序会因内存泄漏逐渐耗尽系统资源,导致性能下降甚至崩溃。


💡 补充 Part1的"双向链表"和"删除/插入操作" 模块

总结一句话: > 只要在代码里写 ->,你的大脑就要立刻反射出:"它前面的那个东西会不会是 NULL?"

相关推荐
一只数据集2 分钟前
柏林道路路面图像数据集-971张沥青与鹅卵石路面图片-训练测试集划分-支持道路材质识别与自动驾驶视觉算法训练
算法·自动驾驶·材质
我不是懒洋洋3 分钟前
【数据结构】二叉树OJ(单值二叉树、检查两棵树是否相同、对称二叉树、二叉树的前序遍历、另一颗树的子树)
c语言·数据结构·c++·经验分享·算法·leetcode·visual studio
wljy16 分钟前
每日一题(2026.4.29) 猫猫与数学
c语言·c++·算法·蓝桥杯·stl·牛客
sali-tec9 分钟前
C# 基于OpenCv的视觉工作流-章56-OCR
图像处理·人工智能·opencv·算法·计算机视觉·ocr
MicroTech202513 分钟前
微算法科技(NASDAQ:MLGO)混合经典量子算法:赋能数字图像处理的创新路径
科技·算法·量子计算
yu859395815 分钟前
降低OFDM系统PAPR的各种算法及误码率分析
前端·算法
阿Y加油吧18 分钟前
二刷 LeetCode:5. 最长回文子串 & 1143. 最长公共子序列 复盘笔记
笔记·算法·leetcode
广州灵眸科技有限公司35 分钟前
瑞芯微(EASY EAI)RV1126B AI算法开发流程
人工智能·算法·机器学习
Rabitebla1 小时前
【C++】string 类:原理、踩坑与对象语义
linux·c语言·数据结构·c++·算法·github·学习方法
小雅痞2 小时前
[Java][Leetcode middle] 167. 两数之和 II - 输入有序数组
java·算法·leetcode