链表--两个单链表相交的一系列问题
在本题中, 单链表可能有环, 也可能无环。 给定两个单链表的头节点 head1和head2,这两个链表可能相交, 也可能不相交。
请实现一个函数, 如果两个链表相交, 请返回相交的第一个节点; 如果不相交, 返回null 即可。
要求: 如果链表1的长度为N, 链表2的长度为M, 时间复杂度请达到 O(N+M), 额外空间复杂度请达到O(1)。
问题1:判断链表是否有环,并返回环的第一个结点:
快慢指针,快指针一次走两步,慢指针一次走一步,当快指针重新追上慢指针的时候,快指针指向链表头部,然后变身为慢指针(一次走一步)
当快指针与慢指针再次相遇时,即为环的第一个结点。
问题2:当两个链表都无环时:
问题退化为求两个无环链表的第一个公共结点的问题(根据链表的特殊性,只有一个next指针,只能连接一个下一个结点,所以第一个公共结点之后,只知道链表的结尾都是相同的),剑指offer上出现过剑指offer——两个链表的第一个公共结点

法1)遍历两个链表,得出两个链表的长度len1, len2,然后在较长的链表上先走len1 - len2个结点,然后在一起走,并且比较,相同返回该结点即可
法2)将链表1,2均压栈stack1, stack2,然后依次弹出,直到遇到不同的结点,此时上一个结点就是第一个公共结点,若有一个栈为空,则两个链表不相交
问题3:当两个链表一个有环一个无环时:
可以得出结论就是这两个链表不相交,因为链表的特性决定了如果两个链表相交的话,它们之后会一直相交下去,无环的和有环的链表不能公共的结点,相矛盾。直接返回null即可
问题4:当两个链表均有环时:环入口是loop1,loop2
情况一:两个链表不相交 loop1 != loop2
此时,遍历两个链表,当其中某一个链表再次回到 loop 时,则确定两个链表不相交
node从环的下一个结点开始遍历,当
node1 == loop1 || node2 == loop2时,此时无相交

情况二:两个链表在同一节点相交
此种情况下,两个链表的环入口肯定是相同的 loop1 == loop2
或者是
情况三:两个链表在不同节点相交 loop1 != loop2
此时,从环的下一个结点开始遍历,当node1 == loop1 || node2 == loop2时 即证明相交

public static class Node{
public int val;
public Node next = null;
public Node(int val){
this.val = val;
}
}
public static Node findFirstNodeFromLoop(Node head){
if(head == null || head.next == null) return null;
Node fast = head;
Node slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(slow == fast) break;
}
if(fast == null || fast.next == null){
return null;
}
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
//有环,且入环第一个结点不同
//1.两个有环链表不相交
//2.两个有环链表相交,但是相交结点不同
public static Node findFirstCommonNodeWithLoop(Node loop1, Node loop2){
if(loop1 == null || loop2 == null) return null;
Node node1 = loop1.next;
Node node2 = loop2.next;
while(node1 != loop1 || node2 != loop2){
System.out.println(node1.val + " " + node2.val);
if(node1 == loop2 || node2 == loop1) return node1;
node1 = node1.next;
node2 = node2.next;
}
return new Node(1000000);
}
//无环,找第一个公共结点
//有环,且入环第一个结点相同
//正确
public static Node findFirstCommonNodeWithoutLoop(Node head1, Node head2){
if(head1 == null || head2 == null) return null;
int len1 = 0;
int len2 = 0;
Node node1 = head1;
Node node2 = head2;
while(node1 != null){
node1 = node1.next;
len1++;
}
while(node2 != null){
node2 = node2.next;
len2++;
}
node1 = len1 > len2 ? head1 : head2;
node2 = len1 > len2 ? head2 : head1;
for(int i = 0; i < Math.abs(len1 - len2); i++){
node1 = node1.next;
}
while(node1 != null && node2 != null){
if(node1 == node2) return node1;
node1 = node1.next;
node2 = node2.next;
}
return null;
}
测试函数
/**
* head1,head2为两个链表均有环,且第一个相交结点不同
* head3,head4为两个链表均有环,且无相交结点
* head5,head6为两个链表均有环,且第一个相交结点相同
* @param args
*/
public static void main(String[] args){
Node head1 = new Node(0);
Node head2 = new Node(0);
Node node1 = head1;
Node node2 = head2;
node1.next = new Node(1);
node1 = node1.next;
node1.next = new Node(2);
node1 = node1.next;
node1.next = new Node(3);
node1 = node1.next;
node1.next = new Node(4);
node1 = node1.next;
node1.next = new Node(5);
node1 = node1.next;
node1.next = new Node(6);
node1 = node1.next;
node1.next = new Node(7);
node1 = node1.next;
node1.next = new Node(8);
node1 = node1.next;
node1.next = new Node(9);
node1 = node1.next;
node1.next = head1.next.next.next.next; //4为环入口
node2.next = new Node(11);
node2 = node2.next;
node2.next = new Node(12);
node2 = node2.next;
node2.next = new Node(13);
node2 = node2.next;
node2.next = new Node(14);
node2 = node2.next;
node2.next = new Node(15);
node2 = node2.next;
node2.next = new Node(16);
node2 = node2.next;
node2.next = head1.next.next.next.next.next.next;//6为环入口
Node head3 = new Node(0);
Node head4 = new Node(0);
node1 = head3;
node2 = head4;
node1.next = new Node(1);
node1 = node1.next;
node1.next = new Node(2);
node1 = node1.next;
node1.next = new Node(3);
node1 = node1.next;
node1.next = new Node(4);
node1 = node1.next;
node1.next = new Node(5);
node1 = node1.next;
node1.next = new Node(6);
node1 = node1.next;
node1.next = new Node(7);
node1 = node1.next;
node1.next = new Node(8);
node1 = node1.next;
node1.next = new Node(9);
node1 = node1.next;
node1.next = head3.next.next.next.next; //4为环入口
node2.next = new Node(11);
node2 = node2.next;
node2.next = new Node(12);
node2 = node2.next;
node2.next = new Node(13);
node2 = node2.next;
node2.next = new Node(14);
node2 = node2.next;
node2.next = new Node(15);
node2 = node2.next;
node2.next = new Node(16);
node2 = node2.next;
node2.next = head4.next.next.next;//14为环入口
Node head5 = new Node(0);
Node head6 = new Node(0);
node1 = head5;
node2 = head6;
node1.next = new Node(1);
node1 = node1.next;
node1.next = new Node(2);
node1 = node1.next;
node1.next = new Node(3);
node1 = node1.next;
node1.next = new Node(4);
node1 = node1.next;
node1.next = new Node(5);
node1 = node1.next;
node1.next = new Node(6);
node1 = node1.next;
node1.next = new Node(7);
node1 = node1.next;
node1.next = new Node(8);
node1 = node1.next;
node1.next = new Node(9);
node1 = node1.next;
node1.next = head5.next.next.next.next; //4为环入口
node2.next = new Node(11);
node2 = node2.next;
node2.next = new Node(12);
node2 = node2.next;
node2.next = new Node(13);
node2 = node2.next;
node2.next = new Node(14);
node2 = node2.next;
node2.next = new Node(15);
node2 = node2.next;
node2.next = new Node(16);
node2 = node2.next;
node2.next = head5.next;//4为环入口 1为公共结点
/**
* head1,head2为两个链表均有环,且第一个相交结点不同
* head3,head4为两个链表均有环,且无相交结点
* head5,head6为两个链表均有环,且第一个相交结点相同
*
*/
Node node = null;
node1 = head3;
node2 = head4;
Node loop1 = findFirstNodeFromLoop( head3 );
Node loop2 = findFirstNodeFromLoop(head4);
System.out.println(loop1.val);
System.out.println(loop2.val);
//两个链表均无环
if(loop1 == null && loop2 == null){
node = findFirstCommonNodeWithoutLoop(node1, node2);
} else if((loop1 == null && loop2 != null) || (loop1 != null && loop2 == null)){
node = null;
} else if(loop1 == loop2){
//两个链表有相交的结点
while(node1.next != loop1){
node1 = node1.next;
}
System.out.println(node1.val);
node1.next = null;
node = findFirstCommonNodeWithoutLoop( head5, head6 );
node1.next = loop1;
} else {
node = findFirstCommonNodeWithLoop(loop1, loop2);
//loop1 != loop2 1.两个链表无相交结点 2.两个链表相交结点不同
}
String str = node.val == 1000000 ? "无相同结点 " : "有相同结点 ";
System.out.println(str + node.val);
}

浙公网安备 33010602011771号