有一种直接快速的方法,在每个链表节点处挂一个标志,表示是否访问过,如果在遍历过程中找到已置标志位的节点,则该节点就是环入口点,遍历结束没找到说明不带环。
经典的做法是用快慢指针法。快指针步进2,慢指针步进1,如有环必然相遇,无环时会有指针走到NULL。
这样的复杂度为o(n)。理由是:假定环长为r,在慢指针入环时,快指针在它前面k步处,那么快指针必在第(r-k)步时与慢指针会于离环起点(r-k)步处。总共的步进数为a(环起点到链表起点的距离) + (r - k) < a + r = n。
如何找到入口点呢?让慢指针步进k步即可,但k不可知。注意到总步进次数s有:2*s = s + nr,a + (r - k) = s,即重合处慢指针再步进a步将回到环入口处,要步进a步,可让快指针回到链表头部,变慢(每次步进1),这样两个指针重合处即是环入口处。
扩展问题:两个单链表是否有交点。
方法一:将一链表首尾相接,判断另一链表是否有环,这样可以顺带求出相交处。
方法二:直接判断最后一个元素是否一样。
方法三:最直观的用经典的在线LCA算法,如果父亲不同,短链表指针不动,长链表指针后移,这样还可以找到他们的交点即他们的最近公共祖先。