一、判断单链表是否有环
1、遍历链表
遍历链表,将已经访问过的结点,设置为已访问,如果访问同一结点两次,则说明有环。(遍历结点时,将结点的引用存储到hashset中)
2、链表反转
遍历链表,将访问的结点指针反向。如果存在环,反转后next指向原链表头,但链表反转会破坏链表的结构,反转需要注意缓存当前结点的下一结点。
3、快慢指针
设置快慢两个指针fast和slow,两个指针都链表头开始,fast每次移动2步,slow为1步。如果存在环,则fast先进入环,slow后进入环,最后两者在环中相遇;否则,fast先到尾部。
二、寻找环的入口
使用快慢指针fast和slow,两者相遇后,fast再次从链表头开始遍历,步长由2变为1,slow继续以步长1遍历,当两者再次相遇时,就是环的入口。
证明如下:
设在fast和slow第一次相遇时,slow走了n步,环路的入口是在x步,y为fast和slow的相遇点离环入口的距离,则:
slow走的路径: x+y=n;
fast走的路径: x+y+k*r = 2*n; r为环路的周长,k是整数;
显然,如果从x+y点开始,slow再走n步的话,还可以回到x+y这个点,因为k*r=n;
同时,fast从头开始走,步长为1,经过n步,也会达到x+y这点。
显然,在这个过程中fast和slow只有前x步骤走的路径不同。所以当fast和slow再次相遇时,必然是在链表的环路入口点上。
三、补充:链表相交
1、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。
2、如果两个链表相交,那么两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交;这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。