某互联网企业的笔试题:判断单链表是否有环?如果有环,如何在O(N)的时间复杂度和O(1)的空间复杂度得到这个环的入口点?
问题1:如何判断有环呢?
这个方法可能不只一种,但是在网上看到的那个类似于两个人跑步的解法最容易让人接受。两个人跑步,同时跑,但一个人在跑的快,一个人跑的慢,如果他们在一个环形操场上跑的话,肯定他们要不只一次的相遇。判断环也是这个原理,两个指针p和q,一开始p和q都指向头结点,如果p每次移动一步,q每次移动两步,那么p和q如果再一次相遇,那么有环,如果走到null,那么肯定没环。
问题2:如何得到环的入口?
这个需要一点的推导,假设在p和q第二次相遇的时候(第一次相遇在头结点)p走了s步,那么q肯定走了2s步,所以有:
2*s-s=nr. 其中,r是环的长度,n是圈数。
如果记从头结点到环入口点的距离为a,从换入口点到相遇点得距离为x,那么有
s=a+x. (q和p相遇时,p肯定没有走完一圈)
由上面两个公式又能推出:
s=a+x=nr
如果链表总长度为L的话,那么r=L-a, 有
a+x=(n-1)r+(L-a)
a=(n-1)r+(L-a-x)
其中L-a-x是p要走完一圈还要走的步数。
因此可以这样找到环的起始点:p和q相遇后,再增加一个指针k,k指向链表的头结点,然后k开始移动,一次一步,p继续移动,一次一步,那么当k走到环的入口点,p也一定到了环的入口点。因此k和p的相遇点,就是换的入口点。
扩展问题1:如何求循环链表上任一点a的最远点b.
一个圆上最远点就是同一条直径上的另一个点嘛。
方法还是指针p和q, 均从a点出发,p一次走1步,q一次走2步,当q回到a时,p指的点就是另一端的点。因为q走一圈的时候,p肯定只走了半圈。
扩展问题2:判断两个单链表是否相交,如果相交,给出相交的第一个点(两个链表都不存在环)
其实只要把第一链表首位相连,判断相交和找相交点就变成了与问题1和2相同的问题。
其实判断两个单链表是否相交有更简单的方法,只需要判断链表的最后一个点是否相同就可以。因为如果相交,就像轨道并轨一样,两个链表的最后一个节点肯定是相同的。
还可以通过两个栈来得到第一个相交的点,将Link A的节点全部压入栈SA,将Link B的节点全部压入栈SB,然后同时出栈,最后一个相同的点就是相交点。
还有另外一种判断方法,容易得到Link A的长度LA,和Link B的长度LB,那么假设LA>LB那么LA先走LA-LB步,LB在从走,两个指针肯定会相交,相交的第一个点就是要找的点。