【双指针问题】已知链表中含有环,返回这个环的起始位置

1,854 阅读3分钟

双指针

双指针一般可以分为两类,一类是快慢指针,一类是左右指针。快慢指针通常是用来解决链表中的问题,比如典型的是判断链表中是否含有环,左右指针主要是用来解决数组/字符串的问题,比如二分搜索等 这道题主要是用快慢指针解决

快慢指针

  1. 初始化:两个指针都指向链表头节点head
  2. 前进:快指针fast在前,慢指针在后

判断链表是否有环

  1. 链表的特点就是每个节点都只知道下一个节点,所以一个指针是无法判断是否有环的,如果链表中不含环,那么这个指针最终会遇到空指针null,这样就知道链表到头了
  2. 但如果链表中有环,那就会陷入死循环,因为链表中没有null作为尾部节点。
  3. 所以判断单链表是否包含环,经典解法就是用双指针,一个跑得快,一个跑得慢,如果不含有环,那么跑得快的那个指针就会遇到null,但如果有环,那么快指针会超慢指针一圈,最终和慢指针相遇。
boolean isHasCycle(ListNode head){
    ListNode fast,slow;
    //初始化,快慢指针都指向头部
    fast=slow=head;
    //如果没有到达链表结尾
    while(fast!==null&&fast/next!==null){
        //快指针每一次走两步
        fast = fast.next.next;
        //慢指针每一次走一步
        slow = slow.next;
        //如果快指针和慢指针相遇,说明有环,返回true
    }
    //到达了链表尾null,说明链表不含有环,返回false
    return false
}

已知链表中含有环,返回这个环的起始位置

这道题其实也不难,先说方法。 方法是:当快慢指针相遇的时候,让其中任何一个指针指向链表头,然后让这两个指针以相同的速度前进,再次相遇时所在的节点位置就是环开始的位置。 在这里插入图片描述

  1. 第一次相遇的时候,假设慢指针slow走了k步,那么快指针一定走了2k步,也就是说slow多走了k步,这k步就是环的长度
  2. 假设相遇点与环的起点的距离为m(橙色曲线所示),那么环的起点与头节点head的距离为k-m。这是因为慢指针走了k步,这k步包含了头节点到环的起点(绿色直线部分),以及环起点到相遇点(橙色部分),而后者的距离为m,那么环的起点与头节点head的距离就为k-m。但因为整个环的长度为k,所以从相遇点继续前进k-m步(绿色曲线部分),也会到达环起点,正好与头节点和环起点的距离是一样的。
  3. 所以我们只需要把快慢指针的任意一个重新指向head,然后两个指针以向相同的速度前进,在走k-m步之后,两个指针必会相遇,而相遇点就是环的起点
  4. 代码为:
ListNode detectCycle(ListNode head){
       ListNode fast,slow;
       fast = slow = head;
       while(fast!=null&&fast.next!=null){
           fast = fast.next.next;
           slow= slow.next;
           if(fast==slow) break;
       }
       //先让其中一个指针指向头指针
       slow = head;
       while(slow!=fast){
           slow = slow.next;
           fast = fast.next;
       }
       //两个指针相遇的地方就是起点
       return slow;
   }