数据结构与算法-单链表
链表
参考学习:B站-逊哥带你学编程

单链表
单链表-存储结构
typedef int ElemType;typedef struct node{ElemType data;struct node *next;
}Node;
单链表-初始化
Node *initList()
{Node *head = (Node *)malloc(sizeof(Node));head->data = 0;head->next = NULL;return head;
}
单链表-头插法
int insertHead(Node *L, ElemType e)
{Node *p = (Node *)malloc(sizeof(Node)); //分配内存p->data = e; //赋值p->next = L->next; //插入的节点指向向头节点指向的节点L->next = p; //头节点指向新节点
}
图示:

单链表-遍历
void listNode(Node *L) //传入头节点
{Node *p = L->next; //指向第一个节点 有数据的节点while(p != NULL) //若不为空{printf("%d ", p->data); //输出节点的值p = p->next; //指向下一个节点}printf("\n"); //换行
}
单链表-尾插法
Node *get_tail(Node *L)
{Node *p = L;while(p->next != NULL) //找到最后一个节点{p = p->next; //指向下一个节点}return p; //返回最后一个节点
}Node* insertTail(Node *tail, ElemType e)
{Node *p = (Node *)malloc(sizeof(Node)); //分配内存p->data = e; //赋值tail->next = p; //最后一个节点指向新节点p->next = NULL; //新节点指向空return p; //返回新节点
}
图示:

单链表-在指定位置插入数据
//插入节点
int insertNode(Node *L, int pos, ElemType e)
{Node *p = L;//用于保存要插入的位置的前一个节点int i = 0;while(i < pos-1) //找到要插入的位置{p = p->next; //指向下一个节点i++;if (p == NULL) //若为空{printf("Position is invalid\n"); //位置无效return 0;}Node *q = (Node *)malloc(sizeof(Node)); //分配内存q->data = e; //赋值q->next = p->next; //新节点指向下一个节点p->next = q; //前一个节点指向新节点return 1;
}
图示:

单链表-删除节点
找到要删除节点的前置节点p
用指针q记录要删除的节点
通过改变p的后继节点实现删除
释放删除节点的空间
图示:

int deleteNode(Node *L, int pos, ElemType *e)
{Node *p = L; //用于保存要删除的位置的前一个节点int i = 0;while(i < pos-1) //找到要删除的位置{p = p->next; //指向下一个节点i++;if (p == NULL) //若为空{printf("Position is invalid\n"); //位置无效return 0;}Node *q = p->next; //要删除的节点*e = q->data; //保存删除的节点的值p->next = q->next; //前一个节点指向下一个节点free(q); //释放内存return 1;}
}
单链表-获取链表长度
int listLength(Node *L)
{Node *p = L->next; //指向第一个节点int length = 0;while(p != NULL) //若不为空{length++; //长度加1p = p->next; //指向下一个节点}return length;
}
单链表-释放链表
指针 p指向头节点后的第一个节点
判断指针 p 是否指向空节点
如果 p不为空,用指针 q记录指针p的后继节点。
释放指针 p 指向的节点
指针 p和指针 q指向同一个节点,循环上面的操作。
图示:

int freeList(Node *L)
{Node *p = L->next; //指向第一个节点Node *q;while(p != NULL) //若不为空{q = p->next; //保存下一个节点free(p); //释放内存p = q; //指向下一个节点}L->next = NULL; //头节点指向空return 1;
}
单链表-应用
问题1:

解决方案:
使用双指针(快慢指针)

代码实现:
//链表应用快慢指针
int findNodeFS(Node *L, int k)
{Node *fast = L->next; //快指针Node *slow = L->next; //慢指针for (int i = 0; i < k; i++){fast = fast->next; //快指针先走k步}while (fast != NULL) //快指针不为空{fast = fast->next; //快指针走一步slow = slow->next; //慢指针走一步}printf("倒数第%d的节点的值:%d\n", k, slow->data); //输出慢指针的值return slow->data; //返回慢指针的值
}
问题2:

解决方案:
快慢指针。
第一步:先求出两个链表的长度m和n。
第二步:Fast指针指向较长的链表,先走m-n步或n-m步。
第三步:同步移动指针,判断它们是否指向同一个节点

代码实现:
Node* findIntersectionNode(Node *headA, Node *headB)
{if(headA == NULL || headB == NULL) //若有一个为空{return NULL; //返回空}Node *p = headA; //指向第一个链表int lenA = 0;int lenB = 0;//计算链表A的长度while(p != NULL) //若不为空{lenA++; //长度加1p = p->next; //指向下一个节点}p = headB; //指向第二个链表while (p != NULL) //若不为空{lenB++; //长度加1p = p->next; //指向下一个节点}Node *fast;Node *slow;int step;if(lenA > lenB) //若A的长度大于B的长度{fast = headA; //快指针指向Aslow = headB; //慢指针指向Bstep = lenA - lenB; //步数为A的长度减去B的长度}else{fast = headB; //快指针指向Bslow = headA; //慢指针指向Astep = lenB - lenA; //步数为B的长度减去A的长度}for (int i = 0; i < step; i++){fast = fast->next; //快指针先走step步}while (fast != NULL && slow != NULL) //若快慢指针都不为空{if(fast == slow) //若快慢指针相等{return fast; //返回快指针}fast = fast->next; //快指针走一步slow = slow->next; //慢指针走一步}
}
问题3:

解决方案:
用空间换时间。
由于节点的绝对值|data|≤n,所以可以创建一个n大小的数组,用于判断该节点的数值是否存在,用0或1表示。
如果在链表中存在±15的节点,那么在遍历链表时,第一次遇到±15时,则在数组中,将数组下标为15的值赋值为1,表示之前在链表中出现了绝对值为15的节点。之后遇到绝对值为15的节点,都通过判断数组下标为15的值为1,删除掉后续绝对值为15的链表节点。(比较绕,建议多读几遍。)

代码实现:
void removeNode(Node *L, int n)
{Node *p = L; //指向头节点int index = 0;int *q = (int *)malloc(sizeof(int)*(n+1)); //分配内存//遍历数组for(int i = 0; i < n+1; i++){*(q + i) = 0; //初始化}while(p->next != NULL){//ads获取绝对值index = abs(p->next->data); //获取绝对值if(*(q + index) == 0) //若为0{*(q + index) = 1; //赋值为1p = p->next; //指向下一个节点}else{Node *temp = p->next; //保存要删除的节点p->next = temp->next; //删除节点free(temp); //释放内存}}free(q); //释放内存}
问题4:

解决方案:
使用三个节点,如图所示,依次将second节点指向first节点,直到second节点为NULL,完成链表反转。

代码实现:
Node* reverseList(Node *head)
{Node *first = NULL; //指向空Node *second = head->next; //指向第一个节点Node *third = NULL; //指向空while(second != NULL){third = second->next; //保存下一个节点second->next = first; //第一个节点指向空first = second; //第一个节点指向第二个节点second = third; //第二个节点指向第三个节点}Node *hd = initList(); //初始化链表hd->next = first; //头节点指向第一个节点return hd; //返回头节点
}
问题5:

解决方案:
Fast指针初始指向第一个节点,Slow节点初始指向头节点,Fast每次走两个节点,Slow每次走一个节点,最后当Fast的下一个节点为NULL时,Slow的下一个节点就是中间节点,直接将Slow的下个节点指向下下个节点即可,然后记得释放掉Slow的下个节点的空间。

代码实现:
int delMiddleNode(Node *head)
{Node *fast = head->next; //快指针Node *slow = head; //慢指针while(fast != NULL && fast->next != NULL) //若快指针不为空{fast = fast->next->next; //快指针走两步slow = slow->next; //慢指针走一步}Node *q = slow->next; //保存要删除的节点slow->next = q->next; //删除节点free(q); //释放内存return 1;
}
问题6:

解决方案:
先找到中间节点。

然后将中间节点的后半段反转后,然后将两段链表互相插入。即:p1->q1->p2->q2。

代码实现:
void reOrderList(Node *head)
{Node *fast = head->next; //快指针Node *slow = head; //慢指针while(fast != NULL && fast->next != NULL) //若快指针不为空{fast = fast->next->next; //快指针走两步slow = slow->next; //慢指针走一步}//此时slow->next为中间节点//反转后半部分Node *first = NULL; //指向空Node *second = slow->next; //指向中间节点 后半段的第一个节点slow->next = NULL; //中间节点指向空Node *third = NULL; //指向空while(second != NULL) //若第二个节点不为空{third = second->next; //保存下一个节点second->next = first; //第二个节点指向空first = second; //第一个节点指向第二个节点second = third; //第二个节点指向第三个节点}Node *p1 = head->next; //指向第一个节点Node *q1 = first; //指向第一个节点Node *p2 = NULL; //指向空Node *q2 = NULL; //指向空while(p1 != NULL && q1 != NULL) //若p1和q1都不为空{p2 = p1->next; //保存上半段的下一个节点q2 = q1->next; //保存下半段的下一个节点p1->next = q1; //上半段的第一个节点指向下半段的第一个节点q1->next = p2; //下半段的第一个节点指向上半段的第二个节点p1 = p2; //指向上半段的下一个节点q1 = q2; //指向下半段的下一个节点}return 1;}
单链表-单向循环链表

例题:
判断链表是否有环。

解决方案:
快指针走两步,慢指针走一步

代码实现:
//判断链表是否有环int isCyscle(Node *head){Node *fast = head; //快指针Node *slow = head; //慢指针while (fast != NULL && fast->next != NULL) //若快指针不为空{fast = fast->next->next; //快指针走两步slow = slow->next; //慢指针走一步if(fast == slow) //若快慢指针相等{return 1; //返回1}}}
相关文章:
数据结构与算法-单链表
链表 参考学习:B站-逊哥带你学编程 单链表 单链表-存储结构 typedef int ElemType;typedef struct node{ElemType data;struct node *next; }Node;单链表-初始化 Node *initList() {Node *head (Node *)malloc(sizeof(Node));head->data 0;head->next …...
ASP.NET Core 如何使用 C# 向端点发出 POST 请求
使用 C#,将 JSON POST 到 REST API 端点;如何从 REST API 接收 JSON 数据。 本文需要 ASP .NET Core,并兼容 .NET Core 3.1、.NET 6和.NET 8。 要从端点获取数据,请参阅本文。 使用 . 将 JSON 数据发布到端点非常容易HttpClien…...
DeepSeek模型R1服务器繁忙,怎么解决?
在当今科技飞速发展的时代,人工智能领域不断涌现出令人瞩目的创新成果,其中DeepSeek模型无疑成为了众多关注焦点。它凭借着先进的技术和卓越的性能,在行业内掀起了一股热潮,吸引了无数目光。然而,如同许多前沿技术在发…...
GlusterFS 深度洞察:从架构原理到案例实践的全面解读(上)
文章目录 一.GlusterFS简介二.GlusterFS原理架构三.适用场景四.Glusterfs与其他存储产品对比五.部署GlusterFS集群六. 使用heketi将glusterfs接入k8s作为后端存储 一.GlusterFS简介 GlusterFS是一个免费的开源分布式文件系统,具有无中心节点、堆栈式设计、全局统一…...
更新无忧:用 Docker 数据卷确保 Open WebUI 数据持久化
在使用 Docker 部署 Open WebUI 时,如何在更新容器的同时确保数据不丢失,始终是工程师们关注的焦点。每次拉取新版镜像、停止并重启容器时,如果没有正确挂载数据卷,配置和数据库数据极易流失,给生产环境带来不必要的麻…...
zyNo.22
常见Web漏洞解析 命令执行漏洞 1.Bash与CMD常用命令 (1)Bash 读取文件:最常见的命令cat flag 在 Bash 中,cat 以及的tac、nl、more、head、less、tail、od、pr 均为文件读取相关命令,它们的区别如下: …...
idea如何使用AI编程提升效率-在IntelliJ IDEA 中安装 GitHub Copilot 插件的步骤-卓伊凡
idea如何使用AI编程提升效率-在IntelliJ IDEA 中安装 GitHub Copilot 插件的步骤-卓伊凡 问题 idea编译器 安装copilot AI工具 实际操作 在 IntelliJ IDEA 中安装 GitHub Copilot 插件的步骤如下: 打开 IntelliJ IDEA: 打开你的 IntelliJ IDEA 应用…...
有哪些PHP开源框架属于是“高开疯走”的?
“高开疯走”是一个网络流行语或者谐音梗。可能是指一开始起点很高(高开),然后发展迅速或者变得非常牛(疯走)。 在PHP生态中,一些框架面对市场的风起云涌,能持续保持高质量发展,切实…...
C# ASP.NET核心特性介绍
.NET学习资料 .NET学习资料 .NET学习资料 在当今的软件开发领域中,C# ASP.NET凭借其强大的功能和丰富的特性,成为构建 Web 应用程序的重要技术之一。以下将详细介绍 C# ASP.NET的核心特性。 多语言支持 ASP.NET 支持多种语言进行开发,这使…...
在 Linux 系统下,解压 `.tar.gz`
在 Linux 系统下,解压 .tar.gz 文件通常使用 tar 命令。.tar.gz 文件是一种压缩归档文件,它首先使用 tar 命令将多个文件打包为一个 .tar 文件,然后再使用 gzip 压缩生成 .tar.gz 文件。 解压 .tar.gz 文件的命令 要解压 .tar.gz 文件,可以使用以下命令: tar -xzvf fil…...
Unity 接入Tripo 文生模型,图生模型
官方网站:https://www.tripo3d.ai/app/home自行注册账号并且登陆下载Unity插件:https://cdn-web.tripo3d.ai/plugin/tripo-unity.zip申请apikey: https://platform.tripo3d.ai/api-keys使用(后续过程就按照第二步下载的插件里面的…...
WPS计算机二级•文档的文本样式与编号
听说这是目录哦 标题级别❤️新建文本样式 快速套用格式🩷设置标题样式 自定义设置多级编号🧡使用自动编号💛取消自动编号💚设置 页面边框💙添加水印🩵排版技巧怎么分栏💜添加空白下划线&#x…...
安卓使用JExcelApi读取Excel文件
要在安卓应用中使用JExcelApi读取Excel文件,你需要先确保你的项目中已经添加了JExcelApi的依赖。由于安卓项目的构建方式多样,这里以使用Gradle为例来介绍如何在安卓应用中集成和使用JExcelAPI。 ### 步骤1: 添加依赖 首先,在你的build.gra…...
外部中断实验 #STM32F407
外部中断实验 此实验将外部中断配置为按键输入,通过按键输入触发外部中断,在外部中断里面实施相应的处理,具体功能: 按下KEY0,翻转LED0状态按下KEY1,翻转LED1状态按下KEY2,同时翻转LED0和LED1…...
kafka了解-笔记
文章目录 kafka快速上手Kafka介绍Kafka快速上手理解Kafka的集群工作机制Kafka集群的消息流转模型 Kafka客户端小型流转流程客户端工作机制 kafka快速上手 Kafka介绍 MQ的作用 MQ:MessageQueue,消息队列,是一种FIFO先进先出的数据结构&#…...
渗透利器:Burp Suite 联动 XRAY 图形化工具.(主动扫描+被动扫描)
Burp Suite 联动 XRAY 图形化工具.(主动扫描被动扫描) Burp Suite 和 Xray 联合使用,能够将 Burp 的强大流量拦截与修改功能,与 Xray 的高效漏洞检测能力相结合,实现更全面、高效的网络安全测试,同时提升漏…...
解决QTimer报“Timers cannot be started from another thread“错误
今天在Qt编程时,将QTimer在子线程里执行start()函数,遇到“Timers cannot be started from another thread”问题,使用了如下AI工具,进行查询: 提示词A:“C QTimer 如何跨线程” 提示词B&#…...
vant4 van-list组件的使用
<van-listv-if"joblist && joblist.length > 0"v-model:loading"loading":finished"finished":immediate-check"false"finished-text"没有更多了"load"onLoad">// 加载 const loading ref(fals…...
C++11新特性之weak_ptr智能指针
本节介绍最后一个智能指针——weak_ptr智能指针。 1.介绍 weak_ptr智能指针也是以模板类的方式实现的。同样定义在<memory>头文件,并位于std命名空间中。在使用前需包含这两条语句。 C11虽然将weak_ptr当做智能指针,但该类型通常不单独使用&#…...
LM Studio无设置代理,更改镜像源方法(MAC)
在macbook上使用LM Studio时发现总是加载失败,App也没有设置代理的地方,搜索了挺多解决方案,貌似官网再可以封补很多解决方案已经过时,最终找到一种替换镜像源的方法共享出来。 方便大家都能使用,不介绍命令行修改方式…...
js中的== 和 ===运算符的比较和区别(面试题)
和 运算符用于比较 JavaScript 值是否相等。 自动转换数据类型,允许不同类型值的比较。 进行严格相等比较,仅在值和数据类型都相同的情况下返回 true。NaN 仅在 比较中与自身相等,而在 比较中不相等。null 和 undefined 仅在 比较中相等。…...
通过客户端Chatbox或OpenwebUI访问识别不到本地ollama中的模型等问题的解决
Chatbox和Open WebUI 等无法获取到 Ollama里的模型,主要是由以下原因导致: Ollama 服务未正确暴露给 Docker 容器或客户端模型未正确下载或名称不匹配网络配置或权限问题 排查以上问题的思路首先排查ollama服务是否启动,然后再看端口号 使…...
C# 上位机--变量
C# 上位机--变量 在 C# 上位机开发领域,变量是构建程序逻辑的基础元素之一。它就像是一个容器,用于存储各种类型的数据,从简单的数值到复杂的对象。正确理解和使用变量,对于开发出高效、稳定且易于维护的上位机程序至关重要。本文…...
【Mastering Vim 2_01】开篇词:在 AI 时代持续深耕底层技术,做长期主义的坚定捍卫者
【最新版《Mastering Vim》封面,涵盖 Vim 9.0 版特性】 文章目录 1 背景:AI 时代的底层技术觉醒2 Vim:一款被严重低估的文本编辑神器3 聊聊 IT 人士的职业病4 进阶之道:构建完整的知识体系5 从 AI 时代的深耕与精进再谈长期主义 1…...
【JVM详解二】常量池
一、常量池概述 JVM的常量池主要有以下几种: class文件常量池运行时常量池字符串常量池基本类型包装类常量池 它们相互之间关系大致如下图所示: 每个 class 的字节码文件中都有一个常量池,里面是编译后即知的该 class 会用到的字面量与符号引…...
Leetcode - 149双周赛
目录 一、3438. 找到字符串中合法的相邻数字二、3439. 重新安排会议得到最多空余时间 I三、3440. 重新安排会议得到最多空余时间 II四、3441. 变成好标题的最少代价 一、3438. 找到字符串中合法的相邻数字 题目链接 本题有两个条件: 相邻数字互不相同两个数字的的…...
Java爬虫:打造高效的数据抓取利器——详解详情接口设计与实现
在当今数字化时代,数据如同黄金般珍贵。无论是企业进行市场调研、竞争对手分析,还是研究人员收集信息,数据的需求无处不在。而爬虫技术,作为一种高效的数据抓取手段,成为了众多开发者手中的利器。本文将深入探讨如何使…...
蓝桥杯K倍区间(前缀和与差分,取模化简)
输入 5 2 1 2 3 4 5 输出 6 思路:首先由连续子串和可以想用前缀和,由于加减法总和取模和分别取模结果不受影响,所以我们前缀和之后直接取模方便观察性质,本题前缀和:1,3,6,10&#…...
CEF132 编译指南 MacOS 篇 - depot_tools 安装与配置 (四)
1. 引言 在 CEF132(Chromium Embedded Framework)的编译过程中,depot_tools 扮演着举足轻重的角色。这套由 Chromium 项目精心打造的脚本和工具集,专门用于获取、管理和更新 Chromium 及其相关项目(包括 CEFÿ…...
Ubuntu 20.04 上安装 qBittorrent
qBittorrent 通过终端安装 系统更新系统升级在 Ubuntu 20.04 上添加 Qbittorent PPA系统更新Qbittorent 安装 Qbittorent 是一个开源且可免费使用的点对点比特流客户端。它体积小,不加载内存盘。众所周知,此应用程序可以在许多操作系统(例如…...
