面试题23:链表中环的入口节点
题目:如果一个链表中包含环,如何找出环的入口节点?例如在下图中,环的入口节点是节点3。
解题思路:
-
第一步,如何确定链表中包含环?
定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果走的快的指针追上了走的慢的指针,那么链表中就包含环;如果走得快的指针走到了链表的末尾都没有追上第一个指针,那么链表就不包含环。
-
第二步,如何找到环的入口。先定义两个指针 p1 和 p2 。如果链表中的环有 n 个节点,则指针 p1 先在链表上向前移动 n 步,然后两个指针以相同的速度向前移动。当第二个指向环的入口节点时,第一个指针已经围绕着环走了一圈,又回到了入口节点。
-
第三步,如何得到环中节点的数目。在第一步中,我们用一快一慢两个指针,如果两个指针相遇,则表明链表中存在环。两个指针相遇的节点一定在环中。可以从这个节点触发,一边继续向前移动一边计数,当再次回到这个节点时,就可以得到环中的节点数了。
代码实现:
package Question23;
public class T01 {
public static void main(String[] args) {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node6 = new Node(6);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
node6.next = null;
System.out.println(solve(null));
}
public static Node solve(Node node) {
Node meeting = meetingNode(node);
if(meeting == null) return null;
int count = 1;
Node temp = meeting.next;
while(meeting != temp) {
count++;
temp = temp.next;
}
Node pNode1 = node;
Node pNode2 = node;
while(pNode2 != null && count > 0) {
pNode2 = pNode2.next;
count--;
}
while(pNode1 != pNode2) {
pNode1 = pNode1.next;
pNode2 = pNode2.next;
}
return pNode1;
}
public static Node meetingNode(Node node) {
if(node == null) return null;
Node pLow = node;
Node pFast = node.next;
while(pLow != null && pFast != null) {
pLow = pLow.next;
pFast = pFast.next;
if(pFast != null) pFast = pFast.next;
else return null;
//如果两个指针相遇,说明有环,并返回这个相遇的节点
if(pLow == pFast) return pLow;
}
return null;
}
}
class Node {
int id;
Node next;
public Node(int id) {
this.id = id;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
'}';
}
}