当前位置: 首页 > news >正文

LeetCode算法题 (移除链表元素)Day15!!!C/C++

https://leetcode.cn/problems/remove-linked-list-elements/description/

一、题目分析

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

        今天的题目非常好理解,也就是要删除掉链表中==val的值,并返回新的头节点。

二、相关知识了解

        链表这种数据结构其实与数组相似,同属线性表,但是与数组相比的话,链表是不支持随机访问元素的,也就是说要想在链表中查询一个元素的位置的话,时间复杂度最坏为O(n)如果要查找的元素位于链表末尾(或不存在),需要遍历整个链表,遍历次数为n次。)。平均也就是O((n + 1)/ 2)(这里就不过多的推导了,感兴趣的同学可以期待一下下一期的题目,我会详细带着大家一起设计出一个功能相对完善的链表

对比数组的随机访问

  • 数组支持随机访问(通过下标),查询时间为 O(1),这是链表与数组的核心差异之一。

  • 但链表在插入/删除节点(尤其头部)时更高效 O(1),而数组可能需要移动元素(O(n))。

三、示例分析

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

四、解题思路&代码实现

        方法一:原链表删除元素(复杂度为O(n))

           首先,拿到一个链表第一步我们要进行的是判断该链表是否为空,如果为空的话直接return head就好。其次就是判断当前节点的值是否==val。如果==的话那么就需要对该节点进行删除操作。下面看一下具体代码的实现。

class Solution {
public:ListNode* removeElements(ListNode* head, int val) {// 处理头节点等于val的情况(可能需要连续删除多个头节点 如示例3)while (head && head->val == val) {ListNode* temp = head;  // 临时保存当前头节点head = head->next;      // 头节点指向下一个节点delete temp;           // 释放原头节点的内存}// 遍历链表,删除非头节点中值等于val的节点ListNode* cur = head;       // 当前节点指针while (cur && cur->next) {  // 当前节点和下一个节点均非空时循环if (cur->next->val == val) {// 如果下一个节点值等于val,则删除它ListNode* temp = cur->next;  // 临时保存待删除节点cur->next = cur->next->next;  // 跳过待删除节点,直接连接下下个节点delete temp;                  // 释放待删除节点的内存} else {// 否则,移动到下一个节点继续检查cur = cur->next;}}return head;  // 返回处理后的链表头节点}
};

        这里需要注意几个关键点:

  1. 首先在C/C++中堆区开辟的空间需进行手动释放,以免造成空间浪费,或者该空间内的脏数据影响程序结果。(一定要养成良好的编程习惯)
  2. 在第二个while语句终止条件中,一定要cur和cur->next都不为空时我们再去进行循环体,确保不会访问到空指针。最终返回的head节点有可能为NULL,也就是示例3的情况。
  3. 在进行头节点的处理使用while处理连续多个头节点值等于 val 的情况而不是if(if只能处理一次(例如 [1,1,1,2] 删除 1)。每次删除节点时记得更新head节点,并使用delete释放内存
  4. 如果 cur->next 的值等于 val,则修改 cur->next 跳过该节点,并释放内存。如果不等,则正常移动到下一个节点。

        方法二:虚拟头节点法(复杂度为O(n))

          虚拟头节点法属于是对法一进行的一个优化操作,可以显著简化链表操作,尤其是在处理涉及头节点删除或修改的问题时。

        对比法一的优点:

  1. 无需特殊处理头节点,虚拟头节点始终作为链表的“前置节点”,使得真正的头节点(dummy->next)和其他节点的删除逻辑完全一致,避免分支判断。
  2. 简化代码的结构,法一需要进行两步操作,需先对头节点进行预处理操作,再去处理其余节点。而使用虚拟头节点则只需要一个循环即可处理所有节点,避免代码冗余从而简化代码。
  3. 虚拟头节点确保 cur 初始指向一个非空节点(dummy),因此 cur->next 的访问总是安全的(无需额外判空)。

        具体实现代码如下:

class Solution {
public:ListNode* removeElements(ListNode* head, int val) {// 创建虚拟头节点(dummy node),其值任意(这里设为0),next指向原链表头节点// 使用虚拟头节点可以统一处理原头节点和其他节点的删除逻辑ListNode* dummy = new ListNode(0);dummy->next = head;  // 将虚拟头节点连接到原链表// cur指针用于遍历链表,初始指向虚拟头节点ListNode* cur = dummy;// 遍历链表,检查每个节点的下一个节点(cur->next)while (cur->next) {if (cur->next->val == val) {  // 如果下一个节点的值等于目标值ListNode* temp = cur->next;  // 临时保存要删除的节点cur->next = cur->next->next; // 跳过要删除的节点,直接连接下下个节点delete temp;                 // 释放要删除节点的内存(避免内存泄漏)// 注意:这里不移动cur指针,因为新的cur->next可能也需要删除// 例如链表[1,2,2,3],删除2时需要连续检查} else {cur = cur->next;  // 如果不需要删除,则正常移动到下一个节点}}// 返回处理后的链表头节点(即dummy->next)// 注意:原头节点可能已被删除,dummy->next会自动更新为新的头节点ListNode* newHead = dummy->next;delete dummy;  // 释放虚拟头节点的内存return newHead;}
};

        关键点说明:

  1. 虚拟头节点:创建一个虚拟头节点作为原链表的起始点,其next指针指向的为原链表的head节点 (作用:统一处理逻辑,避免单独进行头节点删除操作)
  2. 返回值:这里需要注意我们需要返回的值应为虚拟头节点的next,若原链表所有节点都已被删除,那么虚拟头节点的next会变为NULL,则无需处理。

        其余与方法一相同!

至此算法已经是最优解!完结撒花!!!🌸🌸🌸

四、题目总结     

方法一:原链表删除元素

此方法先对头节点进行处理,若头节点的值等于 val,则连续删除头节点直至其值不等于 val。之后遍历链表,对非头节点中值等于 val 的节点进行删除操作。该方法时间复杂度为 O(n),不过在处理头节点时需要额外的逻辑判断,且要分别处理头节点和非头节点的删除情况,代码相对复杂。

方法二:虚拟头节点法

这种方法创建了一个虚拟头节点,将其 next 指针指向原链表的头节点。通过遍历链表,检查每个节点的下一个节点,若其值等于 val 则进行删除。此方法的优点在于统一了头节点和其他节点的删除逻辑,避免了额外的分支判断,简化了代码结构。时间复杂度同样为 O(n),是对方法一的优化。

总结

在处理链表删除操作时,使用虚拟头节点能有效简化代码,避免因头节点的特殊情况而增加的复杂逻辑,提高代码的可读性和可维护性。两种方法的时间复杂度均为线性,已达到最优解。在实际编程中,要养成手动释放堆区内存的习惯,防止内存泄漏。今天的题解分享到这里,谢谢大家!!!荆轲刺秦!!!

相关文章:

  • 如何在linux服务器下载gitee上的模型
  • 开启 Spring AI 之旅:从入门到实战
  • 开发规范-Restful
  • Linux 常用命令 - tar【归档与压缩】
  • C++负载均衡远程调用学习之UDP SERVER功能
  • MATLAB技巧——norm和vecnorm两个函数讲解与辨析
  • 组件通信-$attrs
  • 重构编程范式:解码字节跳动 AI 原生 IDE Trae 的技术哲学与实践价值
  • 数据结构学习之算法复杂度
  • 2025大模型安全研究十大框架合集(10份)
  • C++之类和对象基础
  • 微服务中组件扫描(ComponentScan)的工作原理
  • 【黑马JavaWeb+AI知识梳理】后端Web基础02 - Web基础
  • 单片机-STM32部分:1、STM32介绍
  • 【C++】认识map和set
  • Vue3源码学习4-effect中为什么使用WeakMap,Set?
  • 深入理解 MyBatis 代理机制
  • 数据结构6 · BinaryTree二叉树模板
  • 【51单片机8位数码管动态显示、右向左流水显示】2022-4-16
  • OpenHarmony - 驱动使用指南,HDF驱动开发流程
  • 泽连斯基拒绝普京72小时停火提议,坚持应尽快实现30天停火
  • 三亚再回应游客骑摩托艇出海遇暴雨:俱乐部未配备足额向导人员,停业整改
  • 2025五一档首日电影票房破亿
  • 山西太原小区爆炸事故已造成17人受伤
  • 国泰海通合并后首份业绩报告出炉:一季度净利润增逾391%
  • “光荣之城”2025上海红色文化季启动,红色主题市集亮相