[OJ]数据结构:移除链表元素

目录

题目来源:力扣(LeetCode)

一、完整代码

二、解题思路

三、图解

1.初始化

2.循环启动

3.结束循环

四、常见陷阱

1.空指针解引用陷阱

2.内存泄漏陷阱

3.指针丢失陷阱

4.头节点处理陷阱

五、总结


题目来源:力扣(LeetCode)

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

一、完整代码

复制代码
struct ListNode* removeElements(struct ListNode* head, int val) 
{
    // 删除头节点为val的情况,直到头节点不是val
    while (head != NULL && head->val == val) 
    {
        struct ListNode* del = head;
        head = head->next;
        free(del);
    }

    // 如果链表为空,直接返回
    if (head == NULL) 
    {
        return NULL;
    }

    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    while (cur) 
    {
        if (cur->val == val) 
        {
            struct ListNode* del = cur;
            prev->next = cur->next;
            cur = cur->next;
            free(del);
        } 
        else 
        {
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}

二、解题思路

  1. 预处理头节点:先处理所有连续的要删除头节点

  2. 双指针遍历
    prev:指向当前节点的前一个节点(初始为NULL);
    cur:指向当前正在检查的节点。

  3. 删除逻辑
    cur->val == val时,将prev->next指向cur->next,定义临时指针del指向cur,然后删除del;
    否则,将prev移动到curcur移动到下一个节点。

三、图解

让我们通过一个具体例子来详细理解算法的执行过程。假设我们有以下链表:

要删除所有值为 2 的节点。

1.初始化

  • cur 指向头节点 1

  • prev 初始为 NULL

2.循环启动

(1)第一个循环

由于 cur->val = 1 不等于 2,执行 else 分支:

  • prev = cur(prev指向节点1)

  • cur = cur->next(cur指向节点2)

(2)删除第一个值为2的节点

cur->val = 2 等于要删除的值,执行删除操作:

  • struct ListNode* del = cur(del指向要删除的节点2)
  • prev->next = cur->next(节点1的next指向节点3)
  • cur = cur->next(cur指向节点3)
  • free(del)(释放节点2的内存)

(3)继续遍历,直到遇到val = 2 的节点

.....................

(4)删除第二个值为2的节点

cur->val = 2 等于要删除的值,执行删除操作:

  • struct ListNode* del = cur(del指向要删除的节点2)

  • prev->next = cur->next(节点4的next指向下一个节点2)

  • cur = cur->next(cur指向下一个节点2)

  • free(del)(释放节点2的内存)

(5)删除第三个值为2的节点

cur->val = 2 等于要删除的值,执行删除操作:

  • struct ListNode* del = cur(del指向要删除的节点2)

  • prev->next = cur->next(节点4的next指向节点5)

  • cur = cur->next(cur指向节点5)

  • free(del)(释放节点2的内存)

(6)继续遍历

cur->val = 5 不等于2,执行else分支:

  • prev = cur(prev指向节点5)

  • cur = cur->next(cur指向NULL)

3.结束循环

cur = NULL 时,while 循环条件 cur 为假,循环结束。

最终链表为:

返回 head,即节点1

四、常见陷阱

1.空指针解引用陷阱

复制代码
// 错误示例
if (cur->val == val) 
{
    prev->next = cur->next;  // 当prev为NULL时会崩溃
    free(cur);
}

2.内存泄漏陷阱

复制代码
// 错误示例
if (cur->val == val) 
{
    prev->next = cur->next;
    cur = cur->next;
    // 忘记释放del节点的内存!
}

3.指针丢失陷阱

复制代码
// 错误示例
if (cur->val == val) 
{
    free(cur);           // 先释放内存
    prev->next = cur->next;  // 访问已释放内存!
    cur = prev->next;
}

4.头节点处理陷阱

复制代码
// 错误示例
struct ListNode* cur = head;
struct ListNode* prev = NULL;

while (cur) 
{
    if (cur->val == val) 
    {
        if (prev == NULL) 
        {
            head = cur->next;  // 头节点删除
        } 

        else 
        {
            prev->next = cur->next;
        }
        free(cur);
        // 问题:删除头节点后,cur指针状态可能不对
    }
}

五、总结

双指针法是删除链表节点的核心技巧,使用prev和cur指针遍历链表。需要单独处理可能被连续删除的头节点。删除节点时须严格按顺序操作:先保存待删节点,再移动cur指针,接着更新prev->next,最后释放内存。特别注意,连续删除时prev保持不动,只在保留节点时才向后移动。复杂度:时间O(n),空间O(1)。

相关推荐
刃神太酷啦2 小时前
C++ list 容器全解析:从构造到模拟实现的深度探索----《Hello C++ Wrold!》(16)--(C/C++)
java·c语言·c++·qt·算法·leetcode·list
Herbert_hwt2 小时前
C语言结构体详解:从定义到实战应用
c语言
承渊政道2 小时前
一文彻底搞清楚链表算法实战大揭秘和双向链表实现
c语言·数据结构·算法·leetcode·链表·visual studio
jianfeng_zhu2 小时前
数据结构中括号匹配的问题
数据结构
松涛和鸣2 小时前
32、Linux线程编程
linux·运维·服务器·c语言·开发语言·windows
sali-tec2 小时前
C# 基于halcon的视觉工作流-章69 深度学习-异常值检测
开发语言·图像处理·算法·计算机视觉·c#
努力写代码的熊大2 小时前
手撕AVL树:从理论到实践,掌握插入操作的完美平衡
算法
wbs_scy2 小时前
C++:二叉搜索树(BST)完全指南(从概念原理、核心操作到底层实现)
数据结构·算法