单链表链表专题

1 链表的概念

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表的结构跟⽕⻋⻋厢相似,淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只 需要将⽕⻋⾥的某节⻋厢去掉/加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。

⻋厢是独⽴存在的,且每节⻋厢都有⻋⻔。想象⼀下这样的场景,假设每节⻋厢的⻋⻔都是锁上的状态,需要不同的钥匙才能解锁,每次只能携带⼀把钥匙的情况下如何从⻋头⾛到⻋尾?

最简单的做法:每节⻋厢⾥都放⼀把下⼀节⻋厢的钥匙。

在链表⾥,每节“⻋厢”是什么样的呢?

与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/节点”节点的组成主要有两个部分:当前节点要保存的数据和保存下⼀个节点的地址(指针变量)。

图中指针变量plist保存的是第⼀个节点的地址,我们称plist此时“指向”第⼀个节点,如果我们希 望plist“指向”第⼆个节点时,只需要修改plist保存的内容为0x0012FFA0。 

为什么还需要指针变量来保存下⼀个节点的位置?

链表中每个节点都是独⽴申请的(即需要插⼊数据时才去申请⼀块节点的空间),我们需要通过指针 变量来保存下⼀个节点位置才能从当前节点找到下⼀个节点。

结合前⾯学到的结构体知识,我们可以给出每个节点对应的结构体代码:

假设当前保存的节点为整型

struct SListNode
{int data; //节点数据 struct SListNode* next; //指针变量⽤保存下⼀个节点的地址 
};

当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数 据,也需要保存下⼀个节点的地址(当下⼀个节点为空时保存的地址为空)。

当我们想要从第⼀个节点⾛到最后⼀个节点时,只需要在前⼀个节点拿上下⼀个节点的地址(下⼀个 节点的钥匙)就可以了。

给定的链表结构中,如何实现节点从头到尾的打印?

 先打印第一个节点中的数据,即pcur结构体的data成员,pcur结构体的next指针指向下一个节点(pcur结构体的next指针保存着下一个节点的地址)。我们把这个地址重新赋值给pcur,此时pcur就指向了第二个节点。循环这一过程,直到pcur指向空指针时跳出循环。这样就实现了链表的遍历。

2单链表的实现

2.1开辟新节点

ListNode* NewListNote(DataType x)
{ListNode* newlistnode = (ListNode*)malloc(sizeof(ListNode));if (newlistnode == NULL){perror("malloc error");exit(1);}newlistnode->data = x;newlistnode->next = NULL;return newlistnode;
}

我们对链表进行插入操作时,需要像内存申请一个节点大小的空间,这里我们用到了malloc函数。

申请新空间并且为结构体的成员赋值。

2.2尾插

在链表的尾部插入一个新节点

void LiseTailAdd(ListNode** pphead, DataType x)
{assert(pphead);ListNode* newlistnode = NewListNote(x);if (*pphead == NULL){*pphead = newlistnode;}else{ListNode* ptail = *pphead;while ((ptail)->next != NULL){ptail = (ptail)->next;}(ptail)->next = newlistnode;}
}

如果链表中一个节点都没有,就直接插入一个新节点,新节点作为链表的“头节点”。

如果链表中原来就有节点,那我们应该先找到尾节点,然后在尾节点后边插入新节点。

这里为什么传入二级指针呢?

因为链表的头节点的地址可能会因为新节点的插入而发生改变(原链表中一个节点都没有视情况下)。想要头节点的地址发生改变,就必须传二级指针。即函数的传址调用。

2.3头插

在链表的头部插入一个新节点

void LiseHeadAdd(ListNode** pphead, DataType x)
{assert(pphead);ListNode* newlistnode = NewListNote(x);newlistnode->next = *pphead;*pphead = newlistnode;
}

建一个新节点,再让新节点的next指针指向头节点,然后让这个新节点作为链表的“头节点”。

2.4头删

删除链表的“头节点”

void LiseHeadDel(ListNode** pphead)
{assert(pphead);ListNode* tmp = (*pphead)->next;free(*pphead);*pphead = tmp;
}

直接释放头节点,让它的下一个节点作为链表的新的头节点,但是直接free头节点后,我们就找不到了它的下一个节点,所以要在释放之前用一个变量tmp把他下一个节点的地址保存下来。再让*pphead指向tmp。

2.5尾删

删除链表的尾节点

void LiseTailDel(ListNode** pphead)
{assert(pphead);ListNode* per = *pphead;ListNode* ptail = *pphead;while (ptail->next){per = ptail;ptail = ptail->next;}per->next = NULL;//free(ptail);//ptail = NULL;
}

要删除尾节点,我们应该把尾节点释放掉,并且让为节点的前一个节点的next指针指向空。所以我们要先找到尾节点和尾节点的前一个节点。找尾节点的方法和尾插找尾的方法类似。找到尾后释放即可。

2.6查找数据

ListNode* FindNote(ListNode* phead, DataType x)
{while (phead){if (phead->data == x){printf("找到了\n");return phead;}phead = phead->next;}printf("没找到");
}

遍历链表,查找某个数据是否存在,如果存在就返回对应节点的地址。不存在的话就打印没找到。

2.7在指定位置之前插入

void PopNoteFrontAdd(ListNode** pphead, ListNode* pop, DataType x)
{assert(pphead);if (*pphead == pop){LiseHeadAdd(pphead, x);}else{ListNode* per = *pphead;while (per->next != pop){per = per->next;}ListNode* newlistnode = NewListNote(x);newlistnode->next = pop;per->next = newlistnode;}
}

 在指定位置之前插入,我们需要找到这个位置之前的节点,让这个节点的next指针指向新节点,然后新节点的next指针在指向pop。

所以要遍历链表,通过头节点找到这个位置之前的节点。

2.8在指定位置之后插入

void PopNoteBehindAdd(ListNode* pop, DataType x)
{ListNode* newlistnode = NewListNote(x);newlistnode->next = pop->next;pop->next = newlistnode;
}

在指定位置之后插入,只需要让新节点的next指针指向pop的next节点,再让pop的next指针指向新节点。

注意这两条指令的顺序不能颠倒,因为如果先让pop的next指针先发生改变,就找无法找到原来的pop的next节点。

2.9删除pop之后位置节点

void DelPopBehindNode(ListNode* pop)
{assert(pop && pop->next);ListNode* tmp = pop->next;pop->next = tmp->next;free(tmp);tmp = NULL;
}

释放pop之后位置的节点,但释放之后无法找到这个位置的下一个节点,所以释放之前要用变量tmp把pop->next先存起来。

2.10删除pop位置的节点

void DelPopNode(ListNode* pop, ListNode** pphead)
{assert(pphead);if (pop == *pphead){LiseHeadDel(pphead);}else{ListNode* per = *pphead;while (per->next != pop){per = per->next;}per->next = pop->next;free(pop);pop = NULL;}
}

如果pop是头节点,直接调用头删函数。

如果pop不是头节点,那应该找到pop的前一个结点,并让其next指针指向pop的下一个节点,最后释放pop节点。

2.11链表销毁

void SListDesTroy(ListNode** pphead)
{assert(pphead && *pphead);while (*pphead != NULL){ListNode* next = (*pphead)->next;free(*pphead);*pphead = next;}
}

遍历链表,逐个节点释放。但是直接释放的话会找不到下一个节点,所以要有一个变量next将下一个节点的地址存起来。

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

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

相关文章

MySQL表结构的操作

文章目录 1. 创建表2. 查看表3. 修改表4. 删除表 1. 创建表 create table table_name (field1 datatype,field2 datatype,field3 datatype )character set 字符集 collate 校验集 engine 存储引擎;field:列名datatype:列的类型character set&#xff1a…

zookeeper分布式应用程序协调服务+消息中间件kafka分布式数据处理平台

一、zookeeper基本介绍 1.1 zookeeper的概念 Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。 是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、…

滑动窗口题解2

目录 1 找到字符串中所有字母异位词 分析: 代码展示: 代码展示: 2 串联所有单词的子串 分析: 代码展示: 3 串联所有单词的子串 分析: 代码展示: 4 水果成篮 分析: 代码展…

障碍物识别技术赋能盲人独立出行:一场静默的科技革新

作为一名资深记者,我始终关注并报道那些科技如何助力特殊群体克服生活挑战的动人故事。近期,一款叫做蝙蝠避障的应用进入了我的视线,它搭载先进障碍物识别技术以其独特的优势,悄然为视障人士的独立出行带来了显著变革。 “障碍物识…

一.shell基本知识

目录 1.1为什么学习和使用Shell编程 1.2什么是Shell 1.2.1 shell的起源 1.2.2shell的功能 1.3shell的分类 1.4作为程序设计的语言一—shell 1.5如何学好shell 1.6shell脚本的基本元素 1.7 shell脚本编写规范 1.8 shell脚本的执行方式 1.9 执行脚本的方法 1.10 shel…

ChatGML-6B大模型Windows部署(可CPU运行 保姆级教程)

🚀 写在最前:这篇文章将学习如何运行ChatGML-6B大模型。 🚀:点个关注吧😀,让我们一起探索计算机的奥秘! 一、ChatGML-6B大模型介绍 ChatGML-6B 是一个大型语言模型,被训练来预测人类语言。它是…

人工智能基础部分26-基于知识推理引擎KIE算法的原理介绍与实际应用

大家好,我是微学AI,今天给大家介绍一下人工智能基础部分26-基于知识推理引擎KIE算法的原理介绍与实际应用。知识推理引擎(Knowledge Inference Engine, KIE)是一种人工智能技术,其核心原理是基于规则和逻辑的方法来处理复杂的问题。它构建在业…

华为欧拉系统(openEuler-22.03)安装深信服EasyConnect软件(图文详解)

欧拉镜像下载安装 iso镜像官网下载地址 选择最小化安装,标准模式 换华为镜像源 更换华为镜像站,加速下载: sed -i "s#http://repo.openeuler.org#https://mirrors.huaweicloud.com/openeuler#g" /etc/yum.repos.d/openEuler.r…

MongoDB 初识

1.介绍 什么是Mong MongoDB是一种开源的文档型数据库管理系统,它使用类似于JSON的BSON格式(Binary JSON)来存储数据。与传统关系型数据库不同,MongoDB不使用表和行的结构,而是采用集合(Collection&#x…

Leetcode刷题之删除链表中等于给定值 val 的所有结点

Leetcode刷题之删除链表中等于给定值 val 的所有结点 一、题目描述二、题目解析 一、题目描述 Leetcode刷题之删除链表中等于给定值 val 的所有结点 二、题目解析 本题中我们需要将链表中等于val的值的节点删除,首先我想到的方法是通过暴力求解解决,…

递归、搜索与回溯算法:⼆叉树中的深搜

⼆叉树中的深搜 深度优先遍历(DFS,全称为 Depth First Traversal),是我们树或者图这样的数据结构中常⽤的 ⼀种遍历算法。这个算法会尽可能深的搜索树或者图的分⽀,直到⼀条路径上的所有节点都被遍历 完毕&#xff…

【Java】maven的生命周期和概念图

maven的生命周期: 在maven中存在三套"生命周期",每一套生命周期,相互独立,互不影响的,但是中同一套生命周期里,执行后面的命令会自动先执行前面的命令 CleanLifeCycle:清理的生命周期 clean defaultLifeCycle:默认的…

docker-compose yaml指定具体容器网桥ip网段subnet;docker创建即指定subnet;docker取消自启动

1、docker-compose yaml指定具体容器网桥ip网段subnet docker-compose 启动yaml有时可能的容器网段与宿主机的ip冲突导致宿主机上不了网,这时候可以更改yaml指定subnet 宿主机内网一般是192**,这时候容器可以指定172* version: 3.9 services:coredns:…

解锁生成式 AI 的力量:a16z 提供的 16 个企业指南

企业构建和采购生成式AI方面的16项改变 生成式 AI 领域趋势洞察:企业构建和采购生成式 AI 的方式正在发生重大转变,具体表现在:* 专注于可信度和安全性:75% 的企业将信任和安全性视为关键因素。* 优先考虑可扩展性和灵活性&#x…

go语言并发实战——日志收集系统(一) 项目前言

-goroutine- 简介 go并发编程的练手项目 项目背景 一般来说业务系统都有自己的日志,当系统出现问题时,我们一般需要通过日志信息来定位与解决问题,当系统机器较少时我们可以登录服务器来查看,但是当系统机器较多时,我们通过服务器来查看日志的成本就会变得很大,…

状态模式【行为模式C++】

1.概述 状态模式是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 2.结构 State(抽象状态类):定义一个接口用来封装与上下文类的一个特定状态相关的行为,可以有一个或多…

element问题总结之el-table使用fixed固定列后滚动条滑动到底部或者最右侧的时候错位问题

el-table使用fixed固定列后滚动条滑动到底部或者最右侧的时候错位 效果图前言解决方案纵向滑动滚动条滑动到底部的错位解决横向滚动条滑动到最右侧的错位解决 效果图 前言 在使用el-table固定行的时候移动滚动条会发现移动到底部或者移动到最右侧的时候会出现表头和内容错位或…

Mac电脑安装蚁剑

1: github 下载源码和加载器:https://github.com/AntSwordProjectAntSwordProject GitHubAntSwordProject has 12 repositories available. Follow their code on GitHub.https://github.com/AntSwordProject 以该图为主页面:antSword为源码…

PHPStudy(小皮)切换PHP版本PDO拓展失效的问题

因为要看一个老项目,PHP版本在8.0以上会报错,只能切换到7.2,但又遇到了PDO没开启的问题。 PHPStudy上安装的PHP7.2是需要自己配置一下的,里面php.ini文件是空的,需要将php.ini-development改成php.ini,对于…