问题1:https://leetcode.com/problems/linked-list-cycle/
这是一道算法题,判断一个链表是否有环,相应输出true或者false。那怎么考虑这个问题呢?
首先想到的是遍历链表,有环与无环的区别在于,无环时遍历会最终停止,而有环则会进入死循环,重复遍历环内节点。因此可以借助于判断节点是否第二次访问来判断有无环。具体做法有以下几种:
1、哈希解法。使用一个hash表,用来保存遍历过的节点的地址,若是某个节点的地址在hash表中出现过了,则意味着有环。
2、给每个节点增加一个额外的参数,比如flag位,每次访问这个节点就把这个节点的flag位置为1,若是访问的节点中flag为1,则说明有环。但是这个方法需要修改链表。
上述方法可行,但需要O(n)的空间和O(n)时间,如果要求只用O(1)的空间和O(n)的时间,如何处理?
受启发于快慢同学绕操场跑步可以第二次相遇,也可以成为快指针和慢指针问题。建立两个指针,从header开始,向后遍历,一个步长为1,一个步长为2。
(1)如何判断有环?
如果有两个头结点指针,一个走的快,一个走的慢,那么若干步以后,如果有环,两个指针将相遇;如果无环,快指针先行到达链表尾部null。
(2)如何计算环的长度?
第一次相遇(超一圈)时开始计数,第二次相遇时停止计数,刚好遍历一圈。
(3)如何查找环的入口点?
相遇点到入口点的距离=头指针到入口点的距离+n倍环长,因此,分别从相遇点、头指针开始走,相遇的那个点就是连接点。
盗个图,解释一下:
链表总长度为n,环的长度为m,则循环n-m次时,slow指针刚好进环,假设与fast指针相距为x。经过t次循环,两指针相遇,此时 2t=t+m-x,即t=m-x。所以总的时间复杂度为o(n-m+m-x)=o(n-x)=o(n)。
判断有环和计算环长比较容易理解,下面解释下如何查找环的入口点。两指针第一次相遇时,假设slow走了s步,fast则走了2s步,则2s-s=pm;假设链表起点到环入口点的长度为a,此时,slow指针还未走完环一圈(想一想为什么),所以a+x=s
综上,可得,a+x=pm=(p-1)m+n-a,
即a=(p-1)m+m-x
所以相遇点到入口点的距离和链表起点到入口点的距离相差环长的整数倍,分别从起点和相遇点以相同速度走,将会在入口点相遇。
参考:
http://www.jianshu.com/p/0e28d31600dd
http://www.cnblogs.com/zhangbaochong/p/5151986.html
问题2:判断两个链表是否相交
可以受启发于问题1中的解法。
1、两个无环链表是否相交
有以下几种方法:
(1)哈希解法
对第一个链表的节点地址进行hash排序,建立hash表,然后针对第二个链表的每个节点的地址查询hash表,如果在hash表中出现,那么说明有共同的节点,时间复杂度为O(L1+L2),但是同时要附加O(L1)的存储空间。
(2)转换为问题1。
由于两个链表都没有环,我们可以把第二个链表接在第一个链表后面,如果得到的链表有环,则说明两个链表相交。
由于两个无环单向链表,画出来只可能是2条不相干的链表或一个”Y”字形。我们只需从第二个链表开始遍历,看是否会回到起点就可以判断出来,最后,当然可别忘了恢复原来的状态。
在这种方法下,找到环的入口点即为链表相交的第一个点。
(3)用指针p1、p2分别指向两个链表头,不断后移;最后到达各自表尾时,若p1==p2,那么两个链表必相交
复杂度为O(L1+L2),比解法3更好。
这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。
2、两个链表有环
参考:
http://blog.csdn.net/linyunzju/article/details/7753548