day23 BM6 判断链表中是否有环(Java)

149 阅读3分钟

题目来源: BM6 判断链表中是否有环

题目描述:

  • 描述: 判断给定的链表中是否有环。如果有环则返回true,否则返回false。
    数据范围: 链表长度0≤n≤10000,链表中任意节点的值满足 ∣val∣<=100000
  • 要求: 空间复杂度 O(1),时间复杂度 O(n)
  • 输入分为两部分,第一部分为链表,第二部分代表是否有环,然后将组成的head头结点传入到函数里面。-1代表无环,其它的数字代表有环,这些参数解释仅仅是为了方便读者自测调试。实际在编程时读入的是链表的头节点。
    • 例如输入{3,2,0,-4},1时,对应的链表结构如下图所示:
      • 0710DD5D9C4D4B11A8FA0C06189F9E9C.png
      • 可以看出环的入口结点为从头结点开始的第1个结点(注:头结点为第0个结点),所以输出true。
示例1:
输入:{3,2,0,-4},1
输出:true
说明:第一部分{3,2,0,-4}代表一个链表,第二部分的1表示,-4到位置1(注:头结点为位置0),
      即-4->2存在一个链接,组成传入的head为一个带环的链表,返回true  
示例2:
输入:{1},-1
输出:false
说明:第一部分{1}代表一个链表,-1代表无环,组成传入head为一个无环的单链表,返回false 

思路

  • 知识点:双指针
    • 双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个指针(特殊情况甚至可以多个),两个指针或是同方向访问两个链表、或是同方向访问一个链表(快慢指针)、或是相反方向扫描(对撞指针),从而达到我们需要的目的。
  • 思路:
    • 我们都知道链表不像二叉树,每个节点只有一个val值和一个next指针,也就是说一个节点只能有一个指针指向下一个节点,不能有两个指针,那这时我们就可以说一个性质:环形链表的环一定在末尾,末尾没有NULL了。为什么这样说呢?仔细看上图,在环2,0,-4中,没有任何一个节点可以指针指出环,它们只能在环内不断循环,因此环后面不可能还有一条尾巴。如果是普通线形链表末尾一定有NULL,那我们可以根据链表中是否有NULL判断是不是有环。
    • 但是,环形链表遍历过程中会不断循环,线形链表遍历到NULL结束了,但是环形链表何时能结束呢?我们可以用双指针技巧,同向访问的双指针,速度是快慢的,只要有环,二者就会在环内不断循环,且因为有速度差异,二者一定会相遇。
  • 具体做法:
    • step 1:设置快慢两个指针,初始都指向链表头。
    • step 2:遍历链表,快指针每次走两步,慢指针每次走一步。
    • step 3:如果快指针到了链表末尾,说明没有环,因为它每次走两步,所以要验证连续两步(fast与fast.next)是否为NULL。
    • step 4:如果链表有环,那快慢双指针会在环内循环,因为快指针每次走两步,因此快指针会在环内追到慢指针,二者相遇就代表有环。
  • 图示: C347FCC1F1A8B382DBF0D7DD7325B25B.gif

具体实现:

public class Solution {
    public boolean hasCycle(ListNode head) {
        //先判断链表为空的情况
        if(head == null) 
            return false;
        //快慢双指针
        ListNode fast = head; 
        ListNode slow = head;
        //如果没环快指针会先到链表尾
        while(fast != null && fast.next != null){ 
            //快指针移动两步
            fast = fast.next.next; 
            //慢指针移动一步
            slow = slow.next; 
            //相遇则有环
            if(fast == slow) 
                return true;
        }
        //到末尾则没有环
        return false; 
    }
}

  • 复杂度分析:
    • 时间复杂度:O(n),最坏情况下遍历链表n个节点
    • 空间复杂度:O(1),仅使用了两个指针,没有额外辅助空间
  • 疑问补充:
    • 为什么停止条件是fast和fast.next不为空,直接写一个fast.next不为空不行吗,比如fast跳到了一个NULL,它的next不也是NULL吗?
      • fast调到null后,无法再获取next了