链表经典面试题【典中典】
💯💯💯链表经典面试题❗❗❗炒鸡经典,本篇带有图文解析,建议动手刷几遍。
- 🟥1.反转链表
- 🟧2.合并两个有序链表
- 🟨3.链表分割
- 🟩4.链表的回文结构
- 🟦5.相交链表
🟥1.反转链表

方法一:利用双指针逆转方向
将各个结点的连接方向都倒过来,2结点指向1结点,3结点指向2结点等,我们只要将让每个结点的next指向前一个结点的地址
双指针–将指针指向前面,最后一个变成头。两个指针,一前一后
注意点:
- 单链表不能往前走
- 第一个指针首先指向NULL ,第二个指针在后面,这两个指针用来转变指向
不过这时第二个指针的后面的地址无法知道,这时需要第三个指针用来记录第二个指针后面的结点的地址。 - 当第三个指针往后走的时候可能为NULL,要注意下

代码如下:
struct ListNode* reverseList(struct ListNode* head)
{if(head==NULL)//如果链表为空则直接返回NULLreturn NULL;struct ListNode* prev = NULL;//与cur搭配,记录前面的,一开始为NULLstruct ListNode* cur = head;//一开始为第一个结点,所以将head传给cur,让cur遍历链表struct ListNode* next =cur->next;//next用来记录cur后面结点的地址while (cur)//当cur不为NULL时进行{//方向逆转cur->next = prev;//将每一个结点的next指向前一个结点的地址//迭代--让我们的条件往后执行prev = cur;//让pre跑到cur的位置上来cur = next;//让cur跑到next的位置上来if (next)//这里next可能会跑着跑着为NULL了,所以需要判断下next = next->next;//往后跑}return prev;//最后尾就是头了,将尾传回去
}
方法2:利用头插原理
取原结点头插到新链表
原本是1 2 3 4 5
我们只要将每个结点拿下来,然后头插到一个新链表上就会变成5 4 3 2 1了

代码如下:
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* cur = head;//利用cur进行遍历,不要用形参headstruct ListNode* newnode = NULL;//首先创建一个新的空链表while (cur)//当cur为空时结束{//首先要记录下来cur后面结点的地址struct ListNode* next = cur->next;//头插cur->next = newnode;//将取下来的结点头插到新链表中newnode = cur;//更新头指针cur = next;//cur要往后走}return newnode;//返回新链表的头指针
}
🟧2.合并两个有序链表

这种题目在数组中也有类似的,原理也是一样,合并两个有序数组
也就是依次比较取小的结点尾插,最后如果比较完还有结点直接尾插到没有结点的后面去。
注意点:
-
当第一次尾插到一个新链表时,我们需要的 是进行直接赋值,将第一个结点直接赋值给新链表的头指针而不能进行尾插
-
当第一次以后才可以进行真正的尾插
-
当其中有一个或两个链表为空链表的话,那么直接跳过比较环节,而进入将还有结点的直接尾插到没有结点的后面去
这时因为没有结点的链表为NULL,所以就无法访问它的next也就无法将它和另一个链表链接起来。所以这时我们需要在前面讨论一下,当其中有一个链表为NULL,则直接返回另外一个链表。
方法1:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){if (list1 == NULL)//当链表1为空则直接返回链表2return list2;if (list2 == NULL)//当链表2为空则直接返回链表1return list1;struct ListNode* cur1 = list1, * cur2 = list2;//利用cur1,cur2遍历链表struct ListNode* head = NULL, * tail = NULL;//head,用来传回,tail用来找尾while (cur1 && cur2)//当其中有一个结束就结束{if (cur1->val < cur2->val)//哪个小就尾插谁{//将小的尾插//但一开始为空第一步就是赋值if (head == NULL){head = tail = cur1;}//接下来才是真正的尾插else{tail->next = cur1;//将cur1链接在tail的后面tail = tail->next;//tail要往后找尾,这样就不需要每次从开始进行找尾了}cur1 = cur1->next;//cur往后走}else {//将小的尾插//但一开始为空第一步就是赋值if (head == NULL){head = tail = cur2;}//接下来才是真正的尾插else{tail->next = cur2;tail = tail->next;}cur2 = cur2->next;}}//将长的链表指针尾插到tail后面//不过还有一种情况就是plist为空cur1为空,则tail还是NULL,这种情况需要讨论if (cur1){tail->next = cur1;}else{tail->next = cur2;}return head;//返回head
}


还有一种方法可以避免讨论tail的next是否为空,和第一次尾插需要赋值等问题。
我们可以利用带有头哨兵卫的头结点。
这样tail永远不可能为空了,就不需要讨论那么多为空的情况了。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
struct ListNode* cur1 = list1, * cur2 = list2;struct ListNode* head =NULL,* tail = NULL;struct ListNode* guard = tail = (struct ListNode*)malloc(sizeof(struct ListNode));//动态开辟一个哨兵头结点,让tail一开始就指向头结点,这样tail就永远不会为NULL了while (cur1 && cur2)//当其中有i一个结束就结束{if (cur1->val < cur2->val){//将小的尾插//不需要进行讨论第一个头节点为NULL的情况了,直接进行尾插tail->next = cur1;tail = tail->next;cur1 = cur1->next;}else{//将小的尾插tail->next = cur2;tail = tail->next;cur2 = cur2->next;}}//将长的链表指针尾插到tail后面//如果没有哨兵头,plist为空cur1为空,则tail还是NULL,这种情况就需要讨论//但先走有了哨兵头结点,tail不可能为空,直接让tail的next与另一个链表剩余的结点链接起来if (cur1){tail->next = cur1;}else{tail->next = cur2;}head=guard->next;//将第一个结点的地址记录下来free(guard);//释放guardreturn head;
}

🟨3.链表分割

要求将所有小于x的结点排在其他结点之前,且不能改变原来的数据顺序
我们可以这样做:
- 将小于x的结点插入到一个链表中
- 将大于x的结点插入到一个链表中
- 最后将两个链表链接起来。

不过这里我们最好使用带有哨兵头的链表,这样可以减少进行对NULL的讨论不然会很麻烦
比如如果数据全是 6 6 6 6 6 而x为5
则less链表为空,那么将两个链表链接起来有会出问题,ltail都为NULL还有next吗?,所以我们使用带有哨兵卫的链表能很好的减少这种讨论。


class Partition {
public:ListNode* partition(ListNode* pHead, int x) {ListNode* Lguard,*Gguard,*ltail,*gtail;Lguard=ltail=(ListNode*)malloc(sizeof(ListNode));//less链表的哨兵头Gguard=gtail=(ListNode*)malloc(sizeof(ListNode));//greater链表的哨兵头ltail->next=NULL;//一开始要置NULLgtail->next=NULL;ListNode* cur=pHead;//利用cur进行遍历while(cur){if(cur->val<x)//如果小于x就尾插到less的链表中{ltail->next=cur;//将小的数据尾插到ltail的后面ltail=ltail->next;//找尾}else {gtail->next=cur;//将大的数据尾插到gtail后面gtail=gtail->next;}cur=cur->next;//每次cur也要往后走}ltail->next=Gguard->next;//让两个链表链接起来gtail->next=NULL;//想一想这一步是为什么呢?因为最后7的next还链接着2呀,这样就造成回环了。所以需要将gtail的next置为NULLpHead=Lguard->next;//第一个结点free(Lguard);//释放free(Gguard);//释放return pHead;//返回第一个结点地址}
};

🟩4.链表的回文结构

我们看到链表时有时就会想转空子,想先使用数组来存储链表的数据,然后再进行判断,我们在知道链表长度的前提下理论上是可以的,但最好不要这样做,如果不知道长度,我们还需要动态增容等,所以要抛弃这个想法。
那怎么判断回文结构呢?
回文结构也就是对称,从中间对称,左边与右边对称,如果我们把右边逆转下,那么右边就和左边一样了。所以我们就可以根据左边和右边是否一样进行判断了
- 首先我们需要找到中间结点
- 逆转中间结点以后的结点
- 从逆转开始的位置与开头进行比较
逆转链表,查找链表的中间结点我都已经写过了,这里直接就复制过来。
《找链表的中间结点》
《反转链表》

class PalindromeList {
public:
struct ListNode* middleNode(struct ListNode* head)//找链表中间结点
{struct ListNode *fast,*slow;fast=slow=head;while(fast!=NULL&&fast->next!=NULL){slow=slow->next;fast=fast->next->next;}return slow;}
struct ListNode* reverseList(struct ListNode* head)//反转链表
{
struct ListNode* cur = head;struct ListNode* newnode = NULL;while (cur){//首先要记录下来cur后面结点的地址struct ListNode* next = cur->next;//头插cur->next = newnode;newnode = cur;//更新头指针cur = next;//cur要往后走}return newnode;
}bool chkPalindrome(ListNode* phead){ListNode *mid= middleNode(phead);//找中间结点ListNode *rhead = reverseList(mid);//逆转中间结点以后//比较链表前面数据与逆转后的数据while(rhead)//当逆转后的结点走到NULL时结束{if(rhead->val!=phead->val)return false;//当有一个不等于就然后falsephead=phead->next;rhead=rhead->next;}return true;//走到这说明都相等}
};

🟦5.相交链表

怎么判断两个链表是否相交呢?
怎么返回这个相交结点呢?
传统思想可能会让链表A中每个结点的地址与链表B中结点的地址都比较一下,当第一次比较相同时,就是相交结点,不过这种方法的时间复杂度为O(N^2)了,不好,有没有让时间复杂度为O(N)的呢?
好的让我来告诉你吧:
如果两个链表是相交的,那么它们的尾结点的地址一定相同,如果尾结点地址不相同那么肯定不相交。
接着让长的链表先走长度差步,然后两个链表再一起走,当两个链表结点地址比较相同时就是相同结点的位置。
所以
- 求出两个链表的长度,判断是否相交。
- 让长度长的链表先走长度差步,然后一起走
- 两个链表结点地址相比,第一次相同的为相交结点

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {struct ListNode *cur1=headA,*cur2=headB;//用cur1遍历链表A,用cur2遍历链表2int lena=0;int lenb=0;while(cur1->next)//记录链表A的长度{cur1=cur1->next;++lena;}while(cur2->next)//记录链表B的长度{cur2=cur2->next;++lenb;}if(cur1!=cur2)//如果链表尾结点不相同那么肯定不相交return NULL;int gap=abs(lena-lenb);//长度差,但我们不知道哪个链表长struct ListNode *longlist=headA,*shortlist=headB;if(lenb>lena)//所以我们需要讨论下{longlist=headB,shortlist=headA;}while(gap--)//让长的链表先走长度差步{longlist=longlist->next;}while(longlist!=shortlist)//然后两个链表一起走,当没有结点地址相同时则一起走{longlist=longlist->next;shortlist=shortlist->next;}return longlist;//最后如果有相同的就返回
}
相关文章:
链表经典面试题【典中典】
💯💯💯链表经典面试题❗❗❗炒鸡经典,本篇带有图文解析,建议动手刷几遍。🟥1.反转链表🟧2.合并两个有序链表🟨3.链表分割🟩4.链表的回文结构🟦5.相交链表&…...
Java泛型深入
一. 泛型的概述和优势 泛型概述 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。泛型的格式:<数据类型>,注意:泛型只能支持引用数据类型。集合体系的全部接口和实现类都是…...
体验Linux USB 驱动
目录 一、USB OTG 二、I.MX6ULL USB 接口简介 硬件原理图 1、USB HUB 原理图 2 、USB OTG 原理图 三、使能驱动 1、打开 HID 驱动 2、 使能 USB 键盘和鼠标驱动 3 、使能 Linux 内核中的 SCSI 协议 4、使能 U 盘驱动 四、测试u盘 五、 Linux 内核自带 USB OTG USB 是…...
servlet 中的ServletConfig与servletContext
ServletConfig对象:servlet配置对象,主要把servlet的初始化参数封装到这个对象中。 一个网站中可能会存在多个servletConfig对象,一个servletConfig对象就封装了一个servlet的配置信息。 可以在web.xml中通过<init-param></init-p…...
Hadoop3.1.3单机(伪分布式配置)
参考:林子雨老师网站博客 Hadoop安装搭建伪分布式教程(全面)吐血整理 环境 Vmare12 Ubuntu16.04 创建Hadoop用户 若安装Ubuntu不是用的“hadoop”用户,则需要增加一个名为"hadoop"的用户 直接快捷键ctrlaltt或者点…...
HBase---浅谈HBase原理
浅谈HBase原理 文章目录浅谈HBase原理HBase定义HBase逻辑结构HBase物理存储结构TimeStampType数据模型NaneSpaceRegionRowColumnTineStampCellHBase架构MasterMaster 架构Meta 表格介绍Region ServerRegionServer 架构MemStoreWALBlockCacheZookeeperHDFSHBase写数据流程HBase读…...
学习笔记四:dockerfile
Dockerfile概述dockerfile语法详解FROMMAINTAINERRUN:指定在当前镜像构建过程中要运行的命令EXPOSE指令CMDENTERYPOINTCOPYADDVOLUMEWORKDIRENVUSERONBUILDLABELHEALTHCHECKARG概述 Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜…...
微服务里的小问题
1.微服务为什么设置不同的namespace 为了实现三种服务三种情况下的隔离。 2.为什么要用nginx为naocos集群结点做负载均衡? 2.1 正向代理 就像我们访问外网需要一个代理。 2.2 反向代理 我们不需要访问真实的ip,只需要访问 这个服务的代理服务器即可&a…...
数据库之基本功:Where 中常用运算符
1. 运算符及优先级 ( )优先级最高 SQL> show user; USER is "SCOTT" SQL> select ename, job, sal, comm from emp where jobSALESMAN OR jobPRESIDENT and sal> 1500;ENAME JOB SAL COMM …...
浅谈 Nodejs原型链污染
一直在做php的题目,对其它语言做的很少。刚好在西湖论剑2022复现时,遇到了一道原型链污染的题目,借此机会开始简单学习一下 Nodejs的洞 p🐂讲解的十分清楚,因此下面举例子就直接用p🐂的例子进行解释了 目…...
Linux系统安装Docker
目录 Linux系统安装Docker 1、如果之前安装过旧版本的Docker,可以使用下面命令卸载 2、安装docker 3、启动docker 4、配置镜像加速 Linux系统安装Docker 前提:Docker CE 支持 64 位版本 CentOS 7,并且要求内核版本不低于 3.10࿰…...
MCP2515国产替代DP2515带有SPI 接口的独立CAN 控制器
DP2515是一款独立控制器局域网络(Controller AreaNetwork, CAN)协议控制器,完全支持CAN V2.0B 技术规范。该器件能发送和接收标准和扩展数据帧以及远程帧。DP2515自带的两个验收屏蔽寄存器和六个验收滤波寄存器可以过滤掉不想要的…...
【Kubernetes】第二十篇 - k8s 污点和容忍度
一,前言 上一篇,介绍了 k8s ConfigMap 管理服务环境变量; 本篇,介绍 k8s 污点和容忍度; 二,污点与容忍度介绍 通过污点和容忍度配置可以干预 Pod 部署到特定的节点; 比如: 不想让…...
60% 程序员大呼:我要远程办公!
近几年数字化的普及,白领们从挤地铁、打卡、开会、写日报转变成“早上9点视频会议”,企业的办公场所也从写字楼、会议室、工位变成了手机、电脑中的线上会议室,远程办公已经成为一种流行的办公形式。《财富》杂志发现,75%的员工表…...
jmeter+ant+jenkins接口自动化测试框架
大致思路:Jmeter可以做接口测试,也能做压力测试,而且是开源软件;Ant是基与java的构建工具,完成脚本执行并收集结果生成报告,可以跨平台,Jenkins是持续集成工具。将这三者结合起来可以搭建一套We…...
【protoc自定义插件】「go语言」实现rpc的服务映射成http的服务,protoc生成gin的插件,(详解实现原理及过程)
文章目录前言一、工程实践中如何更好的使用proto文件?二、protoc命令如何查询依赖的proto文件以及执行原理1. protoc命令如何查询依赖的proto文件2. protoc执行的插件加载原理是什么?3. proto文件中的package和go_package的作用三、protoc插件开发原理体…...
【C语言】3天速刷C语言(语句、函数)
语句分支语句if语句if语句语法结构语法结构: if(表达式)语句; if(表达式)语句1; else语句2; //多分支 if(表达式1)语句1; else if(表达式2)语句2; else语句3;表达式如果成立,则执行,不成立则弹出。switch语句语法结构:switch(…...
Linux系统中指针的详细分析与操作
文章目录 一、指针 二、指针的初始化 三、指针的运算 四、指针与数组 五、指针与字符串 六、函数指针 七、NULL 指针 八、对复杂指针的解释 C 语言指针真正精髓的地方在于指针可以进行加减法,这一点极大的提升了程序的对指针使用的灵活性,同时也…...
工程(十一)——NUC11+D435i+VINS-FUSION+ESDF建图(github代码)
博主的合并代码gitgithub.com:huashu996/VINS-FUSION-ESDFmap.git一、D435i深度相机配置1.1 SDKROS参考我之前的博客,步骤和所遇见的问题已经写的很详细了https://blog.csdn.net/HUASHUDEYANJING/article/details/129323834?spm1001.2014.3001.55011.2 相机标定参数…...
第十四届蓝桥杯三月真题刷题训练——第 4 天
目录 题目 1 :九数算式_dfs回溯(全排列) 题目描述 运行限制 代码: 题目2:完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 评测用例规模与约定 运行限制 代码: 题目 1 &am…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
