此题有两种解法:(1.Set集合 2.快慢双指针)
题解一:(Set集合)
我们知道Set集合的结构特点是不允许存放重复的元素!那我们就可以考虑,假如链表有环,遍历链表节点的时候就肯定会重复遍历环节点。如果将重复的链表结点放进Set集合里,那Set集合就会返回false。我们就可以利用Set集合返回的false判断链表是否有环。
代码如下:

 Set<ListNode> set = new HashSet<>();
        while(head!=null && head.next!=null){
            if(!set.add(head)){//判断set是否重复加入相同的链表结点
                return true;//说明有环
            }
            head= head.next;//继续遍历链表节点
        }
        //当链表遍历完了,说明链表不存在环,不然while循环不会跳出
        return false;

题解二:(快慢双指针)空间复杂度比利用Set低
双指针相信大家都碰到过了,这里是利用快慢两个指针同时对链表进行遍历,其中最主要的思想是:(快指针是跳着一步遍历链表!慢指针一步一步遍历链表!)假如链表有环,那么快指针一定会提前到达环里,慢指针会晚一步,然后快慢指针最后会相遇。假如链表没有环,快指针肯定会提前到达链表结尾,因此结束遍历。
代码如下:

 if(head==null||head.next==null){
            return false;
        }
        //双指针,降低空间复杂度。
        ListNode slow=head,quick=head.next;
        while(slow!=quick){//只要有环,两个指针必相遇,就会跳出
            if(quick==null || quick.next==null){//快指针到达链表尾部,结束。
                return false;
            }
            slow = slow.next;
            quick = quick.next.next;
        }
        return true;