2024/6/19 今天是学校的毕业典礼,线上直播看了一会儿。起床来实验室,路上细雨就开始变大,看来我不能在细雨中呼喊了,在暴雨中淋成落汤鸡。到实验室,放下包,打开电脑,去二楼接水,看看我的栀子花。流水账到此,听着《寂寞的季节》,敲着键盘。
1、题目描述
2、逻辑分析
昨天才做数组,一下就到了链表,当年还用c++写链表,还有指针。回归主题,环形链表求起始点。我得想法就是:遍历链表,存在哈希集合里面,如果再次遇到,那就表示遇到了环的第一个节点。那么开始敲代码。
3、代码演示
public ListNode detectCycle(ListNode head) {
// 创建一个HashSet,用于存储已经遍历过的节点
// 如果某个节点在HashSet中已经存在,说明存在环
Set<ListNode> check = new HashSet<ListNode>();
// pos是一个指针,用于遍历链表
ListNode pos = head;
// 当pos不为null时,继续遍历
while(pos != null){
// 如果check集合中已经包含了pos节点,说明存在环,返回该节点
if(check.contains(pos)){
return pos;
}else{
// 如果check集合中没有pos节点,将其加入集合
check.add(pos);
}
// 移动pos到下一个节点
pos = pos.next;
}
// 没有环,返回null
return null;
}
以上是哈希表解法。时间复杂度是O(n),空间复杂度也为O(n)。
哈希表解法简单粗暴,有没有一种方法难一些,不好理解呢?那当然有啦,谈到链表怎么能忘了双指针呢。是的,另一种解法就是快慢双指针。大致思路是:
Floyd’s Cycle-Finding Algorithm(快慢指针法)
-
初始化:定义两个指针,一个快指针(
fast
)和一个慢指针(slow
),都指向链表的头节点(head
)。 -
移动指针:
在循环中,每次迭代时:
慢指针(slow
)向前移动一步(slow = slow.next
)。
快指针(fast
)向前移动两步(fast = fast.next.next
),但需要先检查fast
和fast.next
都不为null,以防止在链表末尾时抛出空指针异常。 -
检查相遇:
如果在某个时刻快指针和慢指针相遇(即它们指向同一个节点),说明链表中存在环。
如果快指针到达链表末尾(即fas
t或fast.next
为null
),说明链表中不存在环,算法结束。 -
找到环的入口:
一旦确定存在环,为了找到环的入口,我们使用另一个指针(ptr
)从头节点开始遍历链表。
同时,让慢指针(slow
)继续在环中移动。
当ptr
和slow
相遇时,它们所指向的节点即为环的入口节点。
为什么是这样呢?我解释不了,反正先记住就可以啦,下面是代码演示:
public ListNode detectCycle(ListNode head) {
// 如果头节点为空,说明链表为空,不存在环,直接返回null
if(head == null){
return null;
}
// 定义快慢指针,初始时都指向头节点
ListNode fast = head, slow = head;
// 当快指针不为null时,继续循环
while(fast != null){
// 慢指针每次移动一步
slow = slow.next;
// 检查快指针的下一个节点是否为null,如果是,说明快指针已经到达链表末尾,不存在环,返回null
if(fast.next != null){
// 快指针每次移动两步
fast = fast.next.next;
}else{
return null;
}
// 如果快慢指针相遇(即指向同一个节点),说明存在环
if(fast == slow){
// 定义一个指针ptr,从头节点开始重新遍历
ListNode ptr = head;
// 当ptr和slow不相遇时,继续遍历
// 当它们相遇时,ptr所指的节点就是环的入口节点
while(ptr != slow){
ptr = ptr.next;
slow = slow.next;
}
// 返回环的入口节点
return ptr;
}
}
// 如果快指针为null,说明链表已经被遍历完,不存在环,返回null
return null;
}
4、复杂度分析
- 时间复杂度:
O(n)
。其中 n为链表中节点的数目。在最初判断快慢指针是否相遇时,slow
指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(n)+O(n)=O(n) - 空间复杂度:
O(1)
。这里只用到了三个指针,所以是常数级。 - okok,结束啦,拜拜!