【链表OJ题(一)】移除链表元素

news2025/1/13 10:12:46

在这里插入图片描述

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:数据结构
🎯长路漫漫浩浩,万事皆有期待

文章目录

  • 链表OJ题(一)
    • 1. 移除链表元素
      • 思路一-遍历
        • 一、考虑常见情况
        • 二、考虑特殊情况
          • 1.当链表的第一个结点为待移除的结点时
          • 2.当链表的最后一个结点为待移除的结点时
          • 3.当传入链表为空时
      • 思路二-尾插法
        • 一、考虑常见情况
        • 二、考虑特殊情况
          • 1.当链表的最后一个结点为待删除的结点时
          • 2.当传入链表为空时
          • 2.当要删除所有节点时
      • 思路三-强行加上一个头结点
  • 2.总结:

链表OJ题(一)

1. 移除链表元素

链接:203. 移除链表元素
题目描述:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
在这里插入图片描述

示例1:
输入: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
输出:[]

提示:
列表中的节点数目在范围 [0, 104] 内
1 <= Node.val <= 50
0 <= val <= 50

思路一-遍历

要移除链表中值为val的结点,我们肯定是要将链表遍历一遍,关键是我们在遍历的过程中应该如何操作。我们考虑问题的时候,可以先考虑比较常见的情况,再考虑特殊情况。

一、考虑常见情况

要移除某一结点,也就是让该结点的前一个结点指向待移除结点的后一个结点,然后将待移除结点释放即可。我们可以定义3个指针变量:prev,cur,next 。

prev:记录待排查结点的前一个结点位置(previous)。
cur:记录当前正在排查的结点位置(current)。
next:记录待排查结点的后一个结点(next)。
在这里插入图片描述

当cur指针指向的结点并非待移除的结点时,3个结点依次向后移动。
在这里插入图片描述

当cur指针指向待移除的结点时,我们首先让prev指针指向的结点指向next,然后将cur指针指向的结点释放掉
在这里插入图片描述

并将next指针赋值给cur指针,next指针再后移。
在这里插入图片描述

如此进行下去,直到链表遍历完毕,那么值为val的结点也就删除了。

二、考虑特殊情况

常见情况的分析往往只能解决问题的一般情况,并不能解决问题的极端情况。要真正解决问题,我们需要考虑到问题的极端情况。例如,当待移除的结点是第一个结点或是最后一个结点的情况,当链表为空的情况。

1.当链表的第一个结点为待移除的结点时

在这里插入图片描述
这时我们需要先将头指针指向next,然后释放cur指向的结点
在这里插入图片描述
并将next指针赋值给cur指针,next指针再后移。
在这里插入图片描述

2.当链表的最后一个结点为待移除的结点时

当排查到最后一个结点时,cur指向最后一个结点,next指针指向该结点指向的位置,即NULL。
在这里插入图片描述

我们用上面常规情况的方法对其进行分析,发现常规情况的思路适用于这种特殊情况。
在这里插入图片描述
并且发现遍历的终止条件,就是当cur为NULL的时候遍历停止。
在这里插入图片描述

3.当传入链表为空时

我们可以发现,若传入的链表为空链表(NULL),cur指针的值一开始就为空,而我们遍历的终止条件就是当cur为NULL时停止遍历,所以当传入链表为空时,直接执行到函数末尾,即返回头指针(NULL)。

代码实现

struct ListNode {
	int val;
	struct ListNode *next;
};

struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* prev = NULL;//记录待排查结点的前一个结点位置
	struct ListNode* cur = head;//记录当前正在排查的结点位置
	while (cur != NULL)//当cur为空时,循环停止
	{
		if (cur->val == val)//当前排查的结点是待移除的结点
		{
			struct ListNode* next = cur->next;//记录待排查结点的后一个结点位置
			if (cur == head)//待移除的结点是链表的第一个结点
			{
				head = next;//头指针指向next
				free(cur);//释放第一个结点
				cur = next;//将next指针赋值给cur指针
			}
			else//待移除的结点不是链表的第一个结点
			{
				prev->next = next;//prev指针指向的结点指向next
				free(cur);//将cur指针指向的结点释放掉
				cur = next;//将next指针赋值给cur指针
			}
		}
		else//当前排查的结点不是待移除的结点
		{
			prev = cur;//指针后移
			cur = cur->next;//指针后移
		}
	}
	return head;//返回新的头指针
}

在这里插入图片描述

思路二-尾插法

一、考虑常见情况

还可以通过遍历原链表,将不是val的值尾插到新链表newHead,这时删第一个也没有什么影响,转换成尾插的思路。
定义一个尾指针tail,第一次尾插需要赋值,newHead=tail=cur
在这里插入图片描述
cur->val!=val,尾插进新链表,再更新tail,tail->next=cur,tail=tail->next
在这里插入图片描述

cur->val==val,提前保存下一个节点,释放cur,将下一个赋值给cur
在这里插入图片描述
最后返回newHead

二、考虑特殊情况

在这里插入图片描述

1.当链表的最后一个结点为待删除的结点时

在删除最后一个(6)的时候,上一个节点tail(5)的next还指向(6),删除后就指向野指针了,所以需要tail->next=NULL
在这里插入图片描述

但如果最后一个为(7),便会拿下来尾插,tail->next!=NULL,这时不会出现野指针
在这里插入图片描述

那综合上面两种情况,不如直接置空(tail->next=NULL)吧。但运行后发现还是有问题
在这里插入图片描述

2.当传入链表为空时

在链表为空时,循环根本不会进入,所以还要加一个链表为空的判断if(head==NULL),直接return NULL,不删除了。但运行后发现还是有问题
在这里插入图片描述

2.当要删除所有节点时

若链表全是7的时候,这时所以节点都被删了,没有节点尾插了,newHead和tail还是空,又出现了野指针,所以干脆不判断链表为空,而是判断tail是否为空,若不为空,tail->next=NULL

 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode*newHead=NULL,*tail=NULL;
    struct ListNode*cur=head;
    while(cur)
    {
        if(cur->val!=val)
        {
            //尾插
            if(tail==NULL)
            {
                newHead=tail=cur;
            }
            else
            {
                tail->next=cur;
                tail=tail->next;
            }
                cur=cur->next;
        }
        else
        {
        struct ListNode*next=cur->next;
        free(cur);
        cur=next;
        }
    }
    if(tail)
        tail->next=NULL;

    return newHead;
}

在这里插入图片描述

思路三-强行加上一个头结点

我们可能觉得思路一的代码比较复杂,当我们要移除某一个结点时,还需要判断该结点是否为第一个结点,那么有没有什么办法可以不用进行这一步操作呢?
回答是肯定的。办法就是在传入的链表前面强行加上一个头结点,并让链表原来的头指针指向该头结点,这样我们就不用判断待移除的结点是否为第一个结点了(因为现在第一个结点是头结点)。
在这里插入图片描述

在加了头结点后,我们就只需要根据常见情况的逻辑进行代码的编写即可。但是有一点不能忘记,就是在遍历完链表后要将头结点指向的位置(即第一个结点的位置)赋值给头指针,并将头结点释放掉,最后才能返回头指针。
在这里插入图片描述
代码实现

struct ListNode {
	int val;
	struct ListNode *next;
};
struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));//申请一个头结点,返回其地址
	guard->next = head;//让头结点指向链表的第一个结点
	struct ListNode* cur = guard->next;//cur指针指向原链表第一个结点
	struct ListNode* prev = guard;//prev指针指向头结点
	while (cur != NULL)//当cur为空时,循环停止
	{
		if (cur->val == val)//当前排查的结点是待移除的结点
		{
			struct ListNode* next = cur->next;//记录待排查结点的后一个结点位置
			prev->next = next;//prev指针指向的结点指向next
			free(cur);//将cur指针指向的结点释放掉
			cur = next;//将next指针赋值给cur指针
		}
		else//当前排查的结点不是待移除的结点
		{
			prev = cur;//指针后移
			cur = cur->next;//指针后移
		}
	}
	head = guard->next;//将头结点指向的位置赋值给头指针,使头指针指向链表第一个结点
	free(guard);//释放头结点
	guard = NULL;//及时置空
	return head;//返回新的头指针
}

在这里插入图片描述

2.总结:

今天我们通过三种思路分析并完成移除链表元素这道链表OJ题目。总体来说,思路三会相较于前两个思路减少踩坑,但若是第一次遇见,也很难想到。希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

在这里插入图片描述

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

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

相关文章

FreeRTOS入门(06):任务通知

文章目录目的基础说明使用演示作为二进制信号量作为计数信号量作为事件组作为队列或邮箱相关函数总结目的 任务通知&#xff08;TaskNotify&#xff09;是RTOS中相对常用的用于任务间交互的功能&#xff0c;这篇文章将对相关内容做个介绍。 本文代码测试环境见前面的文章&…

「JVM 高效并发」锁优化

为了线程间更高效的共享数据及解决竞争问题&#xff0c;提高程序执行效率&#xff0c;JDK 6 做了大量锁优化&#xff0c;如适应性自旋&#xff08;Adaptive Spinning&#xff09;、锁消除&#xff08;Lock Elimination&#xff09;、锁膨胀&#xff08;Lock Coarsening&#xf…

2、监控界面设计

【任务描述】本任务要求使用相对布局或约束布局以及相应的控件完成智慧园区监控系统界面开发一、相对布局&#xff08;RelativeLayout&#xff09;概述相对布局&#xff08;RelativeLayout&#xff09;是一种根据父容器和兄弟控件作为参照来确定控件位置的布局方式。使用相对布…

《机器学习》- 习题解析 - 第一章

《机器学习》- 习题 - 第一章 文章目录《机器学习》- 习题 - 第一章一、示例-计算表1.1中的版本空间二、习题 1 - 计算题目中的版本空间三、单个合取式&析合范式的概念四、习题 2 - 计算题目中假设空间的规模大小一、示例-计算表1.1中的版本空间 首先从概念上理解版本空间…

一起玩转开源数据库!OceanBase DevCon 之开源生态全景解析

​ 2023 年 3 月 25 日&#xff0c;首次 OceanBase 开发者大会将在北京举办&#xff0c;OceanBase 首席科学家阳振坤与 OceanBase CTO 杨传辉领携众多技术专家&#xff0c;将与开发者共同探讨单机分布式、云原生、HTAP 等数据库前沿趋势&#xff0c;OceanBase 开源技术全景生…

数据库——1.数据库设计的三大范式

这篇文章我们主要来讲一下数据库设计的三大范式&#xff0c;这个还是很有用的。 目录 1.概述 2.第一范式 3.第二范式 4.第三范式 5.小结 1.概述 为了建立冗余较小、结构合理的数据库&#xff0c;设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范…

Python每日一练(20230301)

目录 1. 只出现一次的数字 2. 以特殊格式处理连续增加的数字 3. 最短回文串 1. 只出现一次的数字 给定一个非空整数数组&#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 说明&#xff1a; 你的算法应该具有线性…

「TCG 规范解读」基础设施架构和协议 (2)

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…

有什么好用的在线统计表单吗?

有什么好用的在线统计表单吗&#xff1f;最好是免费的&#xff1f;市面上这样的表单工具其实很多&#xff0c;先来看看题主的需求&#xff1a; 收集信息&#xff0c;数据统计数据分析&#xff0c;报表展示 以简道云在线表单为例&#xff0c;能完美实现题主这两个需求—— http…

携程面经1

面经 HDFS读写流程 1.读流程 客户端向NameNode发起读请求&#xff08;如果存在&#xff09;NameNode返回一批block地址客户端与第一个block的拓扑距离最近的节点建立连接以packet&#xff08;64kb&#xff09;的单位读取数据块。一个block读取完成后客户端会断开与该DataNod…

算法训练营 day59 动态规划 两个字符串的删除操作 编辑距离

算法训练营 day59 动态规划 两个字符串的删除操作 编辑距离 两个字符串的删除操作 583. 两个字符串的删除操作 - 力扣&#xff08;LeetCode&#xff09; 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符…

DBeaver连接mysql数据库图文教程

文章目录前言一、DBeaver连接mysql数据库二、文档下载地址前言 DBeaver是免费、开源、通用数据库工具&#xff0c;是许多开发开发人员和数据库管理员的所选。下面详细介绍Dbeaver连接mysql数据库的过程。 一、DBeaver连接mysql数据库 1、 打开Dbeaver后&#xff0c;按下图操…

【Unity】P4 脚本文件(基础)

Unity脚本文件&#xff08;基础&#xff09;适配的C#代码编辑器如何添加一个脚本文件获取蘑菇当前位置基础代码改变物体位置帧与帧更新前言 上一篇博文主要围绕Unity Inspector部分&#xff0c;围绕组件&#xff0c;资源文件&#xff0c;父子节点部分做介绍。 链接&#xff1a;…

阿里黑客入门学习资料流出来了!!

各位粉丝朋友大家好&#xff0c;最近看到很多粉丝朋友给我留言&#xff0c;希望我给大家找一些学习内容。前段时间整理了我平时常看的一些黑客相关的技术书籍&#xff0c;这些内容从未对外公开&#xff0c;今天分享给大家 &#xff01; 内容非常详细且全面&#xff0c;覆盖了W…

5分钟轻松拿下Java枚举

文章目录一、枚举(Enum)1.1 枚举概述1.2 定义枚举类型1.2.1 静态常量案例1.2.2 枚举案例1.2.3 枚举与switch1.3 枚举的用法1.3.1 枚举类的成员1.3.2 枚举类的构造方法1&#xff09;枚举的无参构造方法2&#xff09;枚举的有参构造方法1.3.3 枚举中的抽象方法1.4 Enum 类1.4.1 E…

c++系列12:使用vscode进行编译

1. 入门 1.1 操作方法 1&#xff09;下载安装vscode 2&#xff09;在扩展中搜索c/c extension pack并安装&#xff08;或者直接打开cpp文件&#xff0c;会自动提示进行安装&#xff09; 3&#xff09;创建项目目录&#xff0c;会自动生成.vscode文件夹&#xff0c;里面是编译…

虹科分享 | Domo零售行业商业智能白皮书:《从零售企业的数据中获取价值》

市场因素、技术创新和不断增长的客户期望&#xff0c;给电子商务带来了新的机遇&#xff0c;与此同时也给传统零售行业带来了压力。零售业正面临着新的挑战&#xff1a;不断变化的需求模式和渠道、不断变化的服务期望、复杂的库存以及交付问题。为了解决这些问题&#xff0c;零…

Linux系统介绍及熟悉Linux基础操作

一、什么是Liunx Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹&#xff08;Linus Benedict Torvalds&#xff09;于1991年10月5日首次发布&#xff0c;它主要受到Minix和Unix思想的启发&am…

机器学习知识总结 —— 21. 什么是主成分分析

文章目录什么是PCA&#xff08;Principal Component Analysis&#xff09;协方差矩阵什么是协方差协方差矩阵特征值与特征向量PCA降维什么是PCA&#xff08;Principal Component Analysis&#xff09; 在机器学习中&#xff0c;PCA&#xff08;Principal Component Analysis&a…

除了Confluence,还有哪些好用的文档管理软件?测评

在早期&#xff0c;文档管理软件主要是为了将企业内部海量的电子文档集中存储、管理&#xff0c;通过设置共享权限进行内部员工的文档分发&#xff0c;有些甚至可能要提供API接口&#xff0c;便于将ERP、OA等系统的文档纳入其中&#xff0c;形成企业文档管理中心。而随着时间的…