[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)。

相关推荐
八解毒剂4 分钟前
数据结构-平衡二叉树——对二叉搜索树的优化
数据结构·c++·算法
wu_ye_m14 分钟前
学习c语言第35天 函数声明和定义
c语言·开发语言·学习
运行时记录27 分钟前
别再手动写提示词了 — SkillOpt 让技能文档自己进化
算法
啦啦啦啦啦zzzz40 分钟前
算法总结(二分查找、双指针)
c++·算法
qq_8573058191 小时前
python语法
开发语言·python·算法
DXM05211 小时前
第9期|从机器学习到深度学习:AI遥感解译的进化逻辑
人工智能·算法·计算机视觉
小蒋学算法2 小时前
算法-阶乘函数后K个零
算法
weixin_307779132 小时前
智能模拟数据生成平台:生成式AI合成数据技术重塑开发测试效能
人工智能·测试工具·算法·测试用例
Darling噜啦啦2 小时前
JavaScript 数组深度解析:从纯函数到二维数组陷阱,一文吃透前端数据结构核心
前端·javascript·数据结构
羊羊小栈3 小时前
Uplift营销供应链协同决策系统(基于Uplift因果推断与运筹优化算法)
前端·人工智能·算法·毕业设计·大作业