《数据结构》十道链表经典面试题多种方法深度解析

article/2025/10/16 3:58:03

目录

⛰️一、题目解析

🗻1.1删除链表中等于给定值 val 的所有节点(力扣)

🗻1.2反转一个单链表。(力扣)

🗻1.3给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个 中间结点。(力扣)

🗻1.4输出链表中倒数第k个结点。

🗻1.5将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

🗻1.6编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。

🗻1.7 链表的回文结构

🗻1.8输入两个链表,找出它们的第一个公共结点。

🗻1.9给定一个链表,判断链表中是否有环。

🗻1.10给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL


⛰️一、题目解析

🏠1.删除链表中等于给定值 val 的所有节点。
🏠2.反转一个单链表。
🏠3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个 中间结点。
🏠4.输出链表中倒数第k个结点。
🏠5.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
🏠6.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。
🏠7.链表的回文结构。
🏠8.输入两个链表,找出它们的第一个公共结点。
🏠9.给定一个链表,判断链表中是否有环。
🏠10.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL

🗻1.1删除链表中等于给定值 val 的所有节点(力扣)

 ⚓思路一:剔除某些值,反之,保留某些值。

将不等于val的节点全都尾插至新的链表中,然后返回新的头节点。

细节:

新的链表尾指针需指向空

否则遇到1->2->3->6->5->8->6   val=6。这种情况会有问题。

因为tail的最后的指向是8,而8指向6,6指向空。

最后的错误输出是1->2->3->5->8->6。

解决:在cur退出循环的时候,将tail->next=NULL。但要保证前题tail不为空。

代码:

typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{struct ListNode* newhead=NULL;struct ListNode* newtail=NULL;struct ListNode* cur=head;while(cur){if(cur->val!=val){//将节点尾插至新链表if(newhead==NULL){newhead=newtail=cur;}else{newtail->next=cur;newtail=cur;}}cur=cur->next;if(cur==NULL){if(newtail)newtail->next=NULL;}}return newhead;
}
⚓思路二:在原链表中操作,找到等于val值的节点,在删除它,这种删除就是把它前一个指针的指向后一个节点的地址。
图解:

 细节:

当要删除的节点为头节点时,prev==NULL。

struct ListNode* temp=cur;

cur=cur->next;

prev->next=cur;(对空指针解引用,发生错误)。

free(temp);

解决:

分情况讨论,当要删除的节点是头节点时。执行以下操作。

 cur=cur->next;

 free(newhead);

newhead=cur;

代码: 

typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{if(head==NULL){return head;}struct ListNode* prev=NULL;struct ListNode* newhead=head;struct ListNode* cur=head;while(cur){if(cur->val==val){if(cur==newhead){cur=cur->next;free(newhead);newhead=cur;}else{struct ListNode* temp=cur;cur=cur->next;prev->next=cur;free(temp);}}else{prev=cur;cur=cur->next;}}return newhead;
}

🗻1.2反转一个单链表。(力扣)

 ⚓思路一:

三指针法:前指针 中指针 后指针

翻转部分:中指针指向前指针

迭代部分:前指针=中指针;中指针=后指针;后指针=后指针->next。

图解:

细节:

最后循环的结束条件是n2==NULL。当n2是最后一个节点的地址时,n3是为空的。迭代部分将n3赋值给n2,此时再进行n3=n3->next显然是不合适的。

解决:

        if(n3)n3=n3->next;

代码:

struct ListNode* reverseList(struct ListNode* head)
{if(head==NULL){return head;}struct ListNode* n1=NULL;struct ListNode* n2=head;struct ListNode* n3=head->next;while(n2){//翻转部分n2->next=n1;//迭代部分n1=n2;n2=n3;if(n3)n3=n3->next;}return n1;
}

 

⚓思路二:

头插法:将原链表的节点头插至新链表,新链表初始为空。

图解:

代码:

struct ListNode* reverseList(struct ListNode* head)
{struct ListNode* cur = head;struct ListNode* newhead = NULL;while(cur){struct ListNode* next = cur->next;cur->next = newhead;newhead = cur;cur = next; }return newhead;
}

🗻1.3给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个 中间结点。(力扣)

⚓思路一:

快慢指针:一个快指针(fast),一个慢指针(slow)。初始状态它们都指向空,快指针一次走两步,慢指针一次走一步。当节点数为偶数时,快指针走到NULL时,slow就是中间节点,当节点数为奇数时,快指针走到最后一个节点时,slow就是中间节点。

图解:

 

代码:

struct ListNode* middleNode(struct ListNode* head)
{if(head==NULL){return head;}struct ListNode* fast=head;struct ListNode* slow=head;while(fast && fast->next){slow=slow->next;fast=fast->next->next;}return slow;
}

 ⚓思路二:

求总链表长度,取一个cur指针,它初始指向head,走总链表长度/2的步数即到达中间节点。

🗻1.4输出链表中倒数第k个结点。

 ⚓思路一:

快慢指针:一个快指针(fast),一个慢指针(slow),起初它们都指向head。先让fast走k步,此时它们之间的距离就是k,在同时让快指针走一步,慢指针走一步,当快指针走到NULL时,slow即为倒数第k个节点。

细节:

当输入的k大于节点个数时,会出现fast越界的问题。

解决:

在fast移动之前判断fast是否为空,如果为空则返回NULL,不为空就向后移动。

代码:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{if(pListHead==NULL){return NULL;}struct ListNode* prev=pListHead;struct ListNode* cur=pListHead;while(k--){if(cur)cur=cur->next;else{return NULL;}}while(cur){cur=cur->next;prev=prev->next;}return prev;
}

🗻1.5将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

 ⚓思路一:

归并思想:取一个初始为空的新链表,比较指向list1和指向list2的节点值,小的先放入新链表,放完之后,小的节点指针向后移动。当有一个指针为空时,就将另一个链表直接连接至新链表。

 代码:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{if(list1==NULL){return list2;}if(list2==NULL){return list1;}struct ListNode* cur1=list1;struct ListNode* cur2=list2;struct ListNode* newhead=(struct ListNode*)malloc(sizeof(struct ListNode));struct ListNode* tail=newhead;while(cur1 && cur2){if(cur1->val>cur2->val){tail->next=cur2;tail=cur2;cur2=cur2->next;}else{tail->next=cur1;tail=cur1;cur1=cur1->next;}}if(cur1){tail->next=cur1;}else{tail->next=cur2;}return newhead->next;
}

🗻1.6编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。

⚓思路一:

取新链表less和新链表greater,它们初始都为空。然后用指针cur遍历一遍原链表,小于x的节点放入less链表中,大于x的放入greater链表中,最后再将less和greater连接起来,返回less的头指针。

细节:

小于x的节点放less链表中,不要写成小于等于x的节点放入less链表中。

当less为空时,返回greater的头指针。

当less不为空时,less最后的节点要指向greater的头节点。并且将greater最后节点的指针指向空。因为greater最后的指向可能指向less的某个节点,连接less和greater的时候就会形成环。

图解:

 

代码:

class Partition {
public:ListNode* partition(ListNode* pHead, int x) {ListNode* less=NULL;ListNode* lesstail=NULL;ListNode* greater=NULL;ListNode* greatertail=NULL;ListNode* cur=pHead;while(cur){if(cur->val<x){if(less==NULL){less=lesstail=cur;}else{lesstail->next=cur;lesstail=cur;}}else{if(greater==NULL){greater=greatertail=cur;}else{greatertail->next=cur;greatertail=cur;}}cur=cur->next;}if(less){lesstail->next=greater;if(greatertail){greatertail->next=NULL;}return less;}else{return greater;}}
};

🗻1.7 链表的回文结构

什么是回文结构?就是正着读和反着读没区别,即对称的图形。

比如1->2->3->2->1。该结构就是回文结构。

⚓思路一:

找中间节点,然后翻转中间节点至最后节点组成的链表

图解:

 如果head1链表和head2链表一致则说明该链表是回文结构。

结束条件是:head2==NULL。

偶数个节点情况:

 与奇数个链表的情况结束条件一致。

代码:

class PalindromeList {
public:bool chkPalindrome(ListNode* A) {//找中间节点ListNode* slow=A;ListNode* fast=A;ListNode* mid=NULL;ListNode* head1=A;ListNode* head2=NULL;while(fast && fast->next){slow=slow->next;fast=fast->next->next;}mid = slow;//逆置mid-NULL各节点(核心)ListNode* n1=NULL;ListNode* n2=mid;ListNode* n3=mid->next;while(n2){n2->next=n1;n1=n2;n2=n3;if(n3)n3=n3->next;}head2=n1;//判断是否回文while(head2){if(head1->val != head2->val){return false;}head1=head1->next;head2=head2->next;}return true;}
};

 

🗻1.8输入两个链表,找出它们的第一个公共结点。

 ⚓思路一:

先判断是否存在交点,判断方法如下:

当A和B有相同的尾节点,则说明A和B有交点。

当存在交点时,让长的链表(图B)先走A和B链表长度的差距步,然后让A和B同时走,直到指针A等于指针B,此时指针A就是交点的地址。

图解:

先让headB走差距步,即走1步。

 让headA和heaB同时走,直到headA等于headB,此时headA就是交点的地址。

 代码:

 typedef  struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{ListNode* list1_tail=headA;ListNode* list2_tail=headB;ListNode* greater=headA;ListNode* less=headB;int lenA=1;int lenB=1;//判断是否有交点的核心思想:尾部节点的地址一致。while(list1_tail->next){list1_tail=list1_tail->next;lenA++;}while(list2_tail->next){list2_tail=list2_tail->next;lenB++;}if(list1_tail!=list2_tail){return NULL;}//找交点int gap=abs(lenA-lenB);if(lenA<lenB){greater=headB;less=headA;}while(gap--){greater=greater->next;}while(greater && less){if(greater == less){return less;}greater=greater->next;less=less->next;}return NULL;
}

🗻1.9给定一个链表,判断链表中是否有环。

 ⚓思路一:

快慢指针:定义两个指针,一个快指针(fast),一个慢指针(slow),初始它们都指向头节点,快指针一次走两步,慢指针一次走一步,如果有环,则快指针必定与慢指针在环的某个节点处相遇。无环,则它们绝对不会相遇。

细节:

 while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){return true;}}

能这样写吗?

不能,因为初始值slow和fast是相等的,直接就return true了。

正确写法:

 while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){return true;}}  

讨论:

如果fast一次走两步,slow一次走一步,它们一次会相遇吗?

如果fast一次走3步,slow一次走1步,它们一次会相遇吗?

如果fast一次走4步,slow一次走一步,它们一次会相遇吗?

代妈:

 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) 
{ListNode* slow=head;ListNode* fast=head;while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){return true;}}    return false;
}

 

🗻1.10给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL

 ⚓思路一:

先判断是否有环,使用快慢指针的方法。

如果有环,则记录快指针和慢指针相遇的节点地址。

 假设在值为4的位置快慢指针相遇,我们让mee指向相遇点。

此时我们做如下操作:

newhead=meet->next(让newhead指向值为5的这一节点)。

然后mee->next=NULL。(让meet指向空)。

此时图转化为:

 我们调整一下图

 我们要找的环入口就是head和newhead两链表的相交点。

与题1.8解法一致。

 代码:

 typedef struct ListNode ListNode;struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{ListNode* list1_tail=headA;ListNode* list2_tail=headB;ListNode* greater=headA;ListNode* less=headB;int lenA=1;int lenB=1;//判断是否有交点的核心思想:尾部节点的地址一致。while(list1_tail->next){list1_tail=list1_tail->next;lenA++;}while(list2_tail->next){list2_tail=list2_tail->next;lenB++;}if(list1_tail!=list2_tail){return NULL;}//找交点int gap=abs(lenA-lenB);if(lenA<lenB){greater=headB;less=headA;}while(gap--){greater=greater->next;}while(greater && less){if(greater == less){return less;}greater=greater->next;less=less->next;}return NULL;
}
struct ListNode *detectCycle(struct ListNode *head) 
{ListNode* slow=head;ListNode* fast=head;ListNode* meet=NULL;ListNode* head1=head;while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){meet=fast;//找交点,即链表head1和链表head2的公共节点。ListNode* head2=meet->next;meet->next=NULL;return getIntersectionNode(head1,head2);}}return NULL;
}

⚓思路二:

数学推理法:与思路一相同,先用快慢指针判断是否出现环,如果无环,直接返回NULL,如果有环,则记录快慢指针的相遇点。

 设从头节点到环的入口点的步数为L,环的长度为C。

假设环入口点走x步快慢指针相遇了。

可得出:

慢指针走的路程为:L+X。

快指针走的路程为:L+X+C*N(其中N代表圈数,N>=1)。

快指针路程是慢指针路程的两倍

所以:L+X+C*N=2*(L+X)。

化简得:L=C*N-X

                L=C*(N-1)+C-X。

因此我们只需要让一个指针从head走,另一个指针从meet走,当两指针相等时,它们就指向环的入口点。

代码:

 typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) 
{ListNode* slow=head;ListNode* fast=head;ListNode* cross_point=NULL;while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){cross_point=slow;while(cross_point!=head){head=head->next;cross_point=cross_point->next;}return cross_point;}}return NULL;
}

 


http://chatgpt.dhexx.cn/article/nUEIOtWT.shtml

相关文章

数据结构和算法常见面试问题总结,含答案

0. 写在前面 总导航在此 这些问题是我备考数据结构和算法的过程中&#xff0c;详细总结的常见面试问题和答案。逐个搜索并记录下来&#xff0c;花了很大的精力&#xff01;如果想要获取源文件的话&#xff0c;可以关注我的微信公众号&#xff1a;小梁说代码&#xff0c;获取嘿…

(六)数据结构面试必问

什么是链表、队列、栈&#xff1f; 链表&#xff1a; 当需要存储多个相同数据类型的时候&#xff0c;可以使用数组存储&#xff0c;数组可以通过下标直接访问&#xff0c;但数组有个缺点就是无法动态的插入或删除其中的元素&#xff08;特别是操作第一个位置上的元素&#xff…

数据结构常见面试题

链表是最基本的数据结构&#xff0c;面试官也常常用链表来考察面试者的基本能力&#xff0c;而且链表相关的操作相对而言比较简单&#xff0c;也适合考察写代码的能力。链表的操作也离不开指针&#xff0c;指针又很容易导致出错。综合多方面的原因&#xff0c;链表题目在面试中…

面试中常见的数据结构

上次在面试时被面试官问到学了哪些数据结构&#xff0c;那时简单答了栈、队列/(ㄒoㄒ)/~~其它就都想不起来了&#xff0c;今天有空整理了一下几种常见的数据结构&#xff0c;原来我们学过的数据结构有这么多~ 首先&#xff0c;先来回顾下C语言中常见的基本数据类型吧O(∩_∩)O …

数据结构算法常见面试考题

&#xff08;1&#xff09; 红黑树的了解&#xff08;平衡树&#xff0c;二叉搜索树&#xff09;&#xff0c;使用场景 把数据结构上几种树集中的讨论一下&#xff1a; 1.AVLtree 定义&#xff1a;最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为…

八大数据结构及常见面试题

几乎所有的问题都需要面试者对数据结构有深刻的理解。无论你是初入职场的新兵(刚从大学或者编程培训班毕业)&#xff0c;还是拥有几十年经验的职场老鸟。 即便是对于一些非常基础的工作来说&#xff0c;学习数据结构也是必须的。那么&#xff0c;就让我们先从一些基本概念开始入…

数据结构面试、数据结构考研复试——常见问题以及回答

说明&#xff1a;这些是自己整理回答的答案 可以借鉴 也可能存在错误 欢迎指正 文章目录 逻辑结构与物理结构的区别算法常见的数据结构链表存储结构和顺序存储结构的区别数组和链表的区别头指针和头结点的区别线性链表判断整个链表是否有环&#xff0c;如何找到这个环单链表和…

架构设计分布式数据结构与算法面试题(2020最新版)

Java面试总结&#xff08;2021优化版&#xff09;已发布在个人微信公众号【技术人成长之路】&#xff0c;优化版首先修正了读者反馈的部分答案存在的错误&#xff0c;同时根据最新面试总结&#xff0c;删除了低频问题&#xff0c;添加了一些常见面试题&#xff0c;对文章进行了…

数据结构面试题以及答案整理

参考网络整理的一些问题 一、什么是数据结构&#xff1f; 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。结构包括逻辑结构和物理结构。 数据的逻辑结构包括4种 (1)集合&#xff1a;数据元素之间除了有相同的数据类…

数据结构面试常见问题总结

数据结构面试常见问题总结 写在前面 本文记录了一些数据结构面试常见问题&#xff0c;本意用于考研复试&#xff0c;以下面试题为网上整理的问题以及自己加入的一些问题&#xff0c;答案仅供参考&#xff01; Q&#xff1a;数据结构三要素 A&#xff1a;逻辑结构、物理结构、…

mysql 驱动包 mysql-connect-java

mysql的驱动包 mysql-connect-java 内部封装了jdbc: jdbc(java database connectivity):本身是由一组接口组成 , 可以使得Java编译来访问各种数据库无需自己实现接口,这些接口的实现类由第三方数据库厂商实现 jdbc的核心 接口或类作用DriverManager类创建数据库的连接Conne…

Mysql 驱动包mysql-connector-java-8.0.25.jar下载

安装地址 https://downloads.mysql.com/archives/c-net/ 按需选择所需版本&#xff0c;点击Download即可下载&#xff1b; 网盘下载地址&#xff1a; 需要的小伙伴&#xff0c;请关注微信公众号: Transkai, 或者扫描下方公众号二维码&#xff0c;回复关键字&#xff1a;mysql驱…

下载MySQL驱动程序

下载步骤&#xff1a; 第一步&#xff1a;进入MySQL官方网站&#xff0c;并选择DOWNLOADS和Community。 第二步&#xff1a;选择MySQL Connectors 第三步&#xff1a;选择Connector/J 第四步&#xff1a;进入下面界面&#xff0c;找到下面的Generally available (GA)…

【java】Java连接mysql数据库及mysql驱动jar包下载和使用

文章目录 JDBCJDBC本质&#xff1a;JDBC作用&#xff1a;跟数据库建立连接发送 SQL 语句返回处理结果 操作流程和具体的连接步骤如下&#xff1a;操作步骤&#xff1a;需要导入驱动jar包 mysql-connector-java-8.0.22.jar注册驱动获取数据库连接对象 Connection定义sql获取执行…

Mysql-connector-java驱动包(最新版下载详细教程)

步骤如下&#xff1a; 1.进入下载官网 https://dev.mysql.com/downloads/ 2.点击Connector/J 3.选platform Independent选项 4.选zip 5.选择不登陆进行下载 6.自己选择下载到哪个文件夹即可下载成功

Java连接MySQL mysql-connector-java-bin.jar驱动包的下载与安装

eclipse在连接mysql数据库的时候要通过mysql驱动包进行连接 首先进入官网中----官网地址&#xff1a;https://dev.mysql.com/ 进入官网中选择DOWNLOADS&#xff08;下载&#xff09; 2. 选择下载中的mysql-connectors 3. 选择connector/J J指的是Java 4.接下在选择操作系统…

Java连接mysql数据库及mysql驱动jar包下载和使用(详细记录)

JDBC 基本概念&#xff1a;java 数据库连接&#xff0c;简称&#xff1a;&#xff08; java DataBase Connectivity &#xff09;&#xff0c;java语言操作数据库。 JDBC本质&#xff1a; 其实是官方&#xff08;SUN公司&#xff09;定义的一套操作所有关系型数据库的规则&…

记录下载com.mysql.jdbc.Driver驱动包过程

一、网上找了好多要么收费要么没有资源&#xff0c;所以只好去官网上找了 二、官网地址 https://dev.mysql.com/downloads/ 三、下载过程 1、点击官网进去点击downloads 2、点击MySQL Community (GPL) Downloads 进去 3、点击MySQL Community Downloads下的Connector/J 4、在这…

1.MySql驱动的jar包下载

文章目录 1.下载MySql驱动的jar包 1.下载MySql驱动的jar包 1&#xff09;官网&#xff1a;http://dev.mysql.com/downloads/connector/ 2&#xff09;点击右边的Connetor/J 3&#xff09;点击Archives 4&#xff09;Product Version为MySql驱动版本&#xff0c;可以根据需要…

如何下载mysql-java驱动jar包

1、首先打开网址https://dev.mysql.com/downloads/connector/j/ 选择Archives 2、在Product Version中选择mysql的版本 我选择的是5.1版本的&#xff0c;选择之后点击下面第二个下载按钮&#xff0c;第一个下载的是在linux中使用的 3、下载完成之后解压进入文件夹&#xff0c;…