LeetCode.203移除链表元素(原链表操作、虚拟头结点)

LeetCode.203移除链表元素

  • 1.问题描述
  • 2.解题思路
  • 3.代码

1.问题描述

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

2.解题思路

以链表 1 4 2 4 来举例,移除元素4。

如果使用C,C++编程语言的话,不要忘了还要从内存中删除这两个移除的节点, 清理节点内存之后如图:

当然如果使用java ,python的话就不用手动管理内存了。(就算使用C++来做leetcode,如果移除一个节点之后,没有手动在内存中删除这个节点,leetcode依然也是可以通过的,只不过,内存使用的空间大一些而已,但建议依然要养成手动清理内存的习惯。)

那么:上述例子中,删除第一个4,只要将1的next指针直接指向下下个节点就好了。但这样,如果删除的是头结点就不好办了,于是两个办法。

  1. 直接使用原来的链表来进行删除操作。

    其余节点:用上述法子便行

    头结点:移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。

    所以只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。依然别忘将原头结点从内存中删掉。

  2. 设置一个虚拟头结点在进行删除操作。

    这里来给链表添加一个虚拟头结点为新的头结点,此时要移除这个旧头结点元素1。就和移除链表其他节点的方式统一了。最后return 头结点的时候,别忘了 return dummyNode->next;, 这才是新的头结点

3.代码

C++:直接使用原来的链表来进行删除操作

#include <iostream>
#include <vector>
using namespace std;

struct ListNode {
	int val;
	ListNode* next;
	ListNode(int x): val(x), next(NULL) {}
};

class Solution {
	public:
		ListNode* removeElements(ListNode* head, int val) {
			// 删除头结点值等于val的节点
			//为什么不用if,因为若输入为head = [1,1,1,1,1],应为一个持续移除的过程,所以为while
			//头结点不能为空,如果为空,相当于操作空指针了
	 		while(head != NULL && head->val == val) {
				//创建了一个临时指针tmp,它指向链表的头结点head。
				//这是为了保存需要被删除的节点,以便在将指针从链表中删除时,能够释放该节点所占用的内存。
				ListNode* tmp = head;
				head = head->next;
				delete tmp;  
			}

			ListNode* cur = head;
			//cur != NULL: 确保当前节点不为空。在删除节点时,我们需要更改节点的指针来连接正确的节点,
			//所以我们要确保cur指针不为空,避免访问空指针导致程序崩溃。
			//cur->next != NULL: 确保当前节点的下一个节点不为空。在删除节点时,
			//我们需要访问当前节点的下一个节点的值来进行比较,如果下一个节点为空,
			//则没有必要进行比较和删除操作
			while(cur != NULL && cur -> next != NULL) {
				if(cur->next->val == val) {
					ListNode* tmp = cur->next;
					cur->next = cur->next->next;
					delete tmp;
				} else {
					cur = cur->next;
				}

			}
			return head;
		}
};

int main() {    
    vector<int> nums = {1, 2, 6, 3, 4, 5, 6}; // 用数组存储链表节点的值    
    ListNode* head = NULL; // 链表的头节点初始化为NULL    
  
    // 将数组的值转换为链表节点并加入到链表中  
    for(int i = 0; i < nums.size(); i++) {   
        ListNode* node = new ListNode(nums[i]);    
        if (head == NULL) {  
            head = node; // 如果链表为空,则新节点即为头节点  
        } else {  
            ListNode* temp = head;  
            while(temp->next != NULL) {  
                temp = temp->next;  
            }  
            temp->next = node; // 将新节点添加到链表末尾  
        }  
    }    
    
    Solution solve;    
    head = solve.removeElements(head, 6); // 删除值为6的节点  
  
    // 输出新链表的值  
    ListNode* current = head;  
    while(current != NULL) {   
        cout << current->val << " ";    
        current = current->next;    
    }    
  
    return 0;    
}

C++:虚拟头结点

#include <iostream>
#include <vector>
using namespace std;

struct ListNode {
	int val;
	ListNode* next;
	ListNode(int x): val(x), next(NULL) {}//构造函数
};

class Solution {
	public:
		ListNode* removeElements(ListNode* head, int val) {
			ListNode* dummyHead = new ListNode(0);//创建虚拟头节点
			dummyHead->next = head;//虚拟头节点指向头节点
			//临时指针从虚拟头节点开始遍历,如果直接用头结点进行遍历,那么头结点所指向的值是一直在改变的,最后无法返回了
			ListNode* cur = dummyHead;
			while(cur->next != NULL) {
				if(cur->next->val == val) {
					ListNode* tmp = cur->next;//暂存要删除的节点
					cur->next = cur->next->next;//删除节点
					delete tmp;//释放内存
				} else {
					cur = cur->next;//指向下一个节点
				}
			}
			head = dummyHead->next;//更新头节点  因为旧的head可能已经被删除了
			delete dummyHead;//释放虚拟头节点内存
			return head;//返回头节点
		}
};

int main() {    
    vector<int> nums = {1, 2, 6, 3, 4, 5, 6}; // 用数组存储链表节点的值    
    ListNode* head = NULL; // 链表的头节点初始化为NULL    
  
    // 将数组的值转换为链表节点并加入到链表中  
    for(int i = 0; i < nums.size(); i++) {   
        ListNode* node = new ListNode(nums[i]);    
        if (head == NULL) {  
            head = node; // 如果链表为空,则新节点即为头节点  
        } else {  
            ListNode* temp = head;  
            while(temp->next != NULL) {  
                temp = temp->next;  
            }  
            temp->next = node; // 将新节点添加到链表末尾  
        }  
    }    
    
    Solution solve;    
    head = solve.removeElements(head, 6); // 删除值为6的节点  
  
    // 输出新链表的值  
    ListNode* current = head;  
    while(current != NULL) {   
        cout << current->val << " ";    
        current = current->next;    
    }    
  
    return 0;    
}

python:虚拟头结点

class ListNode:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next


class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        dummyHead = ListNode()
        dummyHead.next = head
        cur = dummyHead
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return dummyHead.next


values = [1, 2, 6, 3, 4, 5, 6]

# 使用循环构造链表
dummy = ListNode(0)  # 创建一个虚拟头节点以简化边界情况处理
current = dummy  # 当前节点初始化为虚拟头节点

for v in values:
    current.next = ListNode(v)
    current = current.next

head = dummy.next  # 实际头节点为虚拟头节点的下一个节点

# 移除元素并打印新链表
sol = Solution()
new_head = sol.removeElements(head, 6)

cur = new_head
while cur:
    print(cur.val)
    cur = cur.next

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

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

相关文章

保护模式进阶

本系列文章只做个人学习记录使用 参考资料&#xff1a; 《操作系统真象还原》 从0到-1写一个操作系统 获取物理内存容量 计算机要想被使用&#xff0c;就必须先管理&#xff0c;我们想和物理内存打交道&#xff0c;就必须先知道物理内存有多大 linux获取内存的方法 在linux…

C语言做一个恶作剧关机程序

一、项目介绍 C语言实现一个简单的"流氓软件"&#xff0c;一个可以强制关机恶作剧关机程序&#xff0c;输入指定指令可以解除 二、运行截图 然后当你输入“n”才可以解锁关机。 三、完整源码 #include <stdlib.h> #include <stdio.h> #include <s…

【挑战业余一周拿证】一、亚马逊云科技简介 - 第 2 节 - 模块 简介

CSDN 官方中文视频&#xff08;免费&#xff09;&#xff1a;点击进入 第 2 节 - 模块 1 简介 这门课程将为您提供需要了解的所有重要信息&#xff0c;让您能够轻松讨论亚马逊云科技并了解它为 何对您的企业有利 亚马逊云科技为每个企业都提供了非常广泛的服务&#xff0c;从…

【Linux】信号

Linux 信号 1.信号介绍2.core dump3.发送信号3.1.kill3.2.send3.3.abort 4.信号产生4.1.软件条件产生信号4.1.1.SIGPIPE4.1.2.SIGALRM 4.2.硬件异常产生信号 5.信号处理6.可重入函数 & volatile7.SIGCHLD 1.信号介绍 信号本质是一种通知机制。 而进程要处理信号&#xff0…

Docker Swarm总结+基础、集群搭建维护、安全以及集群容灾(1/4)

博主介绍&#xff1a;Java领域优质创作者,博客之星城市赛道TOP20、专注于前端流行技术框架、Java后端技术领域、项目实战运维以及GIS地理信息领域。 &#x1f345;文末获取源码下载地址&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb;…

PyQt6 QLabel标签控件

​锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计21条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话…

jQuery_08 each函数的使用

each函数的使用 可以循环数组&#xff0c;json&#xff0c;dom对象数组 1.$.each(要循环的内容,function(index,element){处理函数}) 要循环的内容可以是数组&#xff0c;json对象&#xff0c;dom数组 function&#xff1a;循环的处理函数 每个成员都会执行这个函数一次 index&…

5.golang字符串的拆解和拼接

字符串是 Go 中的字节切片。可以通过将一组字符括在双引号中来创建字符串" "。Go 中的字符串是兼容Unicode编码的&#xff0c;并且是UTF-8编码的。 访问字符串的单个字节或字符 由于字符串是字节切片&#xff0c;因此可以访问字符串的每个字节。 func printStr(s …

Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap

在 Spring Boot 项目中&#xff0c;我们经常使用 YAML 文件来配置应用程序的属性。在这篇博客中&#xff0c;我将模拟如何在 Java 的 Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap。 1. 介绍 YAML&#xff08;YAML Aint Markup Language&#xff09;是一种人类…

建造者模式-C语言实现

UML类图&#xff1a; 代码实现&#xff1a; #include <stdio.h> #include <stdlib.h>// 产品类 typedef struct {char* part1;char* part2;char* part3; } Product;// 抽象建造者类 typedef struct {void (*buildPart1)(void*, const char*);void (*buildPart2)(v…

2023-3年CSDN创作纪念日

机缘 今天开开心心出门去上班&#xff0c;就收到了一个csdn私信&#xff0c;打开一看说是给我惊喜来着&#xff0c;我心想csdn还能给惊喜&#xff1f;以为是有什么奖品或者周边之类的&#xff0c;结果什么也没有&#xff0c;打开就是一份信&#x1f602;。 也挺不错的&#xf…

java: nio之DirectByteBuffer

package nio;import java.nio.ByteBuffer; import java.nio.IntBuffer;public class DirectTest {public static void main(String[] args) {ByteBuffer byteBuffer ByteBuffer.allocateDirect(1024);} }

Android 相机库CameraView源码解析 (一) : 预览

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…

由于找不到vcruntime140.dll无法继续执行代码-提供5个修复方法分你对比

摘要&#xff1a;本文将介绍vcruntime140.dll文件的作用及其在程序运行中的重要性&#xff0c;并提供五个解决vcruntime140.dll无法继续执行的方法。 一、vcruntime140.dll文件介绍 vcruntime140.dll是Windows操作系统中的一项重要文件&#xff0c;它是由Microsoft Visual C提…

『OPEN3D』1.8 点云的配准理论

点云的配准是将不同的3D点云对齐成一个完成的点云模型&#xff1b;配准的目标是找到两帧点云之间的相对旋转&#xff08;rotation&#xff09;与平移&#xff08;translation&#xff09;&#xff0c;使得两份点云中有重叠的区域能够完好拼接。 点云配准示例图&#xff08;来自…

NX二次开发UF_CURVE_ask_joined_parms 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_joined_parms Defined in: uf_curve.h int UF_CURVE_ask_joined_parms(tag_t joined_curve_feature, UF_STRING_p_t uf_curve_string, int * creation_method, double …

Windows TCP 通信测试_1

一、单对单通信测试 应用函数 socket、bind、connect、listen、accept、recv、send&#xff08;win下的函数&#xff09;等 1、客户端demo client.cpp #include<WINSOCK2.H> #include<STDIO.H> #include<iostream> #include<cstring> using namespa…

【C++】多线程(一):std::thread的使用

这篇文章应我朋友的邀请&#xff0c;写一篇文章介绍下C多线程。 编译环境准备 首先确定你的编译器支持std的thread&#xff0c;如果不支持&#xff0c;就会出现诸如“thread找不到”的问题。 以下假设你使用 gnu gcc 编译器&#xff0c;因为 MSVC 的我也不太熟悉。 linux …

【挑战业余一周拿证】二、在云中计算 - 第 1 节 - 模块2 简介

第 1 节 - 模块2 简介 无论你的企业是属于像医疗、保健、制造、保险等等行业 , 再或者 , 您的服务是向全世界的数百万用户提供视频、、图片或者文字服务,你也需要服务器来为您的业务和应用程序提供支持,服务器的作用是帮助您托管应用程序并提供满足您业务需求的计算能力. 当你使…

机器学习笔记 - 3D对象检测技术路线调研(未完)

一、3D对象检测简述 3D对象检测是计算机视觉中的一项任务&#xff0c;其目标是根据对象的形状、位置和方向在 3D 环境中识别和定位对象。它涉及检测物体的存在并实时确定它们在 3D 空间中的位置。这项任务对于自动驾驶汽车、机器人和增强现实等应用至关重要。 1、基本流程 给定…