给定两个可能有环也可能无环的单链表,头结点head1和head2.请实现一个函数,如果两个链表相交,请返回相交的第一个节点,如果不相交,返回null
要求:
如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)
先解决这样一个问题:
给定一个存在环的单链表,请找出环的入口结点,如果一个单链表的首尾结点是同一个结点,则默认首结点为环的入口结点。
package com.inspire.chapter6;
public class Code05_FindFirstIntersectNode1 {
public static class Node {
public int value;
public Node next;
public Node(int v) {
value = v;
}
}
//判断单链表是否存在环,有环返回环的入口结点,无环返回null
public static Node getLoopNode(Node head) {
if (head == null || head.next == null || head.next.next == null) {
return null;
}
Node slow = head.next;
Node fast = head.next.next;
while (slow != fast) {
if (fast.next == null || fast.next.next == null) {
return null;
}
slow = slow.next;
fast = fast.next.next;
}
fast = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
public static void main(String[] args) {
Node head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
head.next.next.next.next.next = new Node(6);
head.next.next.next.next.next.next = new Node(7);
head.next.next.next.next.next.next.next = head.next.next.next;
System.out.println(getLoopNode(head).value);
}
}
接下来解决这道题
假设链表head1和链表head2各自的入环结点分别为loop1和loop2
1.loop1=null,loop2=null;
首先判断这两个链表是否存在环,当两个链表都不存在环时
情况1:两个链表的不相交,返回null,判断方法:两个链表的尾结点不相等;
情况2:两个链表相交,返回相交结点,判断方法,分别存在两个结点相等。
2.loop1=null,loop2!=null;loop1!=null,loop2=null;这种两种情况下两个链表是不会相交的
3.loop1!=null,loop2!=null;
情况一:两个链表没有共同结点(不相交)
情况二:两个链表的相交结点不在环上(也即入环结点相等)
情况三:两个链表的相交结点在环上(也即入环结点不相等)
package com.inspire.chapter6;
import com.inspire.chapter6.Test2.Node;
public class Code05_FindFirstIntersectNode {
public static class Node {
public int value;
public Node next;
public Node(int v) {
value = v;
}
}
// 判断单链表是否存在环,有环返回环的入口结点,无环返回null
public static Node getLoopNode(Node head) {
if (head == null || head.next == null || head.next.next == null) {
return null;
}
Node slow = head.next;
Node fast = head.next.next;
while (slow != fast) {
if (fast.next == null || fast.next.next == null) {
return null;
}
slow = slow.next;
fast = fast.next.next;
}
fast = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
// 根据两个无环链表查找相交结点,有则返回相交结点,没有则返回null
public static Node noLoop(Node head1, Node head2) {
if (head1 == null || head2 == null) {
return null;
}
// 判断两个单链表的尾结点是否相等,不相等则返回null,相等则继续
Node cur1 = head1;
Node cur2 = head2;
int n = 0;// 保存两个单链表长度的差值
while (cur1 != null) {
n++;
cur1 = cur1.next;
}
while (cur2 != null) {
n--;
cur2 = cur2.next;
}
if (cur1 != cur2) {// 说明不相交
return null;
}
cur1 = n > 0 ? head1 : head2;// cur1指向较长的单链表
cur2 = cur1 == head1 ? head2 : head1;// cur2指向较短的单链表
n = Math.abs(n);
while (n != 0) {
n--;
cur1 = cur1.next;
} // 退出循环后,较长的单链表中cur1指向结点开始的单链表已经和较短的单链表长度相等了
while (cur1 != cur2) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
// 两个有环链表,返回第一个相交节点,如果不想交返回null
/*
* loop1:链表head1的入环结点 loop2:链表head2的入环结点
*/
public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
if (loop1 == loop2) {// 这是情况二
Node cur1 = head1;
Node cur2 = head2;
int n = 0;
while (cur1 != loop1) {
n++;
cur1 = cur1.next;
}
while (cur2 != loop1) {
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 {// 讨论情况一和情况三
Node cur1 = loop1.next;
while (cur1 != loop1) {
if (cur1 == loop2) {
return loop1;
}
cur1 = cur1.next;
}
return null;
}
}
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 noLoop(head1, head2);
} else if (loop1 != null && loop2 != null) {
return bothLoop(head1, loop1, head2, loop2);
}
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->6
System.out.println(getIntersectNode(head1, head2).value);
// 1->2->3->4->5->6->7->4...
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.next; // 7->4
// 0->9->8->2...
head2 = new Node(0);
head2.next = new Node(9);
head2.next.next = new Node(8);
head2.next.next.next = head1.next; // 8->2
System.out.println(getIntersectNode(head1, head2).value);
// 0->9->8->6->4->5->6..
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->6
System.out.println(getIntersectNode(head1, head2).value);
}
}