问题
给定两个链表(可能有环,也可能无环),返回两个链表相交的节点,如果不相交返回null,如果相交多个返回其中一个。
分析
因为这两个链表可能有环也可能无环,有以下几种情况
- 1、链表A无环,链表B无环
- 2、链表A有环,链表B有环
- 3、链表A有环,链表B无环
- 4、链表A无环,链表B有环
第三,四种情况不可能相交,也就不再考虑。
因此两个链表要么都是无环,要么都是有环。
获取有环链表第一个入环节点
下面的操作首先获取的就是有环链表第一个入环节点,如果无环,返回null
public Node getLoopNode(Node head){
if(head==null||head.next==null||head.next.next==null){
return null;
}
// slow fast point
Node slow = head.next;
Node fast = head.next.next;
while(slow!=fast){
if(slow.next==null||fast.next.next==null){
return null;
}
slow = slow.next;
fast = fast.next.next;
}
// 快指针重新回到head,每次走一步
fast = head;
while(fast!=slow){
slow = slow.next;
fast = fast.next;
}
return slow;
}
无环情况下
无环情况下又分为相交和不相交。那怎样判断两者相交还是不相交?
相交一定有公共部分,因为两者无环,所以遍历到最后的节点是一样的,如果不一样,说明不想交,直接返回null;如果相交,需要找出这两条链表哪条较长,先让长链表走n步,n是两个链表长度的差值,然后两个链表依次向前遍历,直到两个链表指向同一个节点,返回即可。
代码实现:
// 如果两个链表都没环,返回第一个相交节点,不相交返回null
public static Node noLoop(Node head1,Node head2){
if(head1==null||head2==null){
return null;
}
Node cur1=null;
Node cur2 = null;
int n=0;
while(cur1.next!=null){
n++;
cur1=cur1.next;
}
while(cur2!=loop2){
n--;
cur2=cur2.next;
}
// 不相交
if(cur1!=cur2){
return null;
}
//cur1 指向长链表
//cur2指向短链表
cur1 = n>0?head1:head2;
cur2=cur1==head1?head2:head1;
n=Math.abs(n);
//长链表先走n步
while(n!=0){
n--;
cur1=cur1.next;
}
//然后长短链表的长度一致后同时往后遍历,如果相等代表第二种情况:两个有环链表相交一个点
while(cur1!=cur2){
cur1 = cur1.next;
cur2=cur2.next;
}
return cur1;
}
有环情况下
有环的时候有三种情况
- 两个链表是独立的不相交。
- 两个链表相交一个节点(共用一个环)
- 两个链表相交两个节点(共用一个环)
public static Node bothLoop(Node head1,Node loop1,Node head2,Node loo2){
Node cur1 = null;
Node cur2 = null;
//如果两个链表第一个入环节点一致代表两个有环节点相交于一点
if(loop1==loop2){
cur1 = head1;
cur2 = head2;
int n=0;
while(cur1!=loop1){
n++;
cur1=cur1.next;
}
while(cur2!=loop2){
n--;
cur2=cur2.next;
}
//cur1 指向长链表
//cur2指向短链表
cur1 = n>0?head1:head2;
cur2=cur1==head1?head2:head1;
n=Math.abs(n);
//长链表先走n步
while(n!=0){
n--;
cur1=cur1.next;
}
//然后长短链表的长度一致后同时往后遍历,如果相等代表第二种情况:两个有环链表相交一个点
while(cur1!=cur2){
cur1 = cur1.next;
cur2=cur2.next;
}
return cur1;
}else{
//第三种情况:两个有环链表相交与loop1 loop2
// 从loop1开始向后遍历节点,如果遇到loop2代表是第三种情况
cur1 = loop1.next;
while(cur1!=loop1){
if(cur1==loop2){
return loop2;
}
cur1=cur1.next;
}
//没有遇到代表两个有环链表没相交,返回null
return null;
}
}
总代码
如果一上来两个链表都为null,直接返回null;
获取链表A的第一个入环节点
获取B的第一个入环节点
如果入环节点都是null代表是两个链表是无环的,如果两个链表的第一个入环节点都不为空,表示两个链表都是有环的,采用有环的情况处理。其他情况是第三第四种不相交,直接返回null。
public static Node getIntersectNode(Node head1,Node head2){
if(head1==null||head2==null) return null;
// 返回链表1的第一个入环节点
Node loop1 = getLoopNode(head1);
// 返回链表2的第一个入环节点
Node loop2 = getLoopNode(head2);
if(loop1==null&&loop2==null){
//两个链表是无环的相交
return noLoop(head1,head2);
}
if(loo1!=null&&loop2!=null){
//有环链表相交
return bothLoop(head1,loop1,head2,loop2);
}
//不相交
return null;
}