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

前言

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


一、数据结构——链表

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

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

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

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

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

typedef struct Listnode{
	int data;//数据,int可以换别的,看要放什么data
	Listnode* 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 ){		//判断当前节点等于val
            removed = search;			//removed含义,方便释放节点内存
            search = search->next;	    //search移到下一位
            removed_pred->next = search;	//跨过待删除的节点,指向下一位。
            delete removed;
        }
        //判断当前节点不等于val
        removed_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 != nullptr
        while(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{			//必须要有else
                search = 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.mfbz.cn/a/749081.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《化工管理》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问&#xff1a;《化工管理》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的第一批认定学术期刊。 问&#xff1a;《化工管理》级别&#xff1f; 答&#xff1a;国家级。主办单位&#xff1a;中国石油和化学工业联合会 主管单位&…

API-其他事件

学习目标&#xff1a; 掌握其他事件 学习内容&#xff1a; 页面加载事件元素滚动事件页面尺寸事件 页面加载事件&#xff1a; 加载外部资源&#xff08;如图片、外联CSS和JavaScript等&#xff09;加载完毕时触发的事件。 为什么要学&#xff1f;&#xff1f; 有些时候需要等…

华为认证hcna题库背诵技巧有哪些?hcna和hcia有什么区别?

大家都知道华为认证hcna是有题库供考生刷题备考的&#xff0c;但题库中海量的题目想要在短时间背诵下来可并不是一件容易的事情&#xff0c;这就需要我们掌握一定的技巧才行。华为认证hcna题库背诵技巧有哪些? hcna和hcna这二者又有什么区别呢?今天的文章将为大家进行详细解…

IMU坐标系与自定义坐标系转化

1.首先示例图为例&#xff1a; 虚线黑色角度为IMU的坐标系&#xff1b;实线为自定义坐标系&#xff1b; 矫正&#xff1a;&#xff08;默认angleyaw为IMU采的数据角度&#xff09; angleyaw_pt angleyaw-25;if(-180<angleyaw&&angleyaw<-155) // 角度跳变问…

防火墙GRE over IPSec配置

一、基础知识 1、GRE隧道 GRE隧道是一种网络通信协议&#xff0c;使用通用路由封装&#xff08;GRE&#xff09;技术&#xff0c;能够将一种网络协议下的数据报文封装在另一种网络协议中&#xff0c;从而实现在另一个网络层协议中的传输。 GRE隧道的基本概念和工作方式 基本…

怎样实现聊天弹幕效果?

可以使用HTML、CSS和JavaScript的组合。以下是一个简单的步骤和示例代码&#xff0c;说明如何创建一个基本的弹幕效果&#xff1a; HTML结构&#xff1a; 创建一个用于显示弹幕的容器和输入弹幕的表单。 <!DOCTYPE html> <html lang"en"> <hea…

android 通过gradle去除aar的重复资源图片

背景&#xff1a;项目中引入了aar包&#xff0c;结果导致资源出问题了&#xff0c;于是需要对下面aar包进行重复资源去除操作 操作具体如下&#xff1a; 目录&#xff1a;app/build.gradle 末尾配置 apply from: "${project.rootDir}/scripts/excludewidgetAar.gradle&qu…

20240626(周三)AH股行情总结:沪指午后大反弹,港股震荡走高,AIGC、短剧概念走强,低价可转债触底反弹

内容提要 上证指数午后大反弹&#xff0c;创业板指涨近2%。港股震荡走高&#xff0c;恒生科技指数涨近1%。AIGC概念领涨&#xff0c;ST股、贵金属板块领跌。低价可转债集体大涨&#xff0c;广汇转债涨20%触发临停&#xff0c;广汇汽车今日上演地天板。 周三&#xff0c;A股午…

Django项目部署:uwsgi+daphne+nginx+vue部署

一、项目情况 项目根目录&#xff1a;/mnt/www/alert 虚拟环境目录&#xff1a;/mnt/www/venv/alert 激活虚拟环境&#xff1a;source /mnt/www/venv/alert/bin/activate 二、具体配置 1、uwsgi启动配置 根目录下&#xff1a;新增 uwsgi.ini 注意&#xff1a;使用9801端…

NSSCTF-Web题目17(反序列化)

目录 [SWPUCTF 2021 新生赛]pop 1、题目 2、知识点 3、思路 [NISACTF 2022]popchains 4、题目 5、知识点 6、思路 [SWPUCTF 2021 新生赛]pop 1、题目 2、知识点 php反序列化&#xff0c;代码审计 3、思路 打开题目 出现代码&#xff0c;接下来我们逐步对代码进行分析…

模型情景制作-冰镇啤酒

夏日炎炎&#xff0c;当我们在真实世界中开一瓶冰镇啤酒的时候&#xff0c;我们也可以为模型世界中的人物添加一些冰镇啤酒。 下面介绍一种快速酒瓶制造方法&#xff0c;您只需要很少工具&#xff1a; 截取尽量直的流道&#xff08;传说中的板件零件架&#xff09;,将其夹在您的…

惠普笔记本双指触摸不滚屏

查看笔记本型号 一般在笔记本背面很小的字那里 进入惠普官网 笔记本、台式机、打印机、墨盒与硒鼓 | 中国惠普 (hp.com) 选择“支持”>“解决问题”>“软件与驱动程序” 选择笔记本 输入型号&#xff0c;选择操作系统 下载驱动进行完整 重启之后进行测试

404 Not Found(nginx)

#vue-router history 配置location / {add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Headers *;add_header Cross-Origin-Embedder-Policy require-corp;add_header Cross-Origin-Opener-Policy same-origin;try_files $uri $uri/ router;index …

阿里云centos 7.9 使用宝塔面板部署.netcore 6.0

前言&#xff1a; 我有一个netcore6.0的系统接口和手机端程序的站点程序之前是部署在一台windows测试服务器的IIS站点中&#xff0c; 服务器最近压力太大扛不住了&#xff0c;买了一台centos7.9的阿里云服务器准备进行迁移。具体操作日记如下。 一、安装宝塔面板 这一步涉及…

一个去掉PDF背景水印的思路

起因 昨天测试 使用“https://github.com/VikParuchuri/marker” 将 pdf 转 Markdown的过程中&#xff0c;发现转换后的文件中会保护一些背景图片&#xff0c;是转换过程中&#xff0c;程序把背景图识别为了内容。于是想着怎么把背景图片去掉。 背景水印图片的特征 我这里拿…

花8000元去培训机构学习网络安全值得吗,学成后就业前景如何?

我就是从培训机构学的网络安全&#xff0c;线下五六个月&#xff0c;当时学费不到一万&#xff0c;目前已成功入行。所以&#xff0c;只要你下决心要入这一行&#xff0c;过程中能好好学&#xff0c;那这8000就花得值~ 因为只要学得好&#xff0c;工作两个多月就能赚回学费&am…

MySQL递归查询(with recursive)

背景 日常开发中经常会有那种 阶梯式 数据&#xff0c;比如做地图、菜单&#xff0c;裂变给上级、上上级分红等等这样的需求的时候 你需要找个一个对象的 上级&#xff0c;上上级&#xff0c;上上上级 建了一张很容易理解阶级的表&#xff0c;一目了然 很多时候我们的需求就是…

测试开发工程师需要掌握什么技能?

测试开发工程师是软件开发中至关重要的角色之一。他们负责编写、维护和执行自动化测试脚本、开发测试工具和框架&#xff0c;以确保软件的质量和稳定性。为了成为一名优秀的测试开发工程师&#xff0c;你需要掌握以下技能&#xff1a; 1. 编程技能&#xff1a; 作为测试开发工…

java设计模式(七)适配器模式(Adapter Pattern)

1、模式介绍&#xff1a; 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将一个类的接口转换成客户希望的另外一个接口。适配器模式通常用于需要复用现有的类&#xff0c;但是接口与客户端的要求不完全匹配的情况。它包括两种形式&…

鸿蒙面试心得

自疫情过后&#xff0c;java和web前端都进入了冰河时代。年龄、薪资、学历都成了找工作路上躲不开的门槛。 年龄太大pass 薪资要高了pass 学历大专pass 好多好多pass 找工作的路上明明阳关普照&#xff0c;却有一种凄凄惨惨戚戚说不清道不明的“优雅”意境。 如何破局&am…