解密链表元素移除:三种巧妙思路,轻松驱逐难缠结点

news2024/11/16 23:51:14

在这里插入图片描述
本篇博客会讲解力扣“203. 移除链表元素”的解题思路,这是题目链接。

老规矩,先来审题:

在这里插入图片描述
以下是一些示例:
在这里插入图片描述
以下是提示:
在这里插入图片描述
本题的思路还挺多的,不过都是链表的常规操作。

思路1

万能的尾插法。遍历链表,找到所有不是val的结点,尾插到新的链表中。

首先创建哨兵位的头结点:

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = NULL;
}

接着遍历并且尾插。由于单链表尾插需要找尾,建议定义个指针记录尾结点。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = NULL;

    // 遍历并且尾插
    struct ListNode* tail = newHead;
    struct ListNode* cur = head;
    while (cur)
    {
        if (cur->val == val)
        {
        	// 删除
        }
        else
        {
            // 尾插
        }
    }
}

常规的删除和尾插的逻辑:

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = NULL;

    // 遍历并且尾插
    struct ListNode* tail = newHead;
    struct ListNode* cur = head;
    while (cur)
    {
        if (cur->val == val)
        {
            // 删除
            struct ListNode* next = cur->next;
            free(cur);
            cur = next;
        }
        else
        {
            // 尾插
            tail->next = cur;
            tail = cur;
            cur = cur->next;
        }
    }
}

最后把尾部置空,释放哨兵位,再返回头结点。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = NULL;

    // 遍历并且尾插
    struct ListNode* tail = newHead;
    struct ListNode* cur = head;
    while (cur)
    {
        if (cur->val == val)
        {
            // 删除
            struct ListNode* next = cur->next;
            free(cur);
            cur = next;
        }
        else
        {
            // 尾插
            tail->next = cur;
            tail = cur;
            cur = cur->next;
        }
    }

    // 尾部置空
    tail->next = NULL;
    // 释放哨兵位
    struct ListNode* del = newHead;
    newHead = newHead->next;
    free(del);
    del = NULL;

    return newHead;
}

在这里插入图片描述
这样就过了。以上的思路是非常常见的通解,很多链表题目都会用到类似的思路,大家一定要熟练掌握。

思路2

这道题既然要删除结点,一个一个删就行了。遍历链表并且删除即可。

先定义哨兵位的头结点。由于要直接在链表中删除结点,所以在原链表前面定义即可。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = head;
}

接下来遍历链表,并且删除。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = head;

    // 遍历并且删除
    struct ListNode* cur = head;
    while (cur)
    {
        if (cur->val == val)
        {
            // 删除
        }
        else
        {

        }
    }
}

删除时应该注意什么呢?我们要让删除的结点的前一个指向删除的结点的后一个,所以需要记录前一个结点。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = head;

    // 遍历并且删除
    struct ListNode* cur = head; // 负责遍历
    struct ListNode* prev = newHead; // 记录前一个结点
    while (cur)
    {
        if (cur->val == val)
        {
            // 删除
            prev->next = cur->next;
            free(cur);
            cur = prev->next;
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
}

最后释放哨兵位即可。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 定义哨兵位的头结点
    struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    newHead->val = 0;
    newHead->next = head;

    // 遍历并且删除
    struct ListNode* cur = head; // 负责遍历
    struct ListNode* prev = newHead; // 记录前一个结点
    while (cur)
    {
        if (cur->val == val)
        {
            // 删除
            prev->next = cur->next;
            free(cur);
            cur = prev->next;
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }

    // 释放哨兵位
    struct ListNode* del = newHead;
    newHead = newHead->next;
    free(del);
    del = NULL;
    
    return newHead;
}

在这里插入图片描述
轻松通过。

思路3

这道题还可以使用递归来实现。

把问题转化一下:

  1. 递归调用自身,对除了头结点之外的结点进行操作。
  2. 操作头结点。

开始干代码:先处理特殊情况,因为比较好想。如果链表已经是空了,就先返回。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 链表已空
    if (head == NULL)
        return NULL;
}

接着操作除了头结点之外的结点,并且链接到头结点后面:

struct ListNode* removeElements(struct ListNode* head, int val){
    // 链表已空
    if (head == NULL)
        return NULL;

    // 操作除了头结点之外的结点
    head->next = removeElements(head->next, val);
}

然后处理头结点。如果头结点是要删除的,就把头结点更新成head->next。

struct ListNode* removeElements(struct ListNode* head, int val){
    // 链表已空
    if (head == NULL)
        return NULL;

    // 操作除了头结点之外的结点
    head->next = removeElements(head->next, val);

    // 处理头结点
    if (head->val == val)
    {
        // 删除头结点
        struct ListNode* del = head;
        head = head->next;
        free(del);
        del = NULL;
    }

    return head;
}

在这里插入图片描述
这就搞定了。递归的空间消耗比较大是很正常的。

总结

  1. 最通用的思路是尾插法,特殊的思路是直接删除。不管哪种思路,都建议定义哨兵位的头结点。
  2. 递归的思路也很常见。

感谢大家的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/503643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Golang] 爬虫实战-用多层嵌套结构体去接收多层嵌套数据

😚一个不甘平凡的普通人,致力于为Golang社区和算法学习做出贡献,期待您的关注和认可,陪您一起学习打卡!!!😘😘😘 🤗专栏:算法学习 &am…

量子计算(10)编程实践2:隐形传态算法

目录 一、算法目的 二、算法原理 三、pyqpanda实现代码 四、测试结果 一、算法目的 量子隐形传态,又称量子遥传、量子隐形传输、量子隐形传送、量子远距传输或量子远传,是一种利用分散量子缠结与一些物理讯息的转换来传送量子态至任意距离的位置的技…

AutoSar CAN网络管理(CanNm)

文章目录 网络管理目的主动唤醒和被动唤醒状态管理1. 总线睡眠模式(Bus-Sleep Mode)2. 准备总线睡眠模式(Prepare Bus-Sleep Mode)3. 网络模式(Network Mode)3.1 重复报文状态(RepeatMessageState)3.2 常规…

基于springboot+mysql+jpa+html实现商品销售信息系统

基于springbootmysqljpahtml实现商品销售信息系统 一、系统介绍1、系统主要功能:2.涉及技术框架:3.本项目所用环境: 二、功能展示三、其它系统四、获取源码 一、系统介绍 1、系统主要功能: 订单管理模块 商品管理模块 品牌管理模…

【项目实战】—— 我们应该如何正确得创建线程池?

项目实战-我们应该如何正确得创建线程池? 对于现在而言多线程编程已经成为程序员必备的职业技能了,在开发实践过程中,你是否也遇到过相关多线程问题,比如创建多少线程才是合适的?线程池该如何创建?今天我们…

类和对象中(2)

文章目录 一、运算符重载1、运算符重载出现的原因2、在全局和类里实现运算符重载3、赋值运算符重载1、为什么赋值运算符重载不能写在全局 ?2、什么时候需要自己实现赋值运算符重载 ? 4、前置和后置重载5、运算符重载的优势 二、const成员函数1、两个常见…

GNN与MLP:GNN是一种泛化器

图神经网络(GNN)作为图表示学习的模型,建立在MLP架构之上,具有额外的消息传递,以允许特征在节点之间流动。作者通过引入一个被称为P-MLP的中间模型,将GNN性能增益的主要来源定位为其内在的泛化能力&#xf…

JSR303统一校验和分组校验及常用注解@NotBlank@NotEmpty 的使用

JSR303-bean校验规范 JSR303常用注解,下面注解在JavaBean的字段上使用,必须在方法上搭配Vaild才会开启校验,也可以设置分组校验 Null 必须为空 主键字段常用,可以规定新增时字段必须为空NotNull 不能为null 添加数字时使用常用&…

前端学习笔记:CSS中浮动的原理,定位

这是本人学习的总结,主要学习资料如下 马士兵教育 目录 1、float1.1、float设计的初衷1.2、浮动的原理,类似两个图层1.3、浮动的原理,两个图层的特殊性1.4、消除浮动的影响 2、定位2.1、相对定位(relative)2.2、绝对定…

CSS var()的使用

最近在做流量对比的功能,有如下的效果图,当某个节点失败的时候,点击能够弹出对应的提示信息。 这个库使用的是jenkins-pipeline 的库, 但是由于它原本的提示框比较糟糕,所以我们想结合antd的tooltip进行展示&#xff0…

CM211-1-ZG-当贝纯净桌面-线刷固件包-

CM211-1-ZG-当贝纯净桌面-线刷固件包-内有教程及短接点 特点: 1、适用于对应型号的电视盒子刷机; 2、开放原厂固件屏蔽的市场安装和u盘安装apk; 3、修改dns,三网通用; 4、大量精简内置的没用的软件,运…

设计模式之【工厂模式】,创建对象原来有这么多玩法

文章目录 一、什么是工厂模式1、工厂模式的意义2、什么时候应该用工厂模式 二、简单工厂模式1、实例(1)使用简单工厂进行优化(2)静态工厂(3)使用map来去除if(4)使用反射(…

.netCHARTING Crack,添加圆角半径控制

.netCHARTING Crack,添加圆角半径控制 直角或直线组织连接线-通过默认情况下以直角绘制组织连接线,增强了组织连接线的显示方式。可以使用直线选项更改此默认设置,并直接在点之间绘制连接线。 同步组织节点的宽度和高度-添加了Element.Annotation.SyncWi…

Vue3+element-plus实现后台管理系统

背景(未完待续) 环境:node.js软件 、Vs code、vite、elemnt-plus、windicss(样式框架) 第一节课 1、首先,使用npm 命令构建项目( vscode安装的插件 vscode中文显示插件 2、高亮提示插件volar 3、vue 3 …

【Ansys Fluent】根据export导出的ASCII文件按坐标和物理量之间的关系重建物理场(温度场、压力场等)

一、问题说明 在fluent中利用export功能导出ASCII格式的文件,例如下面的文件,第2-4列是单元中心坐标值,第5列是温度值。 如果给出和「导出这个ASCII数据文件时用的几何模型尺寸」一致或等比例放缩的几何模型,可否根据这个ASCII文…

【PCIE体系结构八】数据链路层是如何保证TLP的正确传输的?

👉个人主页:highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容 参考书籍:《深入浅出SSD:固态存储核心技术、原理与实战》 目…

kafka3.x详解

kafka 一、简介1.1、场景选择,与其他mq相比1.2、应用场景1.2.1、流量消峰1.2.2、解耦1.2.3、异步通讯 1.3、消息队列的两种模式1.3.1、点对点模式1.3.2、发布/订阅模式 1.4、Kafka 基础架构 二、安装部署2.1、安装包方式2.2、docker安装方式2.3、docker安装kafka-ma…

RuoYi-Cloud-Plus 登录过程源码

登录界面 ruoyi-ui/src/views/login.vue 点击登录按钮进入handleLogin方法 handleLogin() {//验证数据是否合法this.$refs.loginForm.validate(valid > {if (valid) {this.loading true;//如果记住密码被勾选if (this.loginForm.rememberMe) {//直接在cookie中存入相关信…

面试也要说人话

整理了一些读者的问题。 什么是《面试1v1》? 《面试1v1》是一个以对话形式讲解知识点的文章合集,是由 JavaPub 编写的真人1对1面试对话教程,通过真实案例编写,生动、有趣、干货满满。 为什么要写《面试1v1》这个专题&#xff1…

排序篇:归并排序的递归,非递归以及计数排序的实现(C语言)

目录 一:归并排序 (1)归并排序的基本思想 (2)递归版本 ①实现思路 ②合并 ③递归实现有序 ④最终代码 (3)非递归版本 ①实现思路 ②控制分组 ③最终代码 (4)时间,空间复杂度分析 (5)小结 二:计数排序 (1)计数排序的基本思想 …