单链表环路问题
-
如何计算单链表是否存在环路
设计两个指针变量p和q,都指向链表表头,遍历该链表,且p=2p,当遍历到p=q时,说明该链表存在环路,如果p为null,则说明该链表不存在环路。
boolean isLoop(Link head){ Link p = head; Link q = head; while(p != null){ p = p.next; if(p == null) break; p = p.next; q = q.next; if(p == q){ System.out.println("there is a loop in the link"); break; } } if(p == null){ System.out.println("there is no loop in the link"); return false; } return true; }
-
如果有环,如何计算环的起始节点
结论:分别从p和q相遇的结点和头结点依次向后遍历,当遍历指针再次相遇时,相遇节点即为起始节点。
推导过程:如下图所示红色结点为相遇结点,头结点到起始节点的距离为m,起始节点到相遇结点的距离为n,环为r,链表的长度为L。
当经过m+n步之后p和q相遇,即m+n=kr=(k-1)r+r=(k-1)r+L-m,即可得:m=(k-1)r+L-m-n。从而得出头结点到环起始节点的距离等于相遇结点到起始节点的距离。
Link firstNode(Link head){ Link fast = head; Link slow = head; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; if(fast == slow) break; } if(fast == null) return null; //there is no loop in the linkList fast = head; while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; }
-
如果有环,计算环的长度
按照如上所得的起始节点,一次遍历直到再次遍历到起始节点,则可得环的长度;如只计算环的长度,则从slow和fast相遇结点开始计数,直到再次到达相遇结点,slow所走过的长度即为环的长度。
int loopLength(Link head){ Link slow = head; Link fast = head; int length = 0; boolean Flag = false; //是否相遇过一次 while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; if(Flag) length++; if(fast == slow){ if(Flag == false) Flag = true; else{ break; } } } return length; }
-
判断两个不含环的单链表是否相交,如果相交求其交点
将其中的一个链表首尾相连,然后判断另一个链表是否带环即可,如下图所示: