算法力扣刷题记录六【203移除链表元素】

前言

链表篇,开始。
记录六:力扣【203移除链表元素】


一、数据结构——链表

来源【代码随想录】,总结:
(1)线性结构。内存地址不连续,通过指针指向串联一起。
(2)链表类型:

  • 单链表
    • 单一方向。一个指针,指后继元素即可。在这里插入图片描述
  • 双向链表
    • 两个方向。一个节点内两个指针,一个指前,一个指后。
      在这里插入图片描述
  • 循环链表
    • 链表首尾相连。最后一个节点的指针不是null,而是指向头结点。可以用来解决约瑟夫环问题。
      在这里插入图片描述

(3)链表定义(用C++)

//定义节点
struct Listnode{int data;//数据,int可以换别的,看要放什么dataint* p;//指针,单链表就一个//int* pred;指前面的指针//int* succ;指后面的指针
};

C++中struct和class类似,同样可以用成员函数,都是自定义类型。
可以typedef另起一个名字。比如:

typedef struct Listnode{int data;//数据,int可以换别的,看要放什么dataListnode* pred;//指前面的指针Listnode* succ;//指后面的指针
}Node;
Node n1;
n1.data = 5;

(4)链表操作
总结:操作指针。

  • 删除元素记得用一个临时指针指向被删掉的元素,自己释放掉,不然指针一改,就找不到被删的那个元素,但还在内存中占地。
  • 添加元素,改指针指向。
  • 添加/删除,都是O(1),就一个元素。但前提得先查找到位置。
  • 所以查找(遍历)过程是O(n)。

二、题目阅读和理解

题目阅读

给你一个链表的头节点 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

二、第一次尝试

思路

(1)先看下对于单链表节点的定义。
(2)从头结点循环往下找节点,遇到=val删掉改变指针。

虽然知道链表删除都是操作指针但实现时需要注意方法。

实现成功

先上代码,通过测试:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {ListNode* removed_pred = nullptr;ListNode* removed = nullptr;ListNode* search = nullptr ;//遍历指针if(head){while(head->val == val){removed = head;if(!(head->next)){head = nullptr;delete removed;return head;}head = head->next;//search = search->next;delete removed;}removed_pred = head;search = head -> next;while(search !=nullptr){   if(search->val == val ){removed = search;search = search->next;removed_pred->next = search;delete removed;}else{removed_pred = search;search = search->next; }}return head;}else{return head;}}
};

解释过程

(1)需要返回头结点:

当头结点=val时,head需要一直后移;当头结点不等于val时,head不需要再移动,只用看后面的节点谁等于val,被删去,所以这里需要一个指针来查找后面元素,此时head固定不再移动。

所以先实现头结点=val,head一直后移:
while(head->val == val){  //当head不等于val时,不再循环,开始往后查找,head也就固定不动。removed = head;	//由于考虑到删除节点所在内存,所以用removed指针先指向被删除的节点,等链表指针改动完成后,delete。head = head->next;  //头指针需要后移;(有缺陷)search = search->next; //search是查找指针,初始化为head;delete removed;	//把“应该删去的节点”内存释放掉}再实现当head固定时,查找节点并删除操作:
while(search -> next){   				//search指向正在被判断的节点,看它要不要删去;当操作到最后一个节点时,无法进入循环,所以后面要特别处理。if(search->val == val ){		//判断当前节点等于valremoved = search;			//removed含义,方便释放节点内存search = search->next;	    //search移到下一位removed_pred->next = search;	//跨过待删除的节点,指向下一位。delete removed;}//判断当前节点不等于valremoved_pred = search;		    // removed_pred指向待删除节点的前一位,需要记下来,改变  removed_pred的指向。search = search->next;}if(search -> val == val){           //处理最后一个节点removed_pred->next = nullptr;}return head;	//返回链表

(2)注意“-> next”很容易赋值到nullptr(空指针),导致访问空指针的运行错误。

所以还要考虑以下可能:

  • head指向,初始传入参数head == nullptr;所以修正一

    在最外层加if(head),如果不为空,开始while操作head;else 直接返回head;

  • 链表只有一个节点,且该节点 = val。所以修正二

    //这两行很容易赋值到nullptr,紧跟着后面while(search -> next)导致访问空指针运行错误。
    head = head->next; //头指针需要后移;(有缺陷)
    search = search->next;

  • 用例[1,2,1],val=2。进入while(search -> next)后,走到if条件符合进入if逻辑,此时removed_pred=nullptr,又一次操作了空指针。所以修正三

修正结束:

//通过测试
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {ListNode* removed_pred = nullptr;ListNode* removed = nullptr;ListNode* search = nullptr ;//遍历指针if(head){while(head->val == val){removed = head;if(!(head->next)){head = nullptr;delete removed;return head;}head = head->next;//search = search->next;delete removed;}removed_pred = head;search = head -> next;while(search !=nullptr){   if(search->val == val ){removed = search;search = search->next;removed_pred->next = search;delete removed;}else{removed_pred = search;search = search->next; }}return head;}else{return head;}}
};

最终得到提交版本。


代码随想录

学习内容

(1)解法一:移除头节点和中间节点分开操作。

//发现即使分开操作的思想,我写的也很复杂。重新实现(有错),一写就废
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {while((head != nullptr)&&(head->val == val)){head=head->next;ListNode* tmp = head;	//这里出错,先tmp再head=head->next。delete tmp;}ListNode* cur = head;    //while(cur->next != nullptr )//同时判断cur != nullptrwhile(cur->next != nullptr && cur != nullptr){	//有错if(cur->next->val == val){ListNode* tmp = cur->next;cur -> next=cur->next->next;delete tmp;}else{cur = cur->next;}}return head;}
};

注意

  • 先把要删除的节点给tmp后,再移动指针。
  • while((cur != nullptr)&&(cur->next != nullptr) ) //先判断cur再判断cur->next

(2)解法:虚拟头节点:统一节点删除操作。

//根据dummy node思想尝试写一遍
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {ListNode* dummy_node = new ListNode(0,head);ListNode* search = dummy_node;while(search->next != nullptr){if(search ->next-> val ==val){ListNode* removed = search->next;       search->next = search->next->next;delete removed;}else{			//必须要有elsesearch = search->next;}}delete dummy_node;	//dummy_node也要释放。return dummy_node->next;}
};

总结

(1)判断指针空不空,while(search != nullptr)。while(search)是错的。
(2)加一个虚拟头节点,可以统一删除和添加操作。不用区分删除/添加时:头节点还是中间节点。

(欢迎指正,转载标明出处)


最后有个问题,希望能得到解决

我写出下面的逻辑,但是运行时力扣报错:我非法访问,但我找不到地方。显示说search=head处。请问该怎么解决,欢迎留言。

Line 74: Char 28:
=================================================================
==20==ERROR: AddressSanitizer: heap-use-after-free on address 0x502000000098 at pc 0x5599d37f919c bp 0x7ffd3cb611f0 sp 0x7ffd3cb611e8
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {ListNode* removed_pred = nullptr;//ListNode* removed = nullptr;ListNode* search = nullptr ;//遍历指针while(head != nullptr){if(head->val == val){ListNode* removed = head;head = head->next;delete removed;}else{break;  //当head不在等于val,head固定。进入查找删除。}}search = head;while((search != nullptr) && (search->next != nullptr)  ){removed_pred = search;if(search->val == val ){ListNode* removed = search;search = search->next;removed_pred->next = search;    //如果没有(search->next != nullptr),这里操作空指针。delete removed;}else{search = search->next; }} return head;}
};

自我回答:

找到问题了:
在if(search->val == val )这里判断的是search,而不是search->next->val。

所以改完之后,也就是学习内容中的解法一

//测试通过
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {ListNode* search = nullptr ;//遍历指针while(head){if(head->val == val){ListNode* removed = head;head = head->next;delete removed;}else{break;  //当head不在等于val,head固定。进入查找删除。}}search = head;while(search != nullptr && search->next != nullptr){if(search->next->val == val ){ListNode* removed = search->next;search->next = search->next->next;delete removed;}else{search = search->next; }}return head;}
};

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

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

相关文章

PacBio or Nanopore:测序技术简单对比

前言 在基因组学和生命科学领域&#xff0c;追求知识的旅程不断演变&#xff0c;由揭示DNA和RNA奥秘的技术创新推动。我们熟知的两大测序技术——PacBio和Nanopore&#xff0c;正位于这一领域的前沿。这些由 Pacific Biosciences 和 Oxford Nanopore Technologies 分别开发的先…

实力认可!安全狗受聘成为福建省网信系统2024年度网络安全技术支撑单位

6月6日&#xff0c;福建省委网信办组织召开福建省网信系统2024年度网络安全技术支撑单位座谈会。 作为国内云原生安全领导厂商&#xff0c;安全狗也受邀出席此次活动。 省委宣传部副部长、省委网信办主任、省互联网信息办公室主任张远出席会议并颁发支撑单位证书。安全狗凭借出…

App推广新选择:Xinstall专属地址推广,让你的品牌瞬间引爆市场!

在移动互联网时代&#xff0c;App的推广与运营成为了每个开发者必须面对的重要课题。然而&#xff0c;随着市场竞争的日益激烈&#xff0c;如何让自己的App在众多竞争者中脱颖而出&#xff0c;成为了每个开发者最为关注的问题。今天&#xff0c;我们将为大家介绍一款能够帮助你…

浅谈Mysql Innodb存储引擎

一、Mysql整体架构 二、MySQL 5.7 支持的存储引擎 类型 描述 MyISAM 拥有较高的插入、查询速度&#xff0c;但不支持事务 InnoDB 5.5版本后Mysql的默认数据库&#xff0c;5.6版本后支持全文索引&#xff0c;事务型数据库的首选引擎&#xff0c;支持ACID事务&#xff0c;支…

深入探索Java开发世界:Redis~类型分析大揭秘

文章目录 深入探索Java开发世界&#xff1a;Redis~类型分析大揭秘一、数据结构类型二、分布式锁类型三、事物命令类型四、事物三大特性类型 深入探索Java开发世界&#xff1a;Redis~类型分析大揭秘 Redis数据库基础知识&#xff0c;类型知识点梳理~ 一、数据结构类型 Redis是一…

Vue项目生产环境的打包优化

Vue项目生产环境的打包优化 前言 在这篇文章我们讨论Vue项目生产环境的打包优化&#xff0c;并按步骤展示实际优化过程中的修改和前后对比。 背景 刚开始的打包体积为48.71M 优化 步骤一&#xff1a;删除viser-vue viser-vue底层依赖antv/g2等库一并被删除&#xff0c;…

【selenium 】操作元素

操作元素 元素操作鼠标操作键盘操作 元素操作 元素操作示例清空输入框clear()deiver.find_element_by_id(“username”).clear()输入文字send_keys()deiver.find_element_by_id(“username”).send_keys(‘zs’)元素点击 click()deiver.find_element_by_id(“login”).click()…

720漫游快速入门

720云全景漫游制作工具自2014年8月上线后&#xff0c;为数十万创作者提供全景图片&全景视频&高清矩阵上传、编辑、分享一站式软件服务&#xff0c;获得众多创作者的青睐与认可&#xff0c;同时也承载了创作者越来越多的期望&#xff0c;为了给大家提供更灵活多样的功能…

【Linux】动/静态库的创建和使用

目录 一、动/静态库的概念回顾&#xff1a; 二、动态库与静态库的区别&#xff1a; 三、静态库的创建与使用&#xff1a; 1、Linux静态库命名规则&#xff1a; 2、静态库的创建和使用&#xff1a; 四、动态库的创建与使用&#xff1a; 1、Linux动态库命名规则&#xff1…

【python】pop()函数

python pop() &#xff0c;如何在Python的列表或数组中移除元素 使用 pop() 从列表中删除元素 pop() 语法概述 pop() 方法的语法如下&#xff1a; list_name.pop(index)list_name&#xff1a;列表变量名&#xff1b;内置的 pop() 方法仅需要一个可选参数&#xff1b;可选参…

Windows若要成为最受欢迎的操作系统,则需要解决5个问题

序言 Windows正变得与我在过去几十年中第一次喜欢和使用的操作系统大不相同&#xff0c;但尽管我觉得它偏离了崇高的道路&#xff0c;如果我可以成为CEO一天&#xff0c;那么我会改变以下五件事&#xff0c;让世界上最受欢迎的操作系统重回正轨。 非必要更新的永久延期 多年来…

鸿蒙HarmonyOS自定义组件开发和使用

自定义组件的介绍 在开发和使用自定义组件直接&#xff0c;我们需要了解什么是自定义组件&#xff1f; 在ArkUI中&#xff0c;UI显示的内容均为组件&#xff0c;由框架直接提供的称为系统组件&#xff0c;由开发者定义的称为自定义组件。在进行 UI 界面开发时&#xff0c;通常…

坑——python的redis库的decode_responses设置

python的redis库查询返回的值默认是返回字节串&#xff0c;可以在redis.Redis()方法中通过设置decode_responses参数&#xff0c;让返回值直接是字符串&#xff1b; 查询返回字节串是因为Redis()方法中decode_responses默认值是False&#xff1a; 设置decode_responses为True就…

【从零开始实现联邦学习】

1. 环境配置如下 python3.7pip install torchpip install torchvision 2. 代码如下 原书的代码存在一点bug&#xff0c;现已被作者修复 Client端代码如下 import torch.utils.dataclass Client(object):def __init__(self,conf,model,train_dataset,id1):self.conf conf …

为什么不再推荐使用 VRTK 4?

引言 VRTK (Virtual Reality Toolkit) 发布于2016年&#xff0c;初期受到了广大开发者的欢迎并被广泛采用。但是随着 VR 开发生态的发展&#xff0c;这款工具逐渐失去了最初的光芒。本文试图通过几个维度的分析&#xff0c;解释为什么目前不推荐使用 VRTK 进行开发的理由&…

Eigen中关于四元数的常用操作

四元数&#xff08;Quaternion&#xff09;是一种数学工具&#xff0c;广泛用于计算机图形学、机器人学和物理模拟中&#xff0c;特别适合处理三维旋转。Eigen库是一个高性能的C数学库&#xff0c;提供了丰富的线性代数功能&#xff0c;其中就包括对四元数的支持。 1. 为什么选…

mklink

文章目录 mklink概述笔记备注END mklink 概述 看一个开源工程中&#xff0c;有一个.bat脚本&#xff0c;用来建立符号链接。 用的是mklink, 试试&#xff0c;比快捷方式好用。 笔记 测试环境 win10x64-22H2 准备测试用的文件 D:\my_tmp\dir1\readme.txt mklink的命令行帮助…

Windows平台使用S3Browser连接兼容的对象存储

本文记录了在Windows平台使用S3Browser连接兼容的对象存储的过程 一、安装S3Browser 1、下载 S3Browser官网&#xff1a;https://s3browser.com/ 直接下载&#xff1a;https://s3browser.com/download/s3browser-11-6-7.exe 2、安装 3、同意授权后确定安装目录 4、勾选立即…

第7章 Redis的噩梦:阻塞

文章目录 前言1 发现阻塞2.内在原因2.1API或数据结构使用不合理2.1.1如何发现慢查询2.1.2.如何发现大对象 2.2 CPU饱和2.3 持久化阻塞2.3.1fork阻塞2.3.2.AOF刷盘阻塞2.3.3.HugePage写操作阻塞 3 外在原因3.1CPU竞争3.2 内存交换 前言 Redis是典型的单线程架构&#xff0c;所有…

Studying-代码随想录训练营day23| 39.组合总和、40.组合总和II、131.分割回文串

第23天&#xff0c;回溯part02&#xff0c;回溯两个题型组合&#xff0c;切割(ง •_•)ง&#x1f4aa; 目录 39.组合总和 40.组合总和II 131.分割回文串 总结 39.组合总和 文档讲解&#xff1a;代码随想录组合总和 视频讲解&#xff1a;手撕组合总和 题目&#xff1a;…