题目 在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。请实现一个函数, 如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null 即可。
要求 如果链表1 的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外 空间复杂度请达到O(1)。
思路 考察3个知识点:(1)单链表是否有环;(2)两条单链表是否相交;(3)找到第一个相交点。
(1)给定单链表,返回入环的第一个节点。判断其是否有环的结果只有两种情况,即:有环、无环。
判断方法:设置两个指针slow和fast,遍历链表,其中slow一次走一个节点,fast一次走两个节点。若链表有环,则 slow和fast必然会相遇,若无环,则fast会遍历至链表结束,即节点为Null。若链表有环,需要求出入环的第一个节点。
求解方法:记录判断是否有环时,slow最后的位置,同时将fast重置到表头,两个指针同时往下遍历,且都是 一次走一个节点,二者第一次相遇的点即为入环的第一个节点。
(2)判断两条链表是否相交,一共有6种拓扑关系,返回相交的第一个节点:a.两条链表都无环且不相交;b.两条链表都无环且相交;c.一条链表无环,另一条链表有环;d.两条链表都有环且不相交;e.两条链表都有环且相交,且入环点相同;f.两条链表都有环且相交,且入环点不同。
a.两条链表都无环且不相交:没有相交点,返回null;
b.两条链表都无环且相交:遍历两条链表,同时记录下每条链表的长度,以及最后一个节点。若两条链表的最后一个节点不同,则没有相交点返回null,否则的话将较长的链表从表头开始裁去长出来的部分,然后同时再次遍历两条链表,返回二者的第一个相同的节点;
c.一条链表无环,另一条链表有环:没有相交点,返回null;
判断两条有环链表是否相交的方法:首先判断两条链表的入环点是否相同,相同的话则属于情况[e],否则的话属于情况[d]或[f]。区分[d]和[f]的方法:拿出一条链表从入环点开始遍历,若转了一圈又回到了入环点且没有遇到另一条链表的入环点,则属于情况[d],若在遍历过程中遇到了另一条链表的入环点,则属于情况[f]。
d.两条链表都有环且不相交:没有相交点,返回null;
e.两条链表都有环且相交,且入环点相同:求解方法同[b],不同之处在于将[b]中链表尾部的Null用链表入环点替代,即只遍历到入环点结束;
f.返回两条链表中的任意一个入环点都可以。
package algorithm.section4;
public class FindFirstIntersectNode {
public static class Node{
public int value;
Node next;
public Node(int value){
this.value = value;
}
}
public static Node getIntersectNode(Node head1, Node head2){
if (head1 == null || head2 == null) return null;
Node loop1 = getLoop(head1);
Node loop2 = getLoop(head2);
if (loop1 == null && loop2 == null)
return noLoop(head1, head2);
else if (loop1 != null && loop2 != null)
return twoLoop(head1, loop1, head2, loop2);
else return null;
}
public static Node getLoop(Node head){
if (head == null || head.next == null || head.next.next == null)
return null;
Node slow = head.next;
Node fast = head.next.next;
while (fast != slow) {
if (fast.next == null || fast.next.next == null) return null;
slow = slow.next;
fast = fast.next.next;
}
fast = head;
while (fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
public static Node noLoop(Node head1, Node head2){
if (head1 == null || head2 == null) return null;
Node cur1 = head1;
Node cur2 = head2;
int len1 = 1;
int len2 = 1;
while (cur1.next != null){
len1++;
cur1 = cur1.next;
}
while (cur2.next != null){
len2++;
cur2 = cur2.next;
}
if (cur1 != cur2) return null;
else {
cur1 = head1;
cur2 = head2;
if (len1 > len2){
for (int i = 0; i < len1 - len2; i++)
cur1 = cur1.next;
} else if (len1 < len2){
for (int i = 0; i < len2 - len1; i++)
cur2 = cur2.next;
}
while (cur1 != cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
}
public static Node twoLoop(Node head1, Node loop1, Node head2, Node loop2){
if (loop1 == loop2) {
Node cur1 = head1;
Node cur2 = head2;
int len1 = 1;
int len2 = 1;
while (cur1.next != loop1){
cur1 = cur1.next;
len1++;
}
while (cur2.next != loop2){
cur2 = cur2.next;
len2++;
}
cur1 = head1;
cur2 = head2;
if (len1 > len2){
for (int i = 0; i < len1-len2; i++)
cur1 = cur1.next;
} else if (len1 < len2){
for (int i = 0; i < len2-len1; i++)
cur2 = cur2.next;
}
while (cur1 != cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
Node cur = loop1.next;
while (cur != loop2){
cur = cur.next;
if (cur == loop1) return null;
}
return loop1;
}
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;
System.out.println(getIntersectNode(head1, head2).value);
// 1->2->3->4->5->6->7->4...
head1.next.next.next.next.next.next.next = head1.next.next.next; // 7->4
// 0->9->8->2...
head2.next.next.next = head1.next;
System.out.println(getIntersectNode(head1, head2).value);
head2.next.next.next = head1.next.next.next.next.next; // 8->6
System.out.println(getIntersectNode(head1, head2).value);
}
}