算法思想一:双指针
解题思路:
我们使用两个指针,fast 与 slow。
1、它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。
2、当发现 slow 与 fast 相遇时,我们再额外使用一个指针 cur。起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇
图解:
代码展示:
Python版本
# -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def EntryNodeOfLoop(self, pHead): # write code here if not pHead: return None if pHead.next: slow = pHead.next fast = pHead.next.next else: return None while fast: if fast != slow: # 快指针走两步 if fast.next: fast = fast.next.next else: return None # 慢指针走一步 slow = slow.next else: # 当两指针相同时,则从头节点同时出发,当相遇即为入环口 cur = pHead while cur != slow: slow = slow.next cur = cur.next return cur return None
复杂度分析:
时间复杂度O(N):其中 N 为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度
空间复杂度O(1):额外使用的指针占用常数空间
算法思想二:哈希表
解题思路:
我们遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。借助哈希表可以很方便地实现
1、遍历链表,并将访问过的结点存储到哈希表中
2、判断结点是否在哈希表中,若存在则返回当前结点
3、遍历结束,则返回 null
代码展示:
JAVA版本
public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { ListNode pos = pHead; // 哈希表记录访问过的结点 Set<ListNode> visited = new HashSet<ListNode>(); while (pos != null) { // 判断结点是否被访问 if (visited.contains(pos)) { return pos; } else { // 结点记录添加到哈希表中 visited.add(pos); } // 遍历 pos = pos.next; } return null; }
复杂度分析:
时间复杂度O(N):其中 N 为链表中节点的数目。遍历整个链表的结点
空间复杂度O(N):其中 NN 为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。