题目描述(中等难度)

的升级版,之前只需要判断是否有环,现在需要把环的入口点找到,也就是直线和圆的交接点。思路的话,还是之前的两种思路。

解法一 HashMap

遍历链表,并且把遍历过的节点用 存起来,如果遍历过程中又遇到了之前的节点,说明这个节点就是我们要找到入口点。如果到达了 null 就说明没有环。

解法二 快慢指针

还是之前的思想,

但是这道题,我们需要找到入口点,而快慢指针相遇的点可能并不是入口点,而是环中的某一个点,所以需要一些数学上的推导,参考了 这里-solution-by-using-two-pointers-without-change-anything) 。

如下图,我们明确几个位置。

假设 slow 指针走过的距离为 t,那么 fast 指针走过的一定是 slow 指针的 2 倍,也就是 2t

slow 指针从 head 出发走了 x 的距离到达入口点,然后可能走了 k1 圈,然后再次回到入口点,再走了 y 的距离到达相遇点和 fast 指针相遇。

fast 指针同理,fast 指针从 head 出发走了 x 的距离到达入口点,然后可能走了 k2 圈,然后再次回到入口点,再走了 y 的距离到达相遇点和 slow 指针相遇。

2t = x + k2 * n + y

上边两个等式做一个差,可以得到

t = (k2 - k1) * n

k = k2 - k1 ,那么 t = k * n

t = k * n 代入到第一个式子 t = x + k1 * n + y 中。

移项,x = (k - k1) * n - y

取出一个 ny 结合,x = (k - k1 - 1) * n + (n - y)

左边的含义就是从 head 到达入口点。

右边的含义, n - y 就是从相遇点到入口点的距离,(k - k1 - 1) * n 就是转 (k - k1 - 1) 圈。

左边右边的含义结合起来就是,从相遇点走到入口点,然后转 (k - k1 - 1) 圈后再次回到入口点的这段时间,刚好就等于从 head 走向入口点的时间。

所以代码的话,我们只需要 meet 指针从相遇点出发的同时,让 指针也出发, head 指针和 meet 指针相遇的位置就是入口点了。

  1. public ListNode detectCycle(ListNode head) {
  2. ListNode slow = head;
  3. ListNode fast = head;
  4. ListNode meet = null;
  5. if (fast.next == null) {
  6. return null;
  7. }
  8. slow = slow.next;
  9. fast = fast.next.next;
  10. //到达相遇点
  11. if (fast == slow) {
  12. while (head != meet) {
  13. head = head.next;
  14. meet = meet.next;
  15. }
  16. return head;
  17. }
  18. }
  19. }

解法一很直接,但是多用了空间。解法二自己推导时候,只差了最后一步的变形,没有明确我们要求的变量 x。然后看了别人的题解才恍然大悟。

windliang wechat

添加好友一起进步~

如果想系统的学习数据结构和算法,强烈推荐一个我之前学过的课程,可以点击 这里 查看详情