题目
若一个链表中包含环,如何找出的入口结点?如下图链表中,环的入口节点的节点3。
分析
- 一快(移两节点)一慢(移一节点)两指针判断链表是否存在环。
- 算出环有几个节点(上一步的两指针可知是在环中,让慢指针停止遍历,让快指针改为一节点一节点然后两指针一动一静的计算出环有多少个节点)。
- 重置两指针指向链头,一指针移动2. 步骤得出n,然后两指针一起移动。当两指针相遇,此时它们指向的环的入口结点
放码
import com.lun.util.SinglyLinkedList.ListNode;
public class FindEntryNodeOfLoop {
public ListNode find(ListNode head) {
//1.判断是否存在环
ListNode meetNode = meetNode(head);
if(meetNode == null) {
return null;
}
int nodesInLoop = 1;
ListNode node1 = meetNode;
//2.计算环内节点
while(node1.next != meetNode) {
nodesInLoop++;
node1 = node1.next;
}
//3.先移动node1, 次数为环中节点的数目
node1 = head;
for(int i = 0; i < nodesInLoop; i++)
node1 = node1.next;
ListNode node2 = head;
while(node1 != node2) {
node1 = node1.next;
node2 = node2.next;
}
return node1;
}
public ListNode meetNode(ListNode head) {
if(head == null)
return null;
ListNode slow = head.next;
if(slow == null) {//链表只有一个节点
return null;
}
ListNode fast = slow.next;
while(fast != null && slow != null) {//可能循环几次才能碰上
if(fast == slow) {
return fast;
}
slow = slow.next;
fast = fast.next;
if(fast != null) {
fast = fast.next;
}
}
return null;
}
}
测试
import static org.junit.Assert.*;
import org.junit.Test;
import com.lun.util.SinglyLinkedList;
import com.lun.util.SinglyLinkedList.ListNode;
public class FindEntryNodeOfLoopTest {
@Test
public void test() {
FindEntryNodeOfLoop fl = new FindEntryNodeOfLoop();
ListNode n1 = new ListNode(1);
ListNode n2 = new ListNode(2);
ListNode n3 = new ListNode(3);
ListNode n4 = new ListNode(4);
ListNode n5 = new ListNode(5);
ListNode n6 = new ListNode(6);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;
n6.next = n3;//n3为入口节点
assertEquals(3, fl.find(n1).val);
//没有环的链表
assertNull(fl.find(SinglyLinkedList.intArray2List(new int[] {1, 2, 3, 4, 5, 6})));
}
@Test
public void test2() {
FindEntryNodeOfLoop fl = new FindEntryNodeOfLoop();
assertNull(fl.meetNode(SinglyLinkedList.intArray2List(new int[] {1, 2, 3, 4, 5, 6})));
}
}