[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移动到cur,cur移动到下一个节点。

三、图解

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

要删除所有值为 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)。

相关推荐
没有钱的钱仔1 小时前
协方差矩阵含义
算法·机器学习·矩阵
垆边人似月.1 小时前
华为机试题 :和为 K 的子数组个数
算法·leetcode·华为
三*一1 小时前
优化修整算法
前端·javascript·算法
达子6662 小时前
RUST 图解 第 3 章:通用编程概念(Common Programming Concepts)
开发语言·算法·rust
夏幻灵2 小时前
每日一题:LeetCode 165:比较版本号
算法·leetcode·职场和发展
道尔柯南2 小时前
C++ 面向对象编程入门:类和对象(上)
开发语言·c++·算法
Navigator_Z2 小时前
LeetCode //C - 1288. Remove Covered Intervals
c语言·算法·leetcode
码爸3 小时前
决策树介绍与示例
python·算法·机器学习
动词ing3 小时前
【题目练习】字符串相乘
算法