目录

@

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

说明:不允许修改给定的链表。

进阶
你是否可以不用额外空间解决此题?

最直接的解法就是利用一个集合保存每次遍历的节点的引用。之后,从链表头开始遍历,每遍历一个节点,就判断该节点的引用是否在集合中,如果不在集合中,则将该节点的引用放入集合中;如果在集合中,则返回该节点的引用(环的入口)。当然,如果能遍历到链表尾部,此时链表无环,返回 null

  1. /**
  2. * Definition for singly-linked list.
  3. * class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) {
  7. * val = x;
  8. * next = null;
  9. * }
  10. * }
  11. */
  12. import java.util.Set;
  13. import java.util.HashSet;
  14. public class Solution {
  15. public ListNode detectCycle(ListNode head) {
  16. ListNode curr = head;
  17. Set<ListNode> nodesSeen = new HashSet<>();
  18. while (curr != null) {
  19. if (nodesSeen.contains(curr)) {
  20. return curr;
  21. }
  22. nodesSeen.add(curr);
  23. curr = curr.next;
  24. }
  25. return curr;
  26. }
  27. }
  1. # Definition for singly-linked list.
  2. # class ListNode(object):
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.next = None
  6. class Solution(object):
  7. def detectCycle(self, head):
  8. """
  9. :type head: ListNode
  10. :rtype: ListNode
  11. """
  12. curr = head
  13. nodes_seen = set()
  14. while curr:
  15. if curr in nodes_seen:
  16. return curr
  17. nodes_seen.add(curr)
  18. curr = curr.next
  19. return curr
  • 时间复杂度:\(O(n)\)
  • 空间复杂度:\(O(n)\)

LeetCode 第 141 题一样,如果不想占用额外的空间的话,可以采用双指针的方式。

假设链表的起始节点为 A,环的入口节点为 B,两个指针(快慢指针)相交节点为 C,AB 两点之间的长度为 \(x\),BC 两点之间的长度为 \(y\),CB 两点之间的长度为 \(z\)。慢指针 slow 走过的长度为 \(x+y\),快指针 fast 为了“赶上”慢指针,应该走过的长度为 \(x + y + z + y\),同时,由于快指针的速度是慢指针的两倍,因此相同时间内,快指针走过的路程应该是慢指针(走过的路程)的两倍,即

\[
x + y + z + y = 2 (x + y)
\]

化简得,
\[
x = z
\]

因此,如果此时有另外一个慢指针 slow2 从起始节点 A 出发,则两个慢指针会在节点 B (环的入口)相遇。

  1. /**
  2. * Definition for singly-linked list.
  3. * class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) {
  7. * val = x;
  8. * next = null;
  9. * }
  10. * }
  11. */
  12. public class Solution {
  13. public ListNode detectCycle(ListNode head) {
  14. ListNode slow = head, fast = head;
  15. while (fast != null && fast.next != null) {
  16. slow = slow.next;
  17. fast = fast.next.next;
  18. if (slow == fast) {
  19. ListNode slow2 = head;
  20. while (slow != slow2) {
  21. slow = slow.next;
  22. slow2 = slow2.next;
  23. }
  24. return slow;
  25. }
  26. }
  27. return null;
  28. }
  29. }
  30. // Runtime: 1 ms
  31. // Your runtime beats 100.00 % of python submissions.
  1. # Definition for singly-linked list.
  2. # class ListNode(object):
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.next = None
  6. class Solution(object):
  7. def detectCycle(self, head):
  8. """
  9. :type head: ListNode
  10. :rtype: ListNode
  11. """
  12. slow, fast = head, head
  13. while fast and fast.next:
  14. slow = slow.next
  15. fast = fast.next.next
  16. if slow == fast:
  17. slow2 = head
  18. while slow != slow2:
  19. slow = slow.next
  20. slow2 = slow2.next
  21. return slow
  22. return None
  23. # Runtime: 44 ms
  24. # Your runtime beats 99.73 % of python submissions.
  • 时间复杂度:\(O(n)\),其中 \(n\) 表示链表的长度。最坏的情况下(链表有环),需要迭代的次数为 \(x + y + z = n\) 次,因此时间复杂度为 \(O(n)\)
  • 空间复杂度:\(O(1)\),只需要存储 3 个引用

版权声明:本文为xugenpeng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xugenpeng/p/9912496.html