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

news2024/11/23 4:36:23

前言

链表篇,开始。
记录六:力扣【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.coloradmin.cn/o/1869404.html

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

相关文章

互联网企业出海不得不面对的安全问题

在出海的互联网企业中&#xff0c;为什么游戏、电商企业总是被“D”&#xff1f;究其内因&#xff0c;这或与游戏和电商等业务的商业模式和技术应用有较大的关系。 首先&#xff0c;对于游戏和电商等行业而言&#xff0c;良好的用户体验是业务增长的关键点。对于普通用户而言&a…

当大模型开始「考上」一本

参加 2024 河南高考&#xff0c;豆包和文心 4.0 过了一本线&#xff0c;但比 GPT-4o 还差点。 今天的大模型&#xff0c;智力水平到底如何&#xff1f; 2024 年高考陆续出分&#xff0c;我们想要解开这个过去一年普罗大众一直争论不休的话题。高考是衡量人类智力和学识水平的…

鸿蒙:自定义组件、自定义函数、自定义样式

一、自定义组件 1.新建组件文件夹&#xff0c;新建自定义组件文件 . 2.编辑自定义组件&#xff0c;并通过 Component //声明组件 export struct PageHeader { //结构体private title: stringbuild() { //uiRow() {Image($rawfile(左返回.png)).width(15%)Text(this.title)…

C++实现一个简单的Qt信号槽机制

昨天写这个文章《深入探讨C的高级反射机制&#xff08;2&#xff09;&#xff1a;写个能用的反射库》的时候就在想&#xff0c;是不是也能在这套反射逻辑的基础上&#xff0c;实现一个类似Qt的信号槽机制&#xff1f; Qt信号槽机制简介 所谓的Qt的信号槽&#xff08;Signals …

吉时利 Keithley2461 数字源表

Keithley2461吉时利SMU高电流数字源表 2461 型图形化高电流数字 SourceMeter SMU 2461 高电流 SMU 凭借其 10A/1000W 脉冲电流和 7A/100W 直流电流能力以及双 18 位 1MS/s 数字转换器&#xff0c;优化用于检定和测试高功率材料、器件和模块&#xff0c;例如碳化硅 (SiC)、氮化…

WIFI各版本的带宽

带宽的定义&#xff1a; 带宽在网络领域通常指信道带宽&#xff0c;即信号在频谱中占用的频宽&#xff0c;单位是MHz&#xff08;兆赫&#xff09;。在无线通信中&#xff0c;带宽越宽&#xff0c;能够传输的数据量越大&#xff0c;因此信道带宽直接影响着数据传输速率。WiFi标…

LeetCode刷题之HOT100之岛屿数量

2024 6/27 酷暑难耐&#xff0c;天气热了&#xff0c;似乎更容易午睡了。上午上了cnn最后一节课。睡一觉买一杯蜜雪冰城&#xff0c;坐在舒适的实验室敲击键盘&#xff0c;做题&#xff01; 1、题目描述 2、逻辑分析 是的&#xff0c;又是直奔题解的一天哈&#xff01;题解给…

基于MATLAB仿真设计无线充电系统

通过学习无线充电相关课程知识&#xff0c;通过课程设计无线充电系统&#xff0c;将所学习的WPT&#xff0c;DC-DC&#xff0c;APFC进行整合得到整个无线充电系统&#xff0c;通过进行仿真研究其系统特性&#xff0c;完成我们预期系统功能和指标。 以功率器件为基本元件&#x…

鸿蒙Harmony开发案例教程:如何进行蓝牙设备发现、配对、取消配对功能

如何进行蓝牙连接 场景说明 蓝牙技术是一种无线数据和语音通信开放的全球规范&#xff0c;它是基于低成本的近距离无线连接&#xff0c;为固定和移动设备建立通信环境的一种特殊的连接。本示例通过ohos.bluetoothManager接口实现蓝牙设备发现、配对、取消配对功能。 效果呈现…

《Nest系列 - 4. 听说人人都会CRUD,可是我还不会怎么办???-《4.3结合前端使用实现多表联合查询》

&#x1f351; 联合查询 在我们前端来说&#xff0c;会抽离一些公用组件。不会把重复的组件或者所有代码都放在一个文件夹下。对于后端来说&#xff0c;也是一样的&#xff0c; 我们不会把所有数据都放在一张表里&#xff0c;我们回进行分表&#xff0c;根据一些关联关系&…

为什么说展厅数字人是展览未来的趋势?

展厅数字人是利用数字化、智能化和网络化等信息技术手段提升展厅展览服务和游览体验的全新载体。随着人工智能和虚拟现实技术的应用发展&#xff0c;展厅数字人已成为展厅展览转型升级的重要趋势。 展厅数字人凭借其创新性、强可塑性&#xff0c;成为展厅新名片&#xff0c;为各…

数据库工具之 —— SQLite

数据库工具之 —— SQLite SQLite 是一个非常流行的轻量级数据库&#xff0c;它是一个嵌入式的数据库&#xff0c;意味着数据库文件是存储在磁盘上的一个单一文件。SQLite 不需要一个独立的服务器进程&#xff0c;这使得它非常适合用于小型应用、移动应用、桌面应用&#xff0…

阿里云centos7.9 挂载数据盘 并更改宝塔站点根目录

一、让系统显示中文 参考&#xff1a;centos7 怎么让命令行显示中文&#xff08;英文-&#xff1e;中文&#xff09;_如何在命令行中显示中文-CSDN博客 1、输入命令&#xff1a;locale -a |grep "zh_CN" 可以看到已经存在了中文包 2、输入命令&#xff1a;sudo vi…

Django REST framework安全实践:轻松实现认证、权限与限流功能

系列文章目录 Django入门全攻略&#xff1a;从零搭建你的第一个Web项目Django ORM入门指南&#xff1a;从概念到实践&#xff0c;掌握模型创建、迁移与视图操作Django ORM实战&#xff1a;模型字段与元选项配置&#xff0c;以及链式过滤与QF查询详解Django ORM深度游&#xff1…

ROT5、ROT13、ROT18、ROT47全系列加解密小程序

ROT5、ROT13、ROT18、ROT47全系列加解密小程序 这几天在看CTF相关的课程&#xff0c;涉及到古典密码学和近代密码学还有现代密码学。自己编了一个关于ROT全系列的加、解密小程序。 ​ ROT5、ROT13、ROT18、ROT47 编码是一种简单的码元位置顺序替换暗码。此类编码具有可逆性&a…

use embeddings stored in vector db to reduce work for LLM generating response

题意&#xff1a;使用存储在向量数据库中的嵌入来表示&#xff0c;以减少大型语言模型&#xff08;LLM&#xff09;生成响应的工作量。 问题背景&#xff1a; Im trying to understand what the correct strategy is for storing and using embeddings in a vector database, …

计算机网路面试HTTP篇三

HTTPS RSA 握手解析 我前面讲&#xff0c;简单给大家介绍了的 HTTPS 握手过程&#xff0c;但是还不够细&#xff01; 只讲了比较基础的部分&#xff0c;所以这次我们再来深入一下 HTTPS&#xff0c;用实战抓包的方式&#xff0c;带大家再来窥探一次 HTTPS。 对于还不知道对称…

海报在线制作系统源码小程序

轻松设计&#xff0c;创意无限 一款基于ThinkPHPFastAdminUniApp开发的海报在线制作系统&#xff0c; 本系统不包含演示站中的素材模板资源。​ 一、引言&#xff1a;设计新纪元&#xff0c;在线海报制作引领潮流 在数字时代&#xff0c;海报已成为传播信息、展示创意的重要媒…

松下的台灯值得入手吗?书客、飞利浦热门品牌横评分享!

自从儿子步入小学&#xff0c;他埋首于书桌前的时光愈发冗长&#xff0c;很欣慰他能够认真专心学习&#xff0c;却也隐隐担忧他的视力健康。在了解视力健康中发现长时间在过暗或过亮的光线环境下学习&#xff0c;会导致瞳孔频繁地收缩与扩张&#xff0c;极易引发视觉疲劳。更令…

Isaac Sim 9 物理(1)

使用Python USD API 来实现 Physics 。 以下内容中&#xff0c;大部分 Python 代码可以在 Physics Python 演示脚本文件中找到&#xff0c;本文仅作为个人学习笔记。 一.设置 USD Stage 和物理场景 Setting up a USD Stage and a Physics Scene USD Stage不知道怎么翻译&#…