关于链表的题目—leetcode

news2024/10/6 14:35:46

第一题:删除链表中的指定节点

问题描述:

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

题目接口:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* deleteNode(struct ListNode* head, int val){

}

问题解答思路: 

这道题的意思就是要删除链表中的值等于val的节点,但是这道题需要考虑两种情况。

第一种情况就是删除头节点,第二种情况就是删除不是头节点的情况。这两种情况可是不一样的,假如我们把这两种情况混为一谈这道题是不一定能通过的。所以我们要分两种情况来解决这道题。

解法1:分头节点与一般节点两种情况

struct ListNode* deleteNode(struct ListNode* head, int val){
        while(head!=NULL&&head->val==val){//处理头节点的问题
            struct ListNode*temp = head;
            head = head->next;//移动head删掉头节点
            free(temp);
        }
        struct ListNode*temp = head;
        struct ListNode*cur = head->next;//因为头节点已经处理完了,所以再让cur指向head是没有意义的,所以让cur指向第二个节点
        while(cur){
            if(cur->val==val){
             temp->next = cur->next;//删除值为val的节点
             cur = cur->next;//再次移动cur
            }
            else{
                temp = cur;//记录cur的原来位置
                cur = cur->next;//cur向下一位移动
            }

        }
return head;
}

解法2:加一个节点在头节点前面

思路:既然第一种解法要我们处理两种情况,那我们可不可以把这两种解法换成一种解法呢?当然可以,只要我们人为的创建一个节点就可以将将头节点的情况去掉了。

虚拟头节点法:

struct ListNode* deleteNode(struct ListNode* head, int val){
    struct ListNode* dumy = (struct ListNode*)malloc(sizeof(struct ListNode));//使用malloc搞出来一个虚拟节点。
    struct ListNode* cur = head;
    struct ListNode* temp = dumy;
    dumy->next = head;//将dump连接在head的前面,是dumy成为一个头节点
    while(cur){//第一种方法中的普通节点处理法,这次就要从head开始删除了。
        if(cur->val==val){
            temp->next = cur->next;
            cur = cur->next;
            temp = cur;

        }
        else{
            temp = cur;
            cur = cur->next;
        }

    }
head = dumy->next;//返回值是dumy->next。这一点要注意
free(dumy);//释放掉dumy,防止内存泄漏
return head;//返回头节点。

}

解法三:尾插法

对于愚笨的我来说,这种方法是我最难理解的,那我就在这里重点讲解一下吧。

首先来看一下代码:

尾插法代码:

struct ListNode* deleteNode(struct ListNode* head, int val){
    struct ListNode* cur  = head;
    struct ListNode* tail = NULL;
    struct ListNode* newnode = NULL;
    while(cur){
        if(cur->val!=val){
            if(tail == NULL){
                tail =newnode = cur;
                
            }
            else{
                tail->next = cur;
                tail = tail->next;
               
            }
            cur = cur->next;
            tail->next = NULL;
        }
        else{
            struct ListNode*  del = cur;
            cur = cur->next;
            free(del);
        }
       
         
    }
    return newnode;

}

尾插法解法过程演示:

首先我们要明确的是,不论这道题怎么写这道题都有三种情况要处理。即:删除头节点,删除中间节点,删除尾节点。

1.删除头节点:

假如我们的链表是:-3->5->99,要删除的值是-3,也就是要删除头节点

 现在我们看看代码是如何走的:下面代码是删除头节点时的执行代码

 struct ListNode* cur  = head;
    struct ListNode* tail = NULL;
    struct ListNode* newnode = NULL;
 else{
            struct ListNode*  del = cur;
            cur = cur->next;
            free(del);
        }

这段代码执行以后:

tail,newnode,cur三者与链表的关系就变成这样了:

然后我们就不用删除头节点了,剩下的节点的处理方式就是下面代码:

 struct ListNode* cur  = head;
    struct ListNode* tail = NULL;
    struct ListNode* newnode = NULL;
    while(cur){
        if(cur->val!=val){
            if(tail == NULL){
                tail =newnode = cur;
                
            }
            else{
                tail->next = cur;
                tail = tail->next;
               
            }
            cur = cur->next;
            tail->next = NULL;
        }

现在,因为tail节点指向空,cur->val!=val。所以执行第二个if语句。

但是此时tail还是要与cur指向的下一个元素有联系。其实这也可以把tail看作是cur的一个拷贝,既然是拷贝tail指向的下一个节点当然就是cur指向的下一个节点了。

代码

  cur = cur->next;
  tail->next = NULL;

 再来看看这一段代码的示意图:

在这里可以看到在执行了cur=cur->next的操作以后,将tail的next置空的操作。这是要特别注意的一点。因为这样的操作可以避免野指针的问题。当然还有一点需要注意的就是这两个代码执行顺序的问题。如果先执行tail->next = NULL的操作,再执行cur = cur->next的操作将会导致栈溢出的问题。原因很简单,因为tail与cur一开始指向的是同一块空间,假如先置空tail的next,cur将找不到自己的next。 

最后,执行以下代码:

else{
       tail->next = cur;
       tail = tail->next;
            }
            cur = cur->next;
            tail->next = NULL;

 

 最后再返回newnode,就得到我们想要得到的结果了。

return newnode;

其它的情况这个代码也是可以过的,在这里就不啰嗦了。 

 

 

 

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

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

相关文章

【redis】缓存预热雪崩穿透击穿

【redis】缓存预热雪崩穿透击穿(上) 文章目录 【redis】缓存预热雪崩穿透击穿(上)前言一、面试题二、缓存预热三、缓存雪崩发生原因预防+解决高可用:多缓存结合: 人民币玩家 四、缓存穿透是什么…

谷歌云端硬盘Drive批量下载大文件或大文件夹的稳定方法

本文介绍在谷歌云端硬盘(Drive)中,快速、稳定下载大文件、文件夹的方法。 在使用谷歌Drive下载文件或文件夹时,我们往往会遇到下载不稳定或失败的情况;在下载较大的文件或文件夹时,这一问题出现的频率更多。…

NLP语义识别在人工智能领域中的应用与前景

自然语言处理(NLP)是人工智能领域中的一个重要分支,它致力于让计算机能够理解并处理人类自然语言。语义识别是NLP中的一个重要技术,它可以使计算机更好地理解人类语言的含义和意图。在本文中,我们将探讨NLP语义识别在人…

4月24号软件更新资讯合集.....

GoFrame v2.4 版本发布,企业级 Golang 开发框架 大家好啊,GoFrame 框架今天发布了 v2.4.0 正式版本啦!👏👏👏👏 该版本最大的亮点在于提供了微服务开发的功能特性、开发工具以及工程脚手架&am…

第三章作业:关系数据库

第三章作业:关系数据库 目录 第三章作业:关系数据库选择题简答题1、关系代数:产生学生成绩表,包括学号、姓名、课程名、学分和成绩。题目代码 2、关系代数:检索选择了课程号为“C2"的学生学号和姓名。题目代码 3、…

月获2万份简历,硕士占比超70%!中欧基金如何破圈打造雇主品牌?

成立于2006年的中欧基金,作为国内首批实现员工持股的基金公司,坚持以人为本,相信优秀的业绩要靠优秀的人才来创造。 因此,中欧基金在完善公司治理机制基础上,实现不仅有敢打硬仗能打胜仗的将才,还有更多不…

Pytorch损失函数

基本用法 criterion LossCriterion() #构造函数有自己的参数loss criterion(x, y) #调用标准时也有参数 1 L1范数损失 L1Loss 计算 output 和 target 之差的绝对值。 torch.nn.L1Loss(reductionmean)参数: reduction-三个值 none: 不使用约简; me…

S32K系列MCU学习介绍

前言 最近因为工作需要,在学习恩智浦的S32K312,开发一款汽车PDU。 一、S32K3系列 1.特点 S32K系列是恩智浦公司于2017年推出的面向汽车电子的微控制器。S32K3 系列包括基于 Arm Cortex-M7 的 MCU,采用单步、双步和锁步内核配置&#xff0…

滴水逆向3期笔记与作业——01汇编

防止OneNote丢失。 海哥牛逼。 01汇编笔记 01进制进制定义10-2进制转换八进制 02数据宽度/逻辑运算数据宽度与存储逻辑运算计算机做加法的本质作业 03通用寄存器_内存读写通用寄存器表通用寄存器图内存读写计算机操作系统位数意义 04内存地址_堆栈寻址公式PUSH指令POP指令作业 …

《稻》念袁老,孙溟㠭先生为纪念袁隆平老先生治印一方

孙溟㠭篆刻作品《稻》 孙溟㠭篆刻作品《稻》 稻穗熟了,袁老走了。溟㠭先生为纪念袁隆平老先生而治印一枚。 拓印左侧禾苗繁茂,稻田里蛙声一片。拓印右侧为袁老的样子,人瘦心厚,顶着烈日,照料自己试验的稻田。袁老一…

3.黑马springboot开发篇自己修改笔记

SpringBoot开发实用篇 ​ KF-1.热部署 ​ 什么是热部署?简单说就是你程序改了,现在要重新启动服务器,嫌麻烦?不用重启,服务器会自己悄悄的把更新后的程序给重新加载一遍,这就是热部署。 ​ 热部署的功能…

[渗透教程]-017-入侵检测与社交网络安全

文章目录 1.入侵检测1.1 入侵检测基本概念1.2 入侵艰难侧系统评估指标1.3 入侵检测基本技术1.4 通用入侵检测框架2.社交网络安全1.入侵检测 1.1 入侵检测基本概念 入侵检测(Intrusion Detection),指对系统的运行状态进行监视,发现各种攻击企图、攻击行为或者攻击结果,以保证…

大神们分享STM32的学习方法

单片机用处这么广,尤其是STM32生态这么火!如何快速上手学习呢? 第一:你要考虑的是,要用STM32实现什么 为什么使用STM32而不是8051? 是因为51的频率太低,无法满足计算需求?是51的管脚太少,无法…

MySQL单表操作

二、数据的新增、修改、删除 1.回顾ishop的数据表 mysql> use ishop; Database changed mysql> show tables; ----------------- | Tables_in_ishop | ----------------- | commodity | | commoditytype | | customer | | order | ----------…

【人工智能】遗传算法

人工智能算法---遗传算法(基础篇) 知识导图:遗传算法(概念)1.初始化种群二进制编码与解码 2.选择操作3.交叉操作4.评估操作5.终止操作 知识导图: 遗传算法(概念) 可以把遗传算法类比…

关于API接口应用

随着互联网技术的发展,API接口已成为众多应用程序开发中的必备工具,它不仅方便了开发者进行应用程序开发,也为应用程序提供了更多的功能和服务。本文将介绍API接口的概念和应用,以及API接口的优势和未来趋势。 一、什么是API接口…

Android 系统架构大图

android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和Linux核心层。 1.应用程序 Android会同一系列核心应用程序包一起发布,该应用…

Kubernetes快速部署

2 Kubernetes快速部署 kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具。 这个工具能通过两条指令完成一个kubernetes集群的部署&#xff1a; # 创建一个 Master 节点 $ kubeadm init# 将一个 Node 节点加入到当前集群中 $ kubeadm join <Master节点的IP和…

堆排序详解

如有错误&#xff0c;感谢不吝赐教、交流 文章目录 算法原理堆大根堆构建大根堆 小根堆 Java实现完整代码总结 算法原理 堆 堆是一个数组&#xff0c;可以被看成一个近似的完全二叉树&#xff0c;树上的每一个结点对应数组中的一个元素。除了最底层外&#xff0c;该树是完全…

Linux如何定时执行任务

目录 crontab 介绍 安装crontab 服务操作说明 操作案例 crontab 介绍 Linux crontab是采用定期执行程序的命令&#xff0c;当安装完成操作 系统后&#xff0c;默认便会启动此任务调度命令&#xff0c;crond命令每分钟都会定期检查是否要执行任务的工作&#xff0c;如果要执…