【题目】 在本题中,单链表可能有环,也可能无环。给定两个 单链表的头节点 head1和head2,这两个链表可能相交,也可能 不相交。请实现一个函数, 如果两个链表相交,请返回相交的 第一个节点;如果不相交,返回null 即可。 要求:如果链表1 的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外 空间复杂度请达到O(1)。
将题目拆分:
- 判断链表是否有环,有环则返回第一个换节点
- 判断两个无环链表是否相交,相交则返回第一个节点
- 判断两个有环链表是否相交,相交则返回第一个节点
(一个链表有环,一个链表无环是不可能相交的)
判断链表是否有环,有环则返回第一个换节点:
设置一个快指针,和一个慢指针,快指针每次走两个,慢指针每次走一个。当快指针与慢指针相遇。将快指针重新设置为头节点,然后快指针和慢指针同时每次都走一个,当再次相遇时,此时的位置便是第一个环节点位置。
判断是否有环的代码:
public static Node getLoopNode(Node head){
if (head==null||head.next==null||head.next.next==null){
return null;
}
Node n1=head.next;//慢指针
Node n2=head.next.next;//快指针
while (n1!=n2){
if (n2.next==null||n2.next.next==null){
return null;
}
n2=n2.next.next;
n1=n1.next;
}
n2=head;//将n2设置为头重新遍历
while (n1!=n2){
n1=n1.next;
n2=n2.next;
}
return n1;
}
判断两个无环链表是否相交,相交则返回第一个节点:
可以将两个链表先进行遍历,得到两个链表的长度,假设第一的链表长度为100,第二个链表长度为80,那么可以先让链表一走二十个,然后两个一起走,当两个第一次相遇时,该位置即为两个链表相交的第一个节点。
判断两个无环链表是否相交:
public static Node noLoop(Node head1,Node head2){
if (head1==null&&head2==null){
return null;
}
Node cur1=head1;
Node cur2=head2;
int n=0;
while (cur1!=null){
n++;
cur1=cur1.next;
}
while (cur2!=null){
n--;
cur2=cur2.next;
}
cur1=n>0?head1:head2;
cur2=cur1==head1?head2:head1;
n=Math.abs(n);
while (n!=0){
n--;
cur1=cur1.next;
}
while (cur1!=cur2){
cur1=cur1.next;
cur2=cur2.next;
}
return cur1;
}
判断两个有环链表是否相交,相交则返回第一个节点:
先找到两个链表的各自环节点(通过getLoopNode方法), 然后让第一个环的环节点,继续向下遍历,当遇到第二个链表的环节点时,说明两个有环链表相交,此时返回第一个链表的环节点就可以了。
判断两个有环链表是否相交代码:
public static Node bothLoop(Node head1,Node loop1,Node head2,Node loop2){
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=n>0?head1:head2;
cur2=cur1==head1?head2:head1;
n=Math.abs(n);
while (n!=0){
n--;
cur1=cur1.next;
}
while (cur1!=cur2){
cur1=cur1.next;
cur2=cur2.next;
}
return cur1;
}else {
cur1=loop1.next;
while (cur1!=loop1){
if (cur1==loop2){
return loop1;
}
cur1=cur1.next;
}
return null;
}
}
完整代码:
package basic_class_03;
/**
* 类名:Code_14_FindFirstIntersectNode<br>
* 功能:两个单列表相交一系列问题<br>
* 作者:java战士<br>
* 日期:2019/9/4<br>
* 版本:v1.0.0
* 历史修订:
*/
public class Code_14_FindFirstIntersectNode {
public static class Node{
public int data;
public Node next;
public Node(int data) {
this.data = data;
}
}
public static Node getIntersectNode(Node head1,Node head2){
if (head1==null||head2==null){
return null;
}
Node loop1=getLoopNode(head1);
Node loop2=getLoopNode(head2);
if (loop1!=null&&loop2!=null){
return bothLoop(head1,loop1,head2,loop2);
}else if (loop1==null&&loop2==null){
return noLoop(head1,head2);
}else {
return null;
}
}
public static Node getLoopNode(Node head){
if (head==null||head.next==null||head.next.next==null){
return null;
}
Node n1=head.next;//慢指针
Node n2=head.next.next;//快指针
while (n1!=n2){
if (n2.next==null||n2.next.next==null){
return null;
}
n2=n2.next.next;
n1=n1.next;
}
n2=head;//将n2设置为头重新遍历
while (n1!=n2){
n1=n1.next;
n2=n2.next;
}
return n1;
}
//没有环的时候
public static Node noLoop(Node head1,Node head2){
if (head1==null&&head2==null){
return null;
}
Node cur1=head1;
Node cur2=head2;
int n=0;
while (cur1!=null){
n++;
cur1=cur1.next;
}
while (cur2!=null){
n--;
cur2=cur2.next;
}
cur1=n>0?head1:head2;
cur2=cur1==head1?head2:head1;
n=Math.abs(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 loop2){
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=n>0?head1:head2;
cur2=cur1==head1?head2:head1;
n=Math.abs(n);
while (n!=0){
n--;
cur1=cur1.next;
}
while (cur1!=cur2){
cur1=cur1.next;
cur2=cur2.next;
}
return cur1;
}else {
cur1=loop1.next;
while (cur1!=loop1){
if (cur1==loop2){
return loop1;
}
cur1=cur1.next;
}
return null;
}
}
public static void main(String[] args){
//1,2,3,4,5,6,7,null
Node head1=new Node(1);
head1.next=new Node(2);
head1.next.next=new Node(3);
head1.next.next.next=new Node(4);
head1.next.next.next.next=new Node(5);
head1.next.next.next.next.next=new Node(6);
head1.next.next.next.next.next.next=new Node(7);
//0,9,8,6,7,null
Node head2=new Node(0);
head2.next=new Node(9);
head2.next.next=new Node(8);
head2.next.next.next=head1.next.next.next.next.next;//8--->7
System.out.println(getIntersectNode(head1,head2).data);
//1,2,3,4,5,6,7,3....
head1=new Node(1);
head1.next=new Node(2);
head1.next.next=new Node(3);
head1.next.next.next=new Node(4);
head1.next.next.next.next=new Node(5);
head1.next.next.next.next.next=new Node(6);
head1.next.next.next.next.next.next=new Node(7);
head1.next.next.next.next.next.next=head1.next.next;//7--->3
//0,9,8,3....
head2=new Node(0);
head2.next=new Node(9);
head2.next.next=new Node(8);
head2.next.next.next=head1.next.next;//8--->3
System.out.println(getIntersectNode(head1,head2).data);
head2=new Node(0);
head2.next=new Node(9);
head2.next.next=new Node(8);
head2.next.next.next=head1.next.next.next;//8---4
System.out.println(getIntersectNode(head1,head2).data);
}
}
测试结果: