题目描述
一个链表中包含环,请找出该链表的环的入口结点。
这个题目很经典:但是这种快慢指针的想法的原因我还没有明白其中的由来;还有不明白为什么快指针只能走2步,或者又是什么道理,没懂;
但是清楚的明白一点就是,快慢指针相遇的点一定是在环内,那么根据这个点在环内转圈再次遇到即就能得到环的大小n,也就是环中结点的个数;接着。让指针1先走环的n步.然后指针2从头结点开始走。则再次相遇即就是环的入口。
哇哦真的思维很巧妙,等我明白其中的道理来!
开始根据剑指offer书上写的算法如下:
package TestMyselfe;
public class Test56 {
public static class ListNode {
int value;
ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.value = val;
}
@Override
public String toString() {
return value + "";
}
}
//找到快慢指针相遇的结点在环内
public static ListNode MeetingNode(ListNode pHead)
{
if(pHead==null){
return null;
}
ListNode slow=pHead;
ListNode fast=pHead;
while (fast!=null && slow!=null) {
slow=slow.next;
fast=fast.next.next;
if(fast==slow){
return fast;
}
}
if(fast==null&&slow==null){
return null;
}
return fast;
}
/*
* 在有环的链表中找到环的入口结点的步骤:
* 1.指针p1和p2在初始化时都指向链表的头结点;
* 2.由于环中有4个结点,指针p1先在链表上向前移动4步;
* 3.指针p1和p2以相同的速度在链表上向前移动直到它们相遇。
* 它们相遇的结点就是环的入口结点;
*/
public static ListNode EntryNodeOfLoop(ListNode pHead){
ListNode meetnode=MeetingNode(pHead);//环中相遇的结点是5
if(meetnode==null){
return null;
}
//得到结点中环的个数
int nodesinloop=1;
ListNode node1=meetnode;
//得到环中的结点个数
while(node1.next!=meetnode){
node1=node1.next;
++nodesinloop;//统计环中结点的个数累加
}
//分别两个指针开始行走
node1=pHead;
//指针1先走
for (int i = 0; i < nodesinloop; i++) {
node1=node1.next;//让node1先走nodesinloop步
}
ListNode node2=pHead;
//指针1和指针2同时一步步走,相遇即就是环的入口
while(node1!=node2){
node1=node1.next;
node2=node2.next;
}
return node1;
}
public static void main(String[] args) {
test01();
}
// 1->2->3->4->5->6
private static void test01() {
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;
System.out.println(EntryNodeOfLoop(n1));
}
// 1->2->3->4->5->6
// ^ |
// | |
// +--------+
}
后来发现还有更简单的,意思就一样,只不过就是不分开;先找到相遇点,接着一个从头走,一个继续走,一步一步,再次相遇即就是环的入口:
代码如下:
package TestMyselfe;
public class NewTest56 {
public static class ListNode {
int value;
ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.value = val;
}
@Override
public String toString() {
return value + "";
}
}
/*
* 1->2->3->4->5->6->3
*
*/
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null){
return null;
}
ListNode slow=pHead;
ListNode fast=pHead;
//快慢指针在链表的找到相遇的点肯定在环内
while(slow!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(fast==slow){
break;
}
}
//链表无环就是空
if(slow==null||fast.next==null){
return null;
}
ListNode node1=pHead;
//环内相遇点开始
ListNode node2=slow;
//一步步再次相遇即就是环的入口
while(node1!=node2){
node1=node1.next;
node2=node2.next;
}
return node1;
}
}