【leetcode 力扣刷题】链表基础知识 基础操作

链表基础知识 基础操作

  • 链表基础操作
    • 链表基础知识
    • 插入节点
    • 删除节点
    • 查找节点
  • 707. 设计链表
    • 实现:单向链表:
    • 实现:双向链表

链表基础操作

链表基础知识

在数据结构的学习过程中,我们知道线性表【一种数据组织、在内存中存储的形式】是线性结构的,其中线性表包括顺序表和链表。数组就是顺序表,其各个元素在内存中是连续存储的。
链表则是由数据域指针域组成的结构体构成的,数据域是一个节点的数据,指针域存储下一个节点的地址,下一个节点依靠上一个节点的指针域寻址。给出整个链表的头节点地址(指针)head后,就能依次访问链表的每个节点。
链表定义:

链表是一种递归的数据结构,它或者为空(null),或者是指向一个结点(node)的引用,该节点还有一个元素和一个指向另一条链表的引用。

根据链表间节点的指向,链表可以分为以下三种:

  • 单向链表:指针域中只有指向下一个节点的地址;只能单向遍历链表;
    在这里插入图片描述
  • 双向链表:指针域中有两个指针,一个指向前一个节点,一个指向下一个节点;可以双向遍历链表;
    在这里插入图片描述
  • 循环链表:链表形成环,最后一个节点的指针域不赋值为null,而是指向头节点head;
    在这里插入图片描述

定义一个链表节点,是单向的链表:

	//定义一个结构体ListNode表示链表节点struct ListNode {int val;   //数据域,这里用的int,根据实际情况选择type,比如char、float等ListNode *next; //指针域,存下一个节点的地址,所以是指针类型,并且下一个节点也是该结构体,所以是ListNode*//自定义构造函数,给数据域和指针域赋值ListNode() : val(0), next(nullptr) {}ListNode(int x) : val(x), next(nullptr) {}ListNode(int x, ListNode *next) : val(x), next(next) {}};

链表中头节点直接用head指针访问,其他节点用currentNode->next的指针访问,可见链表的头节点是特殊的。在插入、删除操作中,针对头节点都需要做特殊的处理,会较麻烦,因此在头节点前增加一个虚拟头节点【也叫附加头节点、哨兵节点等】dummy_head,虚拟头节点的指针域存head,即dummy_head->next = head;数据域无效,因为后续不会用到。给定dummy_head后,其他节点都用当前节点的next指针访问,即currentNode->next:
在这里插入图片描述
接下来介绍对链表进行的一些操作【穿针引线法】:

插入节点

在链表中的一个位置插入节点,需要先断开插入位置前后两个节点的链接,再和这两个节点建立新的链接:先cur->next = pre->next;再pre->next = cur; 如果先pre->next = cur,就会丢失sur这个节点的地址。
在这里插入图片描述
上面是普通的情况,那么针对头节点,尾节点的特殊情况呢? 末尾节点其实也没有什么特殊的,只是suc是NULL,也就是pre->next为NULL,按照上述的两步操作也是ok的。问题是在头节点前插入:

  • 如果是普通链表,头节点前什么都没有,pre是NULL的。只进行①:cur->next = head,head = cur【头节点是新插入的节点】;
  • 如果前面有虚拟头节点,那么pre就是dummy_head,头节点和其他节点是一样的操作;

删除节点

从链表中删除节点,可以是删除指定位置的,比如删除第三个节点;也可以是根据节点值删除的,比如删除值等于target的节点。删除节点时,将被删除节点的前面节点和后面节点连接起来的同时,断开被删除节点和其前面一个、后面一个节点的连接,并且要释放掉被删除节点的空间:pre->next = cur->next;delete cur。
在这里插入图片描述
针对头节点和尾节点的特殊情况呢? 将尾节点看作是下一个节点是null的节点,处理和其他节点一样。问题同样是头节点,删除头节点的话:

  • 普通链表,直接tmp = head->next,delete head,head = tmp;
  • 添加了虚拟头节点的链表,删除头节点,其pre = dummy_head,cur = head;直接按照正常节点删除即可。
    从删除头节点和在头节点前插入节点的分析就可以知道,添加了虚拟头节点dummy_head后,对头节点的操作不需要单独讨论,所有节点操作一致。

查找节点

比如按位置查找某个节点,返回其节点值;比如按值查找链表中是否存在值等于target的节点。因为链表是通过节点的指针域而将各个节点连接起来的,它不能像数组一样直接按下标查找【数组各元素在内存空间是连续存储的,通过下标就能够定位到内存空间】。要查找链表中某个节点,需要遍历链表,需要O(n)的时间复杂度。

707. 设计链表

题目链接:707.设计链表
题目内容:
在这里插入图片描述
理解题意:实际上就是实现一个链表类,可以单向也可以双向,其中要涉及到插入节点:在头部插入、在尾部插入、根据下标index在指定位置插入;删除节点;根据下标index获取节点值。

实现:单向链表:

以下代码实现的是有虚拟头节点的单向链表,并且在类中定义一个_size变量存储链表中节点数量,以便根据下标index删除、添加节点时,判断下标是否合理。 另外在节点末尾处添加节点,可认为是index = _size时,在index处插入节点:

class MyLinkedList {
private://定义类的私有变量int _size;   //链表中节点数量,不包括虚拟头节点//定义链表节点结构体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) {}};//链表附加头节点,整个链表的开始ListNode* _dummyhead;
public:    //自定义类的构造函数MyLinkedList() {_dummyhead = new ListNode(0); //新建虚拟头节点_size = 0; //初始化链表中有效节点数量为0}//根据下标返回节点valueint get(int index) {//下标无效if(index >= _size)return -1;ListNode* currNode = _dummyhead; //从虚拟头节点开始访问//遍历到下标为index的节点for(int i = 0; i <= index; i++){currNode = currNode->next;}        return currNode->val;  //返回value}//在头部添加节点void addAtHead(int val) {ListNode * newNode = new ListNode(val); //先构造一个新节点newNode->next = _dummyhead->next; //直接在虚拟头节点后插入_dummyhead->next = newNode;_size++;   //插入节点后,链表节点数量+1}//在尾部添加节点 //实际上就是在index为_size的地方添加节点void addAtTail(int val) {addAtIndex(_size,val);}//在指定下标index位置处插入节点void addAtIndex(int index, int val) {if(index > _size) return ;  //下标不合理ListNode* newNode = new ListNode(val); //构造一个新节点ListNode* prevNode = _dummyhead;//找到需要插入的地方while(index)  {prevNode = prevNode->next;index--;}   //插入节点   newNode->next = prevNode->next;prevNode->next = newNode;_size++;  //节点数量++}//删除指定位置的节点void deleteAtIndex(int index) {if(index >= _size) return;  //下标不合理ListNode* prevNode = _dummyhead;//找到要删除的节点的前一个节点while(index){prevNode = prevNode->next;index--;}//tmp为要删除的节点ListNode* tmp = prevNode->next;//要删除节点前后两个节点建立新连接prevNode->next = tmp->next;//删除tmp节点,释放空间delete tmp;_size--; //链表中节点数量-1;}
};

实现:双向链表

双向链表就是每个节点有两个指针,一个指向前驱节点(preNode),一个指向后驱节点(succNode),插入、删除节点时,需要对两个指针的指向都处理:

  • 插入一个节点时,preNode->next = newNode; newNode->next = succNode; succNode->prior = newNode; newNode->prior = preNode;
  • 删除一个节点时:preNode->next = succNode;succNode->prior = preNode;
    这里需要注意的是,succNode实际上是currNode->next,如果是删除最后一个节点或者在最后一个节点后追加一个节点,succNode=NULL,为了和其他节点统一操作,同样在末尾增加一个虚拟尾节点
    双向链表可以双向遍历,在index位置插入、删除、查询节点值时,可以先判断index是在链表前半段还是后半段,确定是从前往后遍历更快,还是从后往前遍历更快。 代码如下(C++):
class MyLinkedList {
private:int _size;   //链表中有效节点数量,不包括虚拟头节点、虚拟尾节点struct ListNode {  //定义链表节点结构体int val;ListNode *next, *prior;  //双向链表需要有两个指针ListNode() : val(0), next(nullptr), prior(nullptr) {}ListNode(int x) : val(x), next(nullptr), prior(nullptr) {}ListNode(int x, ListNode *next, ListNode *prior) : val(x), next(next), prior(prior) {}};ListNode *_dummyhead, *_dummytail; //虚拟头节点和虚拟尾节点
public:    MyLinkedList() {_dummyhead = new ListNode(0); //虚拟头节点_dummytail = new ListNode(0); //虚拟尾节点//建立虚拟头节点和虚拟尾节点之间的连接_dummyhead->next = _dummytail;  _dummytail->prior = _dummyhead;_size = 0; //链表中有效节点数量}int get(int index) {//下标无效if(index >= _size)return -1;ListNode *currNode;//判断index在前半段if(index < _size/2){currNode = _dummyhead;//从前往后遍历更快for(int i = 0; i <= index; i++){currNode = currNode->next;} }else{ //index在后半段currNode = _dummytail;//从后往前遍历更快for(int i = _size-1; i >= index ;i--){currNode = currNode->prior;}}return currNode->val;}//在头部添加节点,即在虚拟头节点后插入void addAtHead(int val) {ListNode *newNode = new ListNode(val);//建立四条新的连接_dummyhead->next->prior = newNode;newNode->next = _dummyhead->next;_dummyhead->next = newNode;newNode->prior = _dummyhead;_size++;}//在尾部插入节点,等于在index = _size的位置插入节点void addAtTail(int val) {addAtIndex(_size,val);}//在指定index处插入void addAtIndex(int index, int val) {if(index > _size) return ;ListNode* newNode = new ListNode(val);ListNode *prevNode = _dummyhead, *succNode = _dummytail;if(index < _size/2){ //从前往后更快定位到index前的节点for(int i = 0; i < index; i++){prevNode = prevNode->next;}succNode = prevNode->next;}else{ //从后往前更快定位到index前的节点for(int i = _size - 1; i >= index; i--){succNode = succNode->prior;}prevNode = succNode->prior;}//插入一个节点后,新增四条连接succNode->prior = newNode;     newNode->next = succNode;prevNode->next = newNode;newNode->prior = prevNode;_size++;}//删除index处的节点void deleteAtIndex(int index) {if(index >= _size) return;ListNode *prevNode = _dummyhead, *succNode = _dummytail;//根据index和_size的关系,决定从前往后遍历还是从后往前遍历if(index < _size/2){for(int i = 0; i < index; i++){prevNode = prevNode->next;}succNode = prevNode->next->next;}else{for(int i = _size - 1; i > index; i--){succNode = succNode->prior;}prevNode = succNode->prior->prior;}ListNode* tmp = prevNode->next;//preNode和succNode之间建立双向连接prevNode->next = succNode;        succNode->prior = prevNode;delete tmp;_size--;}
};

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

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

相关文章

java八股文面试[多线程]——合适的线程数是多少

知识来源&#xff1a; 【并发与线程】 合适的线程数量是多少&#xff1f;CPU 核心数和线程数的关系&#xff1f;_哔哩哔哩_bilibili

03.sqlite3学习——数据类型

目录 sqlite3学习——数据类型 SQL语句的功能 SQL语法 SQL命令 SQL数据类型 数字类型 整型 浮点型 定点型decimal 浮点型 VS decimal 日期类型 字符串类型 CHAR和VARCHAR BLOB和TEXT SQLite 数据类型 SQLite 存储类 SQLite 亲和类型(Affinity)及类型名称 Boo…

如何使用PHP实现多语言网站功能

如何使用PHP实现多语言网站功能 在全球化的今天&#xff0c;开发多语言网站成为了一个必要的需求。PHP作为一种常用的编程语言&#xff0c;可以很方便地实现多语言网站功能。本文将介绍如何使用PHP实现多语言网站功能&#xff0c;并提供相应的代码示例。 一、创建语言文件 首先…

如果将PC电脑变成web服务器:使用python3监测公网IP实现DDNS

如果将PC电脑变成web服务器&#xff1a;使用python3监测公网IP实现DDNS 上一篇文章中&#xff0c;我们使用Nignx的反向代理和端口转发实现域名访问家里主机上的web了。 由于家庭宽带基本都是动态IP&#xff0c;每当你重启一次光猫&#xff0c;IP地址就会变化一次。当光猫因为…

ubuntu修改默认文件权限umask

最近在使用ubuntu的过程中发现一个问题&#xff1a; 环境是AWS EC2&#xff0c;登录用户ubuntu&#xff0c;系统默认的umask是027&#xff0c;修改/etc/profile文件中umask 027为022后&#xff0c;发现从ubuntu用户sudo su过去root用户登录查询到的umask还是027&#xff0c;而…

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解

目录 1、导入导出声明 2、C函数名称改编与extern "C" 3、函数调用约定与跨语言调用 3.1、函数调用约定 3.2、跨语言调用dll库接口 3.3、函数调用约定以哪个为准 4、def文件的使用 5、在C程序中引用ffmpeg库中的头文件链接报错问题 6、最后 VC常用功能开发汇…

基于RUM高效治理网站用户体验入门-价值篇

用户体验 用户体验基本包含访问网站的性能、可用性和正确性。通俗的讲&#xff0c;就是一把通过用户访问测量【设计者】意图的尺子。 本文目的 网站如何传递出设计者的意图&#xff0c;可能收页面加载时间太长、或者页面在用户的浏览器中渲染时间太慢&#xff0c;或者第三方设…

使用Maven父工程构建spring boot子工程

1.父工程删除src目录&#xff0c;pom文件配置parent为spring-boot-starter-parent 2.创建子工程&#xff0c;子工程引入一个springboot相关依赖 注意&#xff1a;子工程引入springboot相关依赖之后子工程才能被解析为springboot模块

单片机学习-蜂鸣器如何发出声音

硬件电路 软件编写 ①发出声音 #include "reg52.h" typedef unsigned int u16; // 重新定义 类型 typedef unsigned char u8; // 重新定义 类型sbit BEEP P2^5; //定义 P2第五个管教 为BEEP // 延时函数 void delay_time(u16 times) {while(times--); } vo…

qt tableview setEditTriggers解析

EditTrigger EditTrigger是QAbstractItemView Class的Public Function This property holds which actions will initiate item editing 此属性保存哪些操作将启动项编辑 This property is a selection of flags defined by EditTrigger, combined using the OR operator. T…

python编程环境使用技巧2-python环境迁移

Python环境迁移步骤 将Python环境从一个计算机迁移到另一个计算机可以按照以下步骤进行&#xff1a; 1-备份环境&#xff1a; 在源计算机上&#xff0c;使用pip工具备份当前Python环境的包列表到一个文本文件。在命令行终端中执行以下命令&#xff1a; pip freeze > requi…

layUI 中 穿梭框无法获取值的细节问题

初始化的时候一定要指定id&#xff0c;不然就会出现无法调用 获得右侧数据和实例重载的方法

C++ Primer 第2章 变量和基本类型

C Primer 第2章 变量和基本类型 2.1 基本内置类型2.1.1 算术类型一、带符号类型和无符号类型练习 2.1.2 类型转换一、含有无符号类型的表达式 2.1.3 字面值常量一、整型和浮点型字面值二、字符和字符串字面值三、转义序列四、指定字面值的类型五、布尔字面值和指针字面值 2.2 变…

网络字节序——TCP接口及其实现简单TCP服务器

网络字节序——TCP接口及其实现简单TCP服务器 文章目录 网络字节序——TCP接口及其实现简单TCP服务器简单TCP服务器的实现1. 单进程版&#xff1a;客户端串行版2. 多进程版&#xff1a;客户端并行版netstat查看网络信息3.多线程版&#xff1a;并行执行log.hpp 守护进程fg、bg s…

Vue的使用

Vue的使用 Vue到底是啥&#xff1f;Vue中包含了两部分虚拟DOM 模块化编程虚拟DOM&#xff0c;在我们重用模板的时候&#xff0c;在Vue中存在虚拟DOM 虚拟DOM是为了更好的去重用我们的DOM (增加元素的时候&#xff0c;先去虚拟DOM找是否存在&#xff0c;如果有那么不用生成&am…

android系统启动流程之init启动分析

先根据上图来描述下安卓整个系统的启动流程&#xff1a; 当上电时&#xff0c;系统先执行BootRom, 加载引导程序执行。 然后进入bootloader&#xff0c;在安卓系统中基本上这个bootloader是uboot, 通过uboot引导启动内核&#xff0c;此时运行在kernel空间&#xff0c;这时的i…

【Go 基础篇】Go语言中的自定义错误处理

错误是程序开发过程中不可避免的一部分&#xff0c;而Go语言以其简洁和高效的特性闻名。在Go中&#xff0c;自定义错误&#xff08;Custom Errors&#xff09;是一种强大的方式&#xff0c;可以为特定应用场景创建清晰的错误类型&#xff0c;以便更好地处理和调试问题。本文将详…

基于Googlenet深度学习网络的交通工具种类识别matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ....................................................................................% 获…

SpringCloud之断路器聚合监控

一、Hystrix Turbine简介 看单个的Hystrix Dashboard的数据并没有什么多大的价值&#xff0c;要想看这个系统的Hystrix Dashboard数据就需要用到Hystrix Turbine。Hystrix Turbine将每个服务Hystrix Dashboard数据进行了整合。Hystrix Turbine的使用非常简单&#xff0c;只需要…

CSS中如何改变鼠标指针样式(cursor)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ CSS中改变鼠标指针样式&#xff08;cursor&#xff09;⭐ 示例&#xff1a;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅…