35.两个链表的第一个公共节点(延伸——判断链表有无环以及两链表相交问题)
1.如何判断一个单链表是否有环?若有环返回第一个入环节点,无环返回空。
思路一:
从头遍历链表,用HashSet存储此链表的各个节点。如果此节点出现过,则返回它,若直到遍历到null都没有重复节点,就返回null,即不存在这样的结构。
public static Node findFirstIntersectNode(Node head) {
if (head == null) return null;
Node temp = head;
HashSet<Node> hashSet = new HashSet<>();
while (temp != null) {
if (!hashSet.contains(temp)) {
hashSet.add(temp);
} else {
return temp;
}
temp = temp.next;
}
return null;
}
思路二:
快慢指针法。快慢指针都从头走,慢指针先走,快指针一次两步,慢指针一次一步,如果快指针走到了末尾,都没有与慢指针相遇,则返回null,如果相遇了,则必定有环,且在环上相遇的,此时快指针回到head,变成一次走一步;慢指针不动,然后两个指针再一起走,相遇的节点就是第一个入环节点。
public static Node findFirstIntersectNode2(Node head) {
if (head == null) return null;
Node fast = head;
Node slow = head;
while (fast.next != null || fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
break;
}
}
if (fast.next == null || fast.next.next == null)
return null;
else {
fast = head;
}
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
**=====================================================================================**
判断两条链表是否相交,若相交,则返回第一个相交的节点,若不相交则返回null.但注意,两条链表可能有环或无环.
思路:
我们可以通过上文的函数,得知每条链表是否有环,及其第一个入环节点。然后需要分情况讨论。
一、若两条链表都无环
若相交,就会是这种形式(注意不可能交叉,因为每个节点都只有一个后继节点)。实现方法有两种:
①HashSet。先遍历第一条链表,节点全部装入set,然后遍历第二条链表,看其各个节点是否在set中,第一个在set中的节点就是相交节点,若遍历完了都没有,就返回null。
public static Node firstIntersectNodeOfTwoNode(Node head1, Node head2) {
if (head1 == null || head2 == null) return null;
Node temp1 = head1;
Node temp2 = head2;
HashSet<Node> hashSet = new HashSet<>();
while (temp1 != null) {
hashSet.add(temp1);
temp1 = temp1.next;
}
while (temp2 != null) {
if (hashSet.contains(temp2)) {
return temp2;
}
temp2 = temp2.next;
}
return null;
}
②空间复杂度O(1)的方法。假设链表一和链表二不相交的长度是a,b,相交的长度为c。我们可以得到等式:a+c+b=b+c+a。也就是说,两个指针同时分别从两个链表一起走,那么总有一个时刻可以相遇,问题是怎么走。
方法是p从链表一开始走,走到null就返回去从链表二开始走,q从链表二开始走,走到null就返回去从链表一开始走。
class Solution {
public ListNode findFirstCommonNode(ListNode head1, ListNode head2) {
if(head1==null || head2==null) return null;
ListNode p=head1;
ListNode q=head2;
while(p!=q)
{
if(p==null)
{
p=head2;
}else
{
p=p.next;
}
if(q==null)
{
q=head1;
}else
{
q=q.next;
}
}
return p;
}
}
二、若一个有环,一个无环,则必不可能相交。
因为每个节点都只有一个后继节点
三、两个带环链表
有如下三种情况:
如果两个带环链表第一个入环节点相同,就是情况二。此时我们研究的问题就无关于环了,第一个相交节点一定不在环上,我们就要研究“两条链表都无环的情况”。上文已给出。
如果不同,则遍历其中的一条链表,若没遇到另外一条的入环节点,则是情况一,无相交节点,返回null;若遇到了,则是情况三,此时返回任意一个入环节点即可。