当前位置: 首页 > news >正文

链表经典面试题【典中典】

💯💯💯链表经典面试题❗❗❗炒鸡经典,本篇带有图文解析,建议动手刷几遍。

  • 🟥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的结点排在其他结点之前,且不能改变原来的数据顺序
我们可以这样做:

  1. 将小于x的结点插入到一个链表中
  2. 将大于x的结点插入到一个链表中
  3. 最后将两个链表链接起来。

在这里插入图片描述
不过这里我们最好使用带有哨兵头的链表,这样可以减少进行对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.链表的回文结构

在这里插入图片描述
我们看到链表时有时就会想转空子,想先使用数组来存储链表的数据,然后再进行判断,我们在知道链表长度的前提下理论上是可以的,但最好不要这样做,如果不知道长度,我们还需要动态增容等,所以要抛弃这个想法。
那怎么判断回文结构呢?
回文结构也就是对称,从中间对称,左边与右边对称,如果我们把右边逆转下,那么右边就和左边一样了。所以我们就可以根据左边和右边是否一样进行判断了

  1. 首先我们需要找到中间结点
  2. 逆转中间结点以后的结点
  3. 从逆转开始的位置与开头进行比较

逆转链表,查找链表的中间结点我都已经写过了,这里直接就复制过来。
《找链表的中间结点》
《反转链表》
在这里插入图片描述

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)的呢?

好的让我来告诉你吧:
如果两个链表是相交的,那么它们的尾结点的地址一定相同,如果尾结点地址不相同那么肯定不相交。
接着让长的链表先走长度差步,然后两个链表再一起走,当两个链表结点地址比较相同时就是相同结点的位置。
所以

  1. 求出两个链表的长度,判断是否相交。
  2. 让长度长的链表先走长度差步,然后一起走
  3. 两个链表结点地址相比,第一次相同的为相交结点
    在这里插入图片描述
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;//最后如果有相同的就返回
}

相关文章:

链表经典面试题【典中典】

&#x1f4af;&#x1f4af;&#x1f4af;链表经典面试题❗❗❗炒鸡经典&#xff0c;本篇带有图文解析&#xff0c;建议动手刷几遍。&#x1f7e5;1.反转链表&#x1f7e7;2.合并两个有序链表&#x1f7e8;3.链表分割&#x1f7e9;4.链表的回文结构&#x1f7e6;5.相交链表&…...

Java泛型深入

一. 泛型的概述和优势 泛型概述 泛型&#xff1a;是JDK5中引入的特性&#xff0c;可以在编译阶段约束操作的数据类型&#xff0c;并进行检查。泛型的格式&#xff1a;<数据类型>&#xff0c;注意&#xff1a;泛型只能支持引用数据类型。集合体系的全部接口和实现类都是…...

体验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对象&#xff1a;servlet配置对象&#xff0c;主要把servlet的初始化参数封装到这个对象中。 一个网站中可能会存在多个servletConfig对象&#xff0c;一个servletConfig对象就封装了一个servlet的配置信息。 可以在web.xml中通过<init-param></init-p…...

Hadoop3.1.3单机(伪分布式配置)

参考&#xff1a;林子雨老师网站博客 Hadoop安装搭建伪分布式教程&#xff08;全面&#xff09;吐血整理 环境 Vmare12 Ubuntu16.04 创建Hadoop用户 若安装Ubuntu不是用的“hadoop”用户&#xff0c;则需要增加一个名为"hadoop"的用户 直接快捷键ctrlaltt或者点…...

HBase---浅谈HBase原理

浅谈HBase原理 文章目录浅谈HBase原理HBase定义HBase逻辑结构HBase物理存储结构TimeStampType数据模型NaneSpaceRegionRowColumnTineStampCellHBase架构MasterMaster 架构Meta 表格介绍Region ServerRegionServer 架构MemStoreWALBlockCacheZookeeperHDFSHBase写数据流程HBase读…...

学习笔记四:dockerfile

Dockerfile概述dockerfile语法详解FROMMAINTAINERRUN&#xff1a;指定在当前镜像构建过程中要运行的命令EXPOSE指令CMDENTERYPOINTCOPYADDVOLUMEWORKDIRENVUSERONBUILDLABELHEALTHCHECKARG概述 Dockerfile 是一个用来构建镜像的文本文件&#xff0c;文本内容包含了一条条构建镜…...

微服务里的小问题

1.微服务为什么设置不同的namespace 为了实现三种服务三种情况下的隔离。 2.为什么要用nginx为naocos集群结点做负载均衡&#xff1f; 2.1 正向代理 就像我们访问外网需要一个代理。 2.2 反向代理 我们不需要访问真实的ip&#xff0c;只需要访问 这个服务的代理服务器即可&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的题目&#xff0c;对其它语言做的很少。刚好在西湖论剑2022复现时&#xff0c;遇到了一道原型链污染的题目&#xff0c;借此机会开始简单学习一下 Nodejs的洞 p&#x1f402;讲解的十分清楚&#xff0c;因此下面举例子就直接用p&#x1f402;的例子进行解释了 目…...

Linux系统安装Docker

目录 Linux系统安装Docker 1、如果之前安装过旧版本的Docker&#xff0c;可以使用下面命令卸载 2、安装docker 3、启动docker 4、配置镜像加速 Linux系统安装Docker 前提&#xff1a;Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10&#xff0…...

MCP2515国产替代DP2515带有SPI 接口的独立CAN 控制器

DP2515是一款独立控制器局域网络&#xff08;Controller AreaNetwork&#xff0c; CAN&#xff09;协议控制器&#xff0c;完全支持CAN V2.0B 技术规范。该器件能发送和接收标准和扩展数据帧以及远程帧。DP2515自带的两个验收屏蔽寄存器和六个验收滤波寄存器可以过滤掉不想要的…...

【Kubernetes】第二十篇 - k8s 污点和容忍度

一&#xff0c;前言 上一篇&#xff0c;介绍了 k8s ConfigMap 管理服务环境变量&#xff1b; 本篇&#xff0c;介绍 k8s 污点和容忍度&#xff1b; 二&#xff0c;污点与容忍度介绍 通过污点和容忍度配置可以干预 Pod 部署到特定的节点&#xff1b; 比如&#xff1a; 不想让…...

60% 程序员大呼:我要远程办公!

近几年数字化的普及&#xff0c;白领们从挤地铁、打卡、开会、写日报转变成“早上9点视频会议”&#xff0c;企业的办公场所也从写字楼、会议室、工位变成了手机、电脑中的线上会议室&#xff0c;远程办公已经成为一种流行的办公形式。《财富》杂志发现&#xff0c;75%的员工表…...

jmeter+ant+jenkins接口自动化测试框架

大致思路&#xff1a;Jmeter可以做接口测试&#xff0c;也能做压力测试&#xff0c;而且是开源软件&#xff1b;Ant是基与java的构建工具&#xff0c;完成脚本执行并收集结果生成报告&#xff0c;可以跨平台&#xff0c;Jenkins是持续集成工具。将这三者结合起来可以搭建一套We…...

【protoc自定义插件】「go语言」实现rpc的服务映射成http的服务,protoc生成gin的插件,(详解实现原理及过程)

文章目录前言一、工程实践中如何更好的使用proto文件&#xff1f;二、protoc命令如何查询依赖的proto文件以及执行原理1. protoc命令如何查询依赖的proto文件2. protoc执行的插件加载原理是什么&#xff1f;3. proto文件中的package和go_package的作用三、protoc插件开发原理体…...

【C语言】3天速刷C语言(语句、函数)

语句分支语句if语句if语句语法结构语法结构&#xff1a; if(表达式)语句; if(表达式)语句1; else语句2; //多分支 if(表达式1)语句1; else if(表达式2)语句2; else语句3;表达式如果成立&#xff0c;则执行&#xff0c;不成立则弹出。switch语句语法结构&#xff1a;switch(…...

Linux系统中指针的详细分析与操作

文章目录 一、指针 二、指针的初始化 三、指针的运算 四、指针与数组 五、指针与字符串 六、函数指针 七、NULL 指针 八、对复杂指针的解释 C 语言指针真正精髓的地方在于指针可以进行加减法&#xff0c;这一点极大的提升了程序的对指针使用的灵活性&#xff0c;同时也…...

工程(十一)——NUC11+D435i+VINS-FUSION+ESDF建图(github代码)

博主的合并代码gitgithub.com:huashu996/VINS-FUSION-ESDFmap.git一、D435i深度相机配置1.1 SDKROS参考我之前的博客&#xff0c;步骤和所遇见的问题已经写的很详细了https://blog.csdn.net/HUASHUDEYANJING/article/details/129323834?spm1001.2014.3001.55011.2 相机标定参数…...

第十四届蓝桥杯三月真题刷题训练——第 4 天

目录 题目 1 &#xff1a;九数算式_dfs回溯(全排列) 题目描述 运行限制 代码&#xff1a; 题目2&#xff1a;完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 评测用例规模与约定 运行限制 代码&#xff1a; 题目 1 &am…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

goreplay

1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具&#xff0c;可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长&#xff0c;测试它所需的工作量也会呈指数级增长。GoRepl…...

Android屏幕刷新率与FPS(Frames Per Second) 120hz

Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数&#xff0c;单位是赫兹&#xff08;Hz&#xff09;。 60Hz 屏幕&#xff1a;每秒刷新 60 次&#xff0c;每次刷新间隔约 16.67ms 90Hz 屏幕&#xff1a;每秒刷新 90 次&#xff0c;…...

Qt的学习(二)

1. 创建Hello Word 两种方式&#xff0c;实现helloworld&#xff1a; 1.通过图形化的方式&#xff0c;在界面上创建出一个控件&#xff0c;显示helloworld 2.通过纯代码的方式&#xff0c;通过编写代码&#xff0c;在界面上创建控件&#xff0c; 显示hello world&#xff1b; …...