关于单链表中的环,一般涉及到的问题:
1.给一个单链表,判断其中是否有环的存在;
2.如果存在环,找出环的入口点;
当fast和slow相遇时,slow还没有走完链表,假设fast已经在环内循环了n(1<= n)圈。假设slow走了s步,则fast走了2s步,又由于
fast走过的步数 = s + n*r(s + 在环上多走的n圈),则有下面的等式:
2*s = s + n * r ; (1) => s = n*r (2)
如果假设整个链表的长度是L,入口和相遇点的距离是x(如上图所示),起点到入口点的距离是a(如上图所示),则有:
a + x = s = n * r; (3) 由(2)推出
a + x = (n - 1) * r + r = (n - 1) * r + (L - a) (4) 由环的长度 = 链表总长度 - 起点到入口点的距离求出
a = (n - 1) * r + (L -a -x) (5)
从链表起点head开始到入口点的距离a,与从slow和fast的相遇点(如图)到入口点的距离相等。
3.如果存在环,求出环上节点的个数;
思路1:记录下相遇节点存入临时变量tempPtr,然后让slow(或者fast,都一样)继续向前走slow = slow -> next;一直到slow == tempPtr; 此时经过的步数就是环上节点的个数;
思路2: 从相遇点开始slow和fast继续按照原来的方式向前走slow = slow -> next; fast = fast -> next -> next;直到二者再次项目,此时经过的步数就是环上节点的个数 。
fast和slow没一次操作都会使得两者之间的距离较少1。我们可以把两者相遇的时候看做两者之间的距离正好是整个环的长度r。因此,当再次相遇的时候所经过的步数正好是环上节点的数目。
4.如果存在环,求出链表的长度;
链表长度L = 起点到入口点的距离 + 环的长度r;
5.如果存在环,求出环上距离任意一个节点最远的点(对面节点);
对于环上任意的一个点ptr0, 我们要找到它的”对面点“,可以这样思考:同样使用上面的快慢指针的方法,让slow和fast都指向ptr0,每一步都执行与上面相同的操作(slow每次跳一步,fast每次跳两步),当fast = ptr0或者fast = prt0->next的时候slow所指向的节点就是ptr0的”对面节点“。
为什么是这样呢?我们可以这样分析:
如上图,我们想像一下,把环从ptro处展开,展开后可以是无限长的(如上在6后重复前面的内容)如上图。
现在问题就简单了,由于slow移动的距离永远是fast的一般,因此当fast遍历玩整个环长度r个节点的时候slow正好遍历了r/2个节点,也就是说,此时正好指向距离ptr0最远的点。
6.(扩展)如何判断两个无环链表是否相交;
7.(扩展)如果相交,求出第一个相交的节点;
假设有连个链表listA和listB,如果两个链表都无环,并且有交点,那么我们可以让其中一个链表(不妨设是listA)的为节点连接到其头部,这样在listB中就一定会出现一个环。因此我们将问题6和7分别转化成了问题1和2.