题目
描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围:
1<=结点值<=10000
要求:空间复杂度 O(1),时间复杂度 O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
解决了链表成环检测以及找到环的入口节点的问题。
public class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null) {
return null;
}
// 使用快慢指针检测链表是否有环
ListNode fast = pHead;
ListNode slow = pHead;
boolean hasCycle = false;
do {
if (fast.next == null || fast.next.next == null) {
// 如果快指针或者快指针的下一个指针为null,说明链表无环
return null;
}
fast = fast.next.next; // 快指针每次走两步
slow = slow.next; // 慢指针每次走一步
// 如果快慢指针相遇,说明链表有环
if (fast == slow) {
hasCycle = true;
break;
}
} while (true);
// 如果链表有环,找到环的入口节点
if (hasCycle) {
fast = pHead;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
// 返回入口节点
return fast;
} else {
return null;
}
}
}
2.1
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead== null || pHead.next == null) {
return null;
}
ListNode slow = pHead;
ListNode fast = pHead;
// 判断链表中是否存在环
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
break;
}
}
// 如果链表中不存在环,返回null
if (fast == null || fast.next == null) {
return null;
}
// 重置slow指针到链表头,fast保持在相遇点
slow = pHead;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
// 返回环的入口节点
return slow;
}
}
3.1
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null) {
return null;
}
if (pHead.val > 10000) {
pHead.val = pHead.val - 10000;
return pHead;
}
pHead.val = pHead.val + 10000;
return EntryNodeOfLoop(pHead.next);
}
}
这个解决方案首先使用快慢指针的方法来检测链表中是否存在环,如果存在,则找到快慢指针的第一次相遇点。然后,将其中一个指针(这里是快指针)重置到链表头部,另一个指针(慢指针)保持在相遇点。接着,两个指针都每次移动一步,直到它们再次相遇,这个相遇点就是环的入口节点。如果链表中不存在环,函数返回null。这个算法的时间复杂度是O(n),空间复杂度是O(1),符合题目的要求。
知识点:双指针
双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个指针(特殊情况甚至可以多个),两个指针或是同方向访问两个链表、或是同方向访问一个链表(快慢指针)、或是相反方向扫描(对撞指针),从而达到我们需要的目的。
不同于传统的单指针遍历方法,双指针通过使用两个或多个指针(根据具体情况而定),以不同的方式同时访问数据结构,从而达到预期的计算目的。
在链表操作中,双指针的应用尤为广泛。
例如,当需要判断链表中是否存在环时,可以引入快慢指针的概念。
快指针每次移动两个节点,慢指针每次移动一个节点。
如果链表中存在环,这两个指针最终会在环内的某个位置相遇;如果链表中不存在环,快指针会先到达链表的末尾。
这种方法有效地利用了链表的物理结构特点,通过简单的步进差异检测出环的存在。
进一步地,若需确定环的具体入口位置,双指针技术同样适用。
一旦确认链表中存在环,可以将其中一个指针重新置于链表头部,而另一个指针继续按照原来的速度前进。
由于两者速度的差异,从头开始的慢指针与从环内某点继续前进的快指针最终会在环的入口处相遇。
这一过程不仅揭示了环的起点,也展示了双指针在解决复杂问题时的灵活性和高效性。
此外,双指针技术还可以用于其他场景,如数组中的问题解决。
通过对两个指针的合理控制和使用,可以在不增加额外空间复杂度的情况下,优化时间复杂度,提高算法的整体效率。
这种策略的核心在于充分利用问题的结构特征,通过巧妙的设计达到事半功倍的效果。