本文转载自微信公众号“bigsai”,作者bigsai。转载本文请联系bigsai公众号。链表是否有环这个问题看似简单,但是在处理的时候需要注意的地方很多。这道题是一道高频度很高的笔试面试题,记忆不牢很容易忘记。纽扣141和荔扣142,小伙伴在某手采访中认识的。判断一个链表是否有环问题描述:给定一个链表,判断链表中是否有环。如果链表中有一个结点可以通过不断跟踪next指针再次到达,那么链表中就存在环路。如果链表中存在循环,则返回true。否则,返回false。你能用O(1)(即常量)内存解决这个问题吗?分析:对于这个问题,如果没有内存空间限制,首先想到的是用hash的方法,用hash存储节点,然后发送Next枚举链表节点:如果发现是在hash中,则表示有环,返回true。如果枚举到最后结束,说明没有环,但这不满足O(1)空间复杂度的要求。我们应该如何处理呢?如果链表的尾部有环,如果后面再枚举一个节点,就会处于闭环中连续循环枚举,那么如何高效判断有环并能快速终止呢?有环路,其实这条路要经过第二、三次才可以说是有环路。一个指针并没有占用太多空间在存储状态下,无法有效判断是否有环(链表可能很长,也可能已经在循环中),我们可以使用fast和slow指针(双指针)。它的核心思想是使用两个指针:快指针(fast)和慢指针(slow)。两者同时从链表头部开始遍历链表,只是两者的速度不同。如果有环,它们最终会在循环链表中相遇。我们实现的时候,快指针(fast)可以一次走两步,慢指针(slow)可以一次走一步。如果有环,则快指针先入环,慢指针后入环。在慢指针到达终点之前,快指针会追上慢指针。如果快慢指针相遇,就说明有环。如果快指针先为null,则表示没有环。具体实现代码为:/***Definitionforsingly-linkedlist.*classListNode{*intval;*ListNodeext;*ListNode(intx){*val=x;*next=null;*}*}*/publicclassSolution{publicbooleanhasCycle(ListNodehead){ListNodefast=head;ListNodeslow=fast;while(fast!=null&&fast.next!=null){slow=slow.next;fast=fast.next.next;if(fast==slow)returntrue;}returnfalse;}}改进:找到环的入口给定一个链表,返回链表的第一个开始进入环的节点。如果列表是非循环的,则返回null。为了表示给定列表中的循环,我们使用整数pos来表示列表尾部连接的位置(从0开始索引)。如果pos为-1,则列表中没有循环。请注意,pos仅用于标识环,不会作为参数传递给函数。说明:不允许修改给定的链表。你能用O(1)的空间来解决这个问题吗?这道题比上一道难一点,因为如果链表成环,就需要找入口。分析:如果不考虑内存占用,我肯定会先考虑hash,保存节点然后如果第二次出现就说明有循环,直接返回。实现代码也很简单,走投无路的时候可以用这个方法:/***Definitionforsingly-linkedlist.*classListNode{*intval;*ListNodenext;*ListNode(intx){*val=x;*next=null;*}*}*/publicclassSolution{publicListNodedetectCycle(ListNodehead){intpos=-1;Map
