#题目:如果一个链表中包含环,如何找出环的入口节点?
链表问题是面试过程中经常会考察到的问题,这个问题其实内含2个小问题:
1.如何判断一个链表中是否包含环;
2.找出带环链表中环的入口点。
当我们用1个指针遍历链表不能解决问题的时候,可以尝试用2个指针来遍历链表。可以让其中一个指针遍历的速度稍快一些(比如:让其中一个指针一次走2步,另一个指针一次走1步),也可以让其中一个指针在链表上先走若干步。
##1.判断一个链表中是否包含环
我们可以定义2个指针,将2个指针都初始化为指向链表的头结点。然后,让第一个指针遍历链表的速度稍快一些即让第一个指针一次走2步,让第二个指针一次走1步。如果快指针能够追赶上慢指针,则说明链表中包含环,此时快指针与慢指针相遇的结点一定在环的内部;如果快指针走到了链表的末尾(快指针指向null)都没有追上慢指针,则说明链表中不包含环。
##2.找到带环链表的环的入口结点
在这一步仍然需要定义2个指针,然后让第一个指针先在链表上走n步(n为环中结点的个数),再让第二个指针从头结点出发,之后两个指针在链表上以相同的速度一次走1步直到两个指针相遇,则相遇的结点就是环的入口结点。
那么我们需要计算环中的结点数n,定义一个计数器,让第一个指针从上一步得到的快指针与慢指针相遇的结点处出发,遍历链表直到回到该相遇结点,此时计数器的值就是环中的结点数。
Java代码如下:
/*
public class ListNode
{
int val;//原本的数据(数据元素信息)
ListNode next = null;//下一个节点的位置(后继节点的地址)
ListNode(int val)//构造函数
{
this.val = val;//this关键字用于区分成员变量和局部变量同名的情况
}
}
*/
public class Solution
{
//判断链表中是否包含环。如果包含环的化,返回2个指针相遇的结点(该结点一定在环内);如果不包含环,返回null。
public ListNode findMeetingNode(ListNode pHead)
{
//要时刻注意避免出现空指针异常
//如果头结点为空,则说明链表为空
if(pHead == null)
return null;
ListNode pSlow = pHead.next;//慢指针从头结点出发,一次走1步
if(pSlow == null)
return null;
ListNode pFast = pSlow.next;//快指针从头结点出发,一次走2步
while(pFast != null && pSlow != null)
{
if(pFast == pSlow)
return pFast;//存在环
pSlow = pSlow.next;
pFast = pFast.next;
if(pFast != null)
pFast = pFast.next;
}
return null;
}
//找到环的入口结点
public ListNode EntryNodeOfLoop(ListNode pHead)
{
//要时刻注意避免出现空指针异常
//如果头结点为空,则说明链表为空
if(pHead == null)
return null;
ListNode meetingNode = findMeetingNode(pHead);//上一步中得到的相遇结点
if(meetingNode == null)
return null;//表示链表中不包含环
int count = 1;//计数器,计算环中的结点数
ListNode tmpNode = meetingNode;//从相遇结点出发
while(tmpNode.next != meetingNode)
{
tmpNode = tmpNode.next;
count++;
}
//让第一个指针先在链表中走count步
ListNode pNode1 = pHead;
for(int i = 0;i < count;i++)
{
pNode1 = pNode1.next;
}
ListNode pNode2 = pHead;
//让两个指针以相同的速度遍历链表直到相遇
while(pNode1 != pNode2)
{
pNode1 = pNode1.next;
pNode2 = pNode2.next;
}
return pNode1;
}
}