C语言中链表经典面试题目

简介: 环形链表环形链表 II

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

 

目录

环形链表

环形链表 II


环形链表

给你一个链表的头节点 head ,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

ECE52AE0-EE31-44A7-BFEF-B46CDA762B9C.png

输入:head = [3,2,0,-4], pos = 1

输出:true

解释:链表中有一个环,其尾部连接到第二个节点。


示例 2:

14D113E3-FC84-4469-BF87-350A88850648.png

输入:head = [1,2], pos = 0

输出:true

解释:链表中有一个环,其尾部连接到第一个节点。


示例 3:

33AEAB23-736D-49D6-8ECB-38D083899A9D.png

输入:head = [1], pos = -1

输出:false

解释:链表中没有环。


提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos-1 或者链表中的一个 有效索引

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

解题思路:

利用双指针的解法,一个慢指针slow,一个快指针fast,slow一次走一步,fast一次走两步,如果fast和slow相遇,说明链表带环,如果fast或fast->next为空,就说明链表不带环。

1. bool hasCycle(struct ListNode *head) 
2. {
3. struct ListNode* slow=head;
4. struct ListNode* fast=head;
5. while(fast&&fast->next)
6.     {
7.         slow=slow->next;
8.         fast=fast->next->next;
9. if(slow==fast)
10.         {
11. return true;
12.         }
13.     }   
14. return false; 
15. }

注意事项:

假设该链表带环

1.slow走一步,fast走两步,slow和fast一定会相遇吗?

2.slow走一步,fast走n(3,4,5……)步,slow和fast会相遇吗?

a.slow走一步,fast走两步,slow和fast一定会相遇吗?

结论:slow和fast一定会相遇

证明如下:

fast会先进入环,slow后进入环,假设slow进环时,slow与fast之间的距离是N,slow进环以后,fast开始追击slow,slow每走一步,fast走两步,它们之间的距离缩小1,追击过程中,,它们之间距离的变化:

N

N-1

N-2

...

2

1

0

当它们之间的距离为0的时候,说明slow和fast相遇了。即证明slow走一步,fast走两步,slow和fast一定会相遇。

b.slow走一步,fast走n(3,4,5……)步,slow和fast会相遇吗?

假设slow走一步,fast走三步,slow和fast一定会相遇吗?

结论:slow和fast不一定会相遇

fast会先进入环,slow后进入环,假设slow进环时,slow与fast之间的距离是N,slow进环以后,fast开始追击slow,slow每走一步,fast走三步,它们之间的距离缩小2,追击过程中,,它们之间距离的变化:

当N是偶数时

N

N-2

N-4

...

4

2

0

当它们之间的距离为0的时候,说明slow和fast相遇了

当N是奇数时

N

N-2

N-4

...

3

1

-1

当它们之间的距离为-1的时候,说明slow和fast错过了,进入下一轮追击,slow和fast的距离变成了C-1(C是环的长度),如果C-1是偶数则下一轮追上,否则永远追不上。

环形链表 II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

9DE907D5-3BC7-43AD-B156-4B482DAC797F.png

输入:head = [3,2,0,-4], pos = 1

输出:返回索引为 1 的链表节点

解释:链表中有一个环,其尾部连接到第二个节点。


示例 2:

306CDB76-BAF2-4C19-80DE-B0596E53C7DD.png

输入:head = [1,2], pos = 0

输出:返回索引为 0 的链表节点

解释:链表中有一个环,其尾部连接到第一个节点。


示例 3:

E64BD4D9-871C-4BA4-97A7-FB6F53EF176C.png

输入:head = [1], pos = -1

输出:返回 null

解释:链表中没有环。


提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

进阶:你是否可以使用 O(1) 空间解决此题?

解题思路:

方法一:

已知结论,一个指针从相遇点出发,另一个指针从链表表头开始走,它们一定会在入口相遇。

1. struct ListNode *detectCycle(struct ListNode *head) 
2. {
3. struct ListNode* fast=head;
4. struct ListNode* slow=head;
5. while(fast&&fast->next)
6.    {
7.        fast=fast->next->next;
8.        slow=slow->next;
9. if(fast==slow)//这里之前都是复用,判断链表是否带环的原码
10.        {
11. struct ListNode* meet=slow;//相遇点的指针
12. struct ListNode* cur=head;//链表表头指针
13. while(meet!=cur)
14.            {
15.                cur=cur->next;
16.                meet=meet->next;
17.            }
18. return meet;//入口点的位置
19.        }
20.    }  
21. return NULL;  
22. }

结论的验证:

前提:slow走一步,fast走两步

推论:fast走的路程是slow的两倍

声明:链表表头到环入口点的距离:L,环入口点到相遇点点距离:X,环的长度:C

E17C09A5-FA8B-4414-AB89-8265AACBD90C.jpeg

slow走的路程:L+X(因为1圈之内,fast必定能够追上slow)

fast走的路程:L+X+n*C(如果L很长,C很短,所以在slow进入环之前,fast可能走了不止一圈了,所以是n*C)

由推论可以的到:2*(L+X)=L+X+n*C   L=n*C-X

得到L=n*C-X,即证明了结论了

方法二:

在相遇的时候,将相遇的节点和下一个节点断开,如图所示:

0F74EBC3-B68A-47A9-A408-C0C3ED035856.jpeg

这样我们就可以,帮这个题看成,找两个链表相交的第一个节点。

1. struct ListNode *detectCycle(struct ListNode *head) 
2. {
3. struct ListNode* slow=head;
4. struct ListNode* fast=head;
5. while(fast&&fast->next)
6.     {
7.         slow=slow->next;
8.         fast=fast->next->next;
9. if(slow==fast)
10.         {
11. struct ListNode* meet=fast;
12. struct ListNode* meetNext=meet->next;
13.             meet->next=NULL;
14. int LenHead=0,LenMeetNext=0;
15. struct ListNode* n1=meetNext;
16. struct ListNode* n2=head;
17. while(n1)
18.             {
19.                 n1=n1->next;
20.                 LenHead++;
21.             }
22. while(n2)
23.             {
24.                 n2=n2->next;
25.                 LenMeetNext++;
26.             }
27. int gap=abs(LenHead-LenMeetNext);
28. struct ListNode* longlist=meetNext;
29. struct ListNode* shortlist=head;
30. if(LenHead<LenMeetNext)
31.             {
32.                 longlist=head;
33.                 shortlist=meetNext;
34.             }
35. while(gap--)
36.             {
37.                 longlist=longlist->next;
38.             }
39. while(longlist!=shortlist)
40.             {
41.                 longlist=longlist->next;
42.                 shortlist=shortlist->next;
43.             }
44. return longlist;
45.         }
46.     }   
47. return NULL;     
48. }

 

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸



相关文章
|
4月前
|
Web App开发 缓存 前端开发
浏览器常见面试题目及详细答案解析
本文围绕浏览器常见面试题及答案展开,深入解析浏览器组成、内核、渲染机制与缓存等核心知识点。内容涵盖浏览器的主要组成部分(如用户界面、呈现引擎、JavaScript解释器等)、主流浏览器内核及其特点、从输入URL到页面呈现的全过程,以及CSS加载对渲染的影响等。结合实际应用场景,帮助读者全面掌握浏览器工作原理,为前端开发和面试提供扎实的知识储备。
170 4
|
4月前
|
机器学习/深度学习 算法
24. 两两交换链表中的节点, 19.删除链表的倒数第N个节点 ,面试题 02.07. 链表相交
1. **两两交换链表中的节点**:通过引入虚拟头结点,使所有节点都能采用统一的交换逻辑,避免对头结点单独处理。 2. **删除链表的倒数第N个节点**:利用双指针技巧,让快慢指针保持N个节点的距离,当快指针到达末尾时,慢指针正好指向待删除节点的前一个节点。 3. **链表相交**:先计算两链表长度并调整起点,确保从相同距离末尾的位置开始遍历,从而高效找到相交节点或确定无交点。 以上方法均在时间复杂度和空间复杂度上进行了优化,适合用于理解和掌握链表的基本操作及常见算法设计思路。
|
4月前
|
缓存 NoSQL Java
Java Redis 面试题集锦 常见高频面试题目及解析
本文总结了Redis在Java中的核心面试题,包括数据类型操作、单线程高性能原理、键过期策略及分布式锁实现等关键内容。通过Jedis代码示例展示了String、List等数据类型的操作方法,讲解了惰性删除和定期删除相结合的过期策略,并提供了Spring Boot配置Redis过期时间的方案。文章还探讨了缓存穿透、雪崩等问题解决方案,以及基于Redis的分布式锁实现,帮助开发者全面掌握Redis在Java应用中的实践要点。
198 6
|
4月前
|
算法 Java 关系型数据库
校招 Java 面试基础题目解析及学习指南含新技术实操要点
本指南聚焦校招Java面试,涵盖Java 8+新特性、多线程与并发、集合与泛型改进及实操项目。内容包括Lambda表达式、Stream API、Optional类、CompletableFuture异步编程、ReentrantLock与Condition、局部变量类型推断(var)、文本块、模块化系统等。通过在线书店系统项目,实践Java核心技术,如书籍管理、用户管理和订单管理,结合Lambda、Stream、CompletableFuture等特性。附带资源链接,助你掌握最新技术,应对面试挑战。
90 2
|
4月前
|
安全 Java 编译器
Java 校招面试题目合集及答案 120 道详解
这份资料汇总了120道Java校招面试题目及其详细答案,涵盖Java基础、JVM原理、多线程、数据类型、方法重载与覆盖等多个核心知识点。通过实例代码解析,帮助求职者深入理解Java编程精髓,为校招面试做好充分准备。无论是初学者还是进阶开发者,都能从中受益,提升技术实力和面试成功率。附带的资源链接提供了更多学习材料,助力高效备考。
153 3
|
4月前
|
存储 算法 Java
校招 java 面试基础题目及解析
本文围绕Java校招面试基础题目展开,涵盖平台无关性、面向对象特性(封装、继承、多态)、数据类型、关键字(static、final)、方法相关(重载与覆盖)、流程控制语句、数组与集合、异常处理等核心知识点。通过概念阐述和代码示例,帮助求职者深入理解并掌握Java基础知识,为校招面试做好充分准备。文末还提供了专项练习建议及资源链接,助力提升实战能力。
125 0
|
10月前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
2937 6
|
11月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
291 5
|
11月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
836 4
|
11月前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
322 0