题目
1、判断一个单链表是否带环
2、求取一个带环单链表的环的长度
3、求取一个带环单链表的环的入口
4、判断两个不带环单链表是否相交
5、求取两个不带环单链表的一个公共节点
这仅仅一个单链表是否带环可以拓展出这么多的问题,我们来一个一个解决
1、判断一个单链表是否带环。
思路
记得以前做过这个,可以用快慢指针,两个指针同时指向头结点,快指针一次走两个,慢指针一次走一个,如果慢指针最终追上快指针,说明链表有环,类似于两个人在操场跑步,一起开始跑,速度快的人必定会追上速度慢的。
代码
public static Boolean isHasCircle( LinkListNode head )
{
if(head==null) return null;
LinkListNode fast=head;
LinkListNode slow=head;
Boolean flag = false;
while(fast!=null && fast.next!=null )
{
fast = fast.next.next;
slow = slow.next;
if(fast == slow)
{
flag = true;
break;
}
}
return flag;
}
2、求取一个带环单链表的环的长度
思路
当一个链表有环,快慢指针在某一个时刻必然会指向同一个节点,此时开始累加,下一次在相遇的时候,累加器的和便是环的长度,也就是慢指针走过的长度。
代码
public static int lengthCircleOfList(LinkListNode head)
{
if(head==null) return -1;
int count=0;
LinkListNode fast = head;
LinkListNode slow = head;
boolean flag =false;
int sum = 2; //判断是否为第二次相遇
while(fast!=null && fast.next!=null)
{
fast = fast.next.next;
slow = slow.next;
if(fast == slow)
{
flag = true;
sum--;
}
if(sum == 0)break;
if(flag)
{
count++;
}
}
return count;
}
3、求取一个带环单链表的环的入口
思路
这里很巧妙,当快慢指针第一次相遇的时候,从相遇那个节点到环入口的节点和链表头结点到环入口的节点的距离相等,所以此时让一个指针从链头开始跑,一个指针从相遇的节点开始跑,那么相遇时,这个相遇节点便是环的入口节点。
那么会有一个问题:
这俩个距离为什么会相等,我们来证明一下。
当慢指针和快指针相遇的时候,快指针必然在环中转了n圈
所以有:2s = s + nr ; s为慢指针走过的距离,r 为环的长度
可以得出 s = nr
假设环入口到相遇节点的距离为x,链头节点到环入口的距离为a,链表长度为L
所以有 x + a = s ;由上面替换得到 x + a = nr ==> x+ a =(n-1)r +r ==> x + a = (n-1)r +L - a
所以有 a = (n-1)r +L - a - x;我们发现抛去转的圈数,刚好就是相遇节点到环入口的距离 == 链头节点到环入口的距离
代码
public static LinkListNode theEnterOfList(LinkListNode head)
{
if(head==null) return null;
LinkListNode fast = head;
LinkListNode slow = head;
while(fast!=null && fast.next!=null)
{
fast = fast.next.next;
slow = slow.next;
if(slow == fast)
{
break;
}
}
fast = head; //将快指针放到链头
while(slow!=null && fast!=slow) //相遇时跳出,此节点便是入口
{
fast=fast.next;
slow = slow.next;
}
return slow;
}
4、判断两个不带环单链表是否相交
5、求取两个不带环单链表的一个公共节点
思路
将第2条链表尾插到第1条链表,如果新链表有环,则证明两个链表相交
如果相交,则判断环入口,环入口即为两个链表的第一个公共节点
代码
public static boolean isIntersect(LinkListNode head1,LinkListNode head2)
{
//将第2个链表尾插到第1个链表末尾
LinkListNode p =head1;
while(p.next!=null)
{
p=p.next;
}
p.next = head2;
// LinkListNode temp = theEnterOfList(head1); //第一个公共节点
// System.out.println(temp.value);
return isHasCircle(head1);
}
去掉上面的注释,即为打印第一个公共节点。