141环形链表
# 描述
难度:简单
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例 1:

输入:head = [3,2,0,-4]
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/linked-list-cycle 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# 思路
LeetCode的题目描述讲的有点绕,误导了很多人,以上题目是我提取了一些关键消息整理的,如果你还不懂什么意思,可以跟着我的思路一起来看一下。
先上代码,我们常见的链表是这样的:
ListNode listNode = new ListNode(3);
listNode.next = new ListNode(2);
listNode.next.next = new ListNode(0);
listNode.next.next.next = new ListNode(-4);

以上这种链表是没有环形的,只有从链头指向到链尾。
那么假如我们的链表写成这样:
ListNode listNode = new ListNode(3);
listNode.next = new ListNode(2);
listNode.next.next = new ListNode(0);
listNode.next.next.next = new ListNode(-4);
listNode.next.next.next.next = listNode.next; //关键是这里,让节点-4指向节点2
那么现在的链表就有环形了。

题目的意思就是这样,问你这个链表是否有环形。
思路就很简单了,只要判断链表是否有重复的链表节点就行了,实现方法大概就有两种:
Hash
其实你理解了上面就很简单了,把每个节点放进Hash,判断是否有出现过就行了,但是这种空间复杂度最差可能是O(N),因为可能是最后一个节点指向某个前面的节点,这样就要往Hash放N个节点。
双指针
链表的快慢指针。指定slow指针指向head,quick指向head.nex,然后每次遍历一下(判断条件是show!=quick 就遍历),slow向前走1步,quick向前走2步,如果存在环,quick一定会追上slow;如果没有环,那么quick必然先到达终点(也就是NULL,然后退出遍历)
**这就像龟兔赛跑一样。**你可以想象一下,乌龟走得慢,兔子走得快,假如它们的赛道是环形的,兔子必然会饶了一圈追上乌龟。
借用一个动图,红色是slow指针,每次只走1步,quick是快指针,每次走两步,如果存在环,它们必然会在某个节点相遇。

# 题解
public class 环形链表141 {
static class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public static void main(String[] args) {
ListNode listNode = new ListNode(3);
listNode.next = new ListNode(2);
listNode.next.next = new ListNode(0);
listNode.next.next.next = new ListNode(-4);
listNode.next.next.next.next = listNode.next;
System.out.println(hasCycle(listNode));
System.out.println(hasCycle2(listNode));
}
/**
* 辅助HashSet
* 时间复杂度:O(N) N是链表的长度
* 空间复杂度:O(N),主要为哈希表的开销,最坏情况下我们需要将每个节点插入到哈希表中一次。
*
* @param head
* @return
*/
static boolean hasCycle(ListNode head) {
Set<ListNode> seen = new HashSet<ListNode>();
while (head != null) {
if (!seen.add(head)) {//set如果存在,则返回false
return true;
}
head = head.next;
}
return false;
}
/**
* 双指针
* <p>
* 时间复杂度:O(N)
* 空间复杂度:O(1)
*
* @param head
* @return
*/
static boolean hasCycle2(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast ) {
if (fast == null|| fast.next == null) {
return false;
}
slow = slow.next;
//fast = fast.next 是永远追不上的
//让fast每次走两步
fast = fast.next.next;
}
return true;
}
}