《剑指offer—面试题23:链表中环的入口节点》
注明:仅个人学习笔记
/**
*
* 这段代码写的太复杂,在牛客网提交会不成功,但功能没问题
*
* 链表中环的入口节点
* SingleLinkedList:单链表操作
* Node:链表节点
* 这两个实现类详见《剑指offer—面试题18:删除链表的节点》 https://blog.csdn.net/u011296723/article/details/81670532
*/
public class EntryNodeOfLoop23
{
public Node EntryNodeOfLoop(Node pHead)
{
if (pHead == null || pHead.next==null)
{
return null;
}
//
// // 统计链表长度
// int length = 0;
// Node tmp = pHead;
// while (tmp != null)
// {
// length++;
// tmp = tmp.next;
// }
// //链表长度不足时,不可能存在环
// if(length <= 1)
// {
// return null;
// }
Node head = pHead;
// 第一步,确定链表中有环,并且找到了快慢节点的相遇点
Node meetNode = isLoop(head);
// 如果有环,确定环的节点数
// 让该点继续移动,到下一次又到该点时,所移动的步数即为环中节点个数
int loopNodeNum = 1;// 环中节点个数一定得从1开始
Node pmeetNode = meetNode;
if (meetNode != null)
{
while (pmeetNode != meetNode)
{
loopNodeNum++;
pmeetNode = pmeetNode.next;
}
}
// 在确定了环中节点个数后,开始找环的入口点,使用两个指针,原理同求链表倒数第k个节点
Node entry = findEntry(head, loopNodeNum);
return entry;
}
// 明确的一点是,如果有环,快慢指针肯定相遇,并且相遇点一定在环中的某个节点
public Node isLoop(Node head)
{
if (head == null)
{
return null;
}
Node slow = head;// 慢指针,一次一步
Node fast = slow.next;// 快指针,一次两步
while (fast != null && slow != null)
{
if (fast == slow)
{
return fast;// 相遇的节点
}
slow = slow.next;
// fast一次比较后走两步,每走一步都需要判断是否为null
fast = fast.next;
if (fast != null)
{
fast = fast.next;
}
}
return null;
}
private Node findEntry(Node head, int loopNodeNum)
{
Node p1 = head;
Node p2 = head;
// p1先loopNodeNumk个节点
int count = 0;
while (count != loopNodeNum)
{
count++;
p1 = p1.next;
}
while (p1 != p2)
{
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
public static void main(String[] args)
{
SingleLinkedList list = new SingleLinkedList();
list.addNode(1);
list.addNode(2);
list.addNode(3);
list.addNode(4);
list.addNode(5);
list.addNode(6);
}
}